From 76fa6072524e09b5ff921545d42e60b7e0aa68c5 Mon Sep 17 00:00:00 2001 From: nkramer44 Date: Mon, 27 Nov 2023 10:58:16 -0500 Subject: [PATCH] Add support for ledger_entry RPC (#489) * Add support for ledger_entry RPC * use ledgerEntry in ITs --- .../org/xrpl/xrpl4j/client/XrplClient.java | 26 + .../xrpl/xrpl4j/client/XrplClientTest.java | 26 + .../client/ledger/AmmLedgerEntryParams.java | 40 ++ .../DepositPreAuthLedgerEntryParams.java | 40 ++ .../ledger/EscrowLedgerEntryParams.java | 41 ++ .../ledger/LedgerEntryRequestParams.java | 456 ++++++++++++ .../client/ledger/LedgerEntryResult.java | 126 ++++ .../client/ledger/OfferLedgerEntryParams.java | 41 ++ .../ledger/RippleStateLedgerEntryParams.java | 78 ++ .../ledger/TicketLedgerEntryParams.java | 43 ++ .../xrpl/xrpl4j/model/ledger/AmmObject.java | 28 +- .../xrpl4j/model/ledger/TicketObject.java | 7 + .../org/xrpl/xrpl4j/crypto/TestConstants.java | 6 + .../ledger/LedgerEntryRequestParamsTest.java | 432 +++++++++++ .../client/ledger/LedgerEntryResultTest.java | 676 ++++++++++++++++++ .../xrpl4j/model/ledger/AmmObjectTest.java | 11 +- .../model/ledger/TicketObjectJsonTests.java | 8 +- .../org/xrpl/xrpl4j/tests/AccountSetIT.java | 40 +- .../java/org/xrpl/xrpl4j/tests/AmmIT.java | 69 +- .../java/org/xrpl/xrpl4j/tests/CheckIT.java | 29 + .../xrpl/xrpl4j/tests/DepositPreAuthIT.java | 46 +- .../java/org/xrpl/xrpl4j/tests/EscrowIT.java | 72 +- .../xrpl/xrpl4j/tests/IssuedCurrencyIT.java | 92 ++- .../java/org/xrpl/xrpl4j/tests/NfTokenIT.java | 272 +++---- .../java/org/xrpl/xrpl4j/tests/OfferIT.java | 129 ++-- .../xrpl/xrpl4j/tests/PaymentChannelIT.java | 68 +- .../xrpl/xrpl4j/tests/SetRegularKeyIT.java | 43 +- .../xrpl/xrpl4j/tests/SignerListSetIT.java | 40 ++ .../java/org/xrpl/xrpl4j/tests/TicketIT.java | 30 +- 29 files changed, 2800 insertions(+), 215 deletions(-) create mode 100644 xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/AmmLedgerEntryParams.java create mode 100644 xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/DepositPreAuthLedgerEntryParams.java create mode 100644 xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/EscrowLedgerEntryParams.java create mode 100644 xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryRequestParams.java create mode 100644 xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryResult.java create mode 100644 xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/OfferLedgerEntryParams.java create mode 100644 xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/RippleStateLedgerEntryParams.java create mode 100644 xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/TicketLedgerEntryParams.java create mode 100644 xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryRequestParamsTest.java create mode 100644 xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryResultTest.java diff --git a/xrpl4j-client/src/main/java/org/xrpl/xrpl4j/client/XrplClient.java b/xrpl4j-client/src/main/java/org/xrpl/xrpl4j/client/XrplClient.java index 7767c97c1..4be150a83 100644 --- a/xrpl4j-client/src/main/java/org/xrpl/xrpl4j/client/XrplClient.java +++ b/xrpl4j-client/src/main/java/org/xrpl/xrpl4j/client/XrplClient.java @@ -62,6 +62,8 @@ import org.xrpl.xrpl4j.model.client.common.LedgerIndex; import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryRequestParams; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryResult; import org.xrpl.xrpl4j.model.client.ledger.LedgerRequestParams; import org.xrpl.xrpl4j.model.client.ledger.LedgerResult; import org.xrpl.xrpl4j.model.client.nft.NftBuyOffersRequestParams; @@ -89,6 +91,7 @@ import org.xrpl.xrpl4j.model.client.transactions.TransactionResult; import org.xrpl.xrpl4j.model.immutables.FluentCompareTo; import org.xrpl.xrpl4j.model.jackson.ObjectMapperFactory; +import org.xrpl.xrpl4j.model.ledger.LedgerObject; import org.xrpl.xrpl4j.model.transactions.Address; import org.xrpl.xrpl4j.model.transactions.Hash256; import org.xrpl.xrpl4j.model.transactions.Transaction; @@ -672,6 +675,28 @@ public LedgerResult ledger(LedgerRequestParams params) throws JsonRpcClientError return jsonRpcClient.send(request, LedgerResult.class); } + /** + * Retrieve a {@link LedgerObject} by sending a {@code ledger_entry} RPC request. + * + * @param params A {@link LedgerEntryRequestParams} containing the request parameters. + * @param The type of {@link LedgerObject} that should be returned in rippled's response. + * + * @return A {@link LedgerEntryResult} of type {@link T}. + */ + public LedgerEntryResult ledgerEntry( + LedgerEntryRequestParams params + ) throws JsonRpcClientErrorException { + JsonRpcRequest request = JsonRpcRequest.builder() + .method(XrplMethods.LEDGER_ENTRY) + .addParams(params) + .build(); + + JavaType resultType = objectMapper.getTypeFactory() + .constructParametricType(LedgerEntryResult.class, params.ledgerObjectClass()); + + return jsonRpcClient.send(request, resultType); + } + /** * Try to find a payment path for a rippling payment by sending a ripple_path_find method request. * @@ -770,6 +795,7 @@ public GatewayBalancesResult gatewayBalances( * @param params The {@link AmmInfoRequestParams} to send in the request. * * @return A {@link AmmInfoResult}. + * * @throws JsonRpcClientErrorException if {@code jsonRpcClient} throws an error. */ @Beta diff --git a/xrpl4j-client/src/test/java/org/xrpl/xrpl4j/client/XrplClientTest.java b/xrpl4j-client/src/test/java/org/xrpl/xrpl4j/client/XrplClientTest.java index 25fffe8b3..f07094164 100644 --- a/xrpl4j-client/src/test/java/org/xrpl/xrpl4j/client/XrplClientTest.java +++ b/xrpl4j-client/src/test/java/org/xrpl/xrpl4j/client/XrplClientTest.java @@ -77,6 +77,8 @@ import org.xrpl.xrpl4j.model.client.common.LedgerIndex; import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryRequestParams; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryResult; import org.xrpl.xrpl4j.model.client.ledger.LedgerRequestParams; import org.xrpl.xrpl4j.model.client.ledger.LedgerResult; import org.xrpl.xrpl4j.model.client.nft.NftBuyOffersRequestParams; @@ -104,8 +106,10 @@ import org.xrpl.xrpl4j.model.client.transactions.TransactionRequestParams; import org.xrpl.xrpl4j.model.client.transactions.TransactionResult; import org.xrpl.xrpl4j.model.flags.AccountRootFlags; +import org.xrpl.xrpl4j.model.jackson.ObjectMapperFactory; import org.xrpl.xrpl4j.model.ledger.AccountRootObject; import org.xrpl.xrpl4j.model.ledger.Issue; +import org.xrpl.xrpl4j.model.ledger.LedgerObject; import org.xrpl.xrpl4j.model.transactions.Address; import org.xrpl.xrpl4j.model.transactions.Hash256; import org.xrpl.xrpl4j.model.transactions.NfTokenId; @@ -1041,6 +1045,28 @@ void ammInfo() throws JsonRpcClientErrorException { assertThat(result).isEqualTo(mockResult); } + @Test + void ledgerEntry() throws JsonRpcClientErrorException { + LedgerEntryRequestParams params = LedgerEntryRequestParams.index( + Hash256.of("6B1011EF3BC3ED619B15979EF75C1C60D9181F3DDE641AD3019318D3900CEE2E"), + LedgerSpecifier.VALIDATED + ); + + LedgerEntryResult mockResult = mock(LedgerEntryResult.class); + when(jsonRpcClientMock.send( + JsonRpcRequest.builder() + .method(XrplMethods.LEDGER_ENTRY) + .addParams(params) + .build(), + ObjectMapperFactory.create().getTypeFactory().constructParametricType( + LedgerEntryResult.class, LedgerObject.class + ) + )).thenReturn(mockResult); + + LedgerEntryResult result = xrplClient.ledgerEntry(params); + assertThat(result).isEqualTo(mockResult); + } + @Test void nftInfo() throws JsonRpcClientErrorException { NftInfoRequestParams params = NftInfoRequestParams.builder() diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/AmmLedgerEntryParams.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/AmmLedgerEntryParams.java new file mode 100644 index 000000000..e9accab4e --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/AmmLedgerEntryParams.java @@ -0,0 +1,40 @@ +package org.xrpl.xrpl4j.model.client.ledger; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.ledger.Issue; + +/** + * Parameters that uniquely identify an {@link org.xrpl.xrpl4j.model.ledger.AmmObject} on ledger that can be used + * in a {@link LedgerEntryRequestParams} to request an {@link org.xrpl.xrpl4j.model.ledger.AmmObject}. + */ +@Immutable +@JsonSerialize(as = ImmutableAmmLedgerEntryParams.class) +@JsonDeserialize(as = ImmutableAmmLedgerEntryParams.class) +public interface AmmLedgerEntryParams { + + /** + * Construct a {@code AmmLedgerEntryParams} builder. + * + * @return An {@link ImmutableAmmLedgerEntryParams.Builder}. + */ + static ImmutableAmmLedgerEntryParams.Builder builder() { + return ImmutableAmmLedgerEntryParams.builder(); + } + + /** + * One of the two assets in the AMM's pool. + * + * @return An {@link Issue}. + */ + Issue asset(); + + /** + * The other of the two assets in the AMM's pool. + * + * @return An {@link Issue}. + */ + Issue asset2(); + +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/DepositPreAuthLedgerEntryParams.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/DepositPreAuthLedgerEntryParams.java new file mode 100644 index 000000000..8dde10185 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/DepositPreAuthLedgerEntryParams.java @@ -0,0 +1,40 @@ +package org.xrpl.xrpl4j.model.client.ledger; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.transactions.Address; + +/** + * Parameters that uniquely identify a {@link org.xrpl.xrpl4j.model.ledger.DepositPreAuthObject} on ledger that can be + * used in a {@link LedgerEntryRequestParams} to request an {@link org.xrpl.xrpl4j.model.ledger.DepositPreAuthObject}. + */ +@Immutable +@JsonSerialize(as = ImmutableDepositPreAuthLedgerEntryParams.class) +@JsonDeserialize(as = ImmutableDepositPreAuthLedgerEntryParams.class) +public interface DepositPreAuthLedgerEntryParams { + + /** + * Construct a {@code DepositPreAuthLedgerEntryParams} builder. + * + * @return An {@link ImmutableDepositPreAuthLedgerEntryParams.Builder}. + */ + static ImmutableDepositPreAuthLedgerEntryParams.Builder builder() { + return ImmutableDepositPreAuthLedgerEntryParams.builder(); + } + + /** + * The {@link Address} of the account that provided the preauthorization. + * + * @return An {@link Address}. + */ + Address owner(); + + /** + * The {@link Address} of the account that received the preauthorization. + * + * @return An {@link Address}. + */ + Address authorized(); + +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/EscrowLedgerEntryParams.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/EscrowLedgerEntryParams.java new file mode 100644 index 000000000..1c015e654 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/EscrowLedgerEntryParams.java @@ -0,0 +1,41 @@ +package org.xrpl.xrpl4j.model.client.ledger; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.google.common.primitives.UnsignedInteger; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.transactions.Address; + +/** + * Parameters that uniquely identify an {@link org.xrpl.xrpl4j.model.ledger.EscrowObject} on ledger that can be used in + * a {@link LedgerEntryRequestParams} to request an {@link org.xrpl.xrpl4j.model.ledger.EscrowObject}. + */ +@Immutable +@JsonSerialize(as = ImmutableEscrowLedgerEntryParams.class) +@JsonDeserialize(as = ImmutableEscrowLedgerEntryParams.class) +public interface EscrowLedgerEntryParams { + + /** + * Construct a {@code EscrowLedgerEntryParams} builder. + * + * @return An {@link ImmutableEscrowLedgerEntryParams.Builder}. + */ + static ImmutableEscrowLedgerEntryParams.Builder builder() { + return ImmutableEscrowLedgerEntryParams.builder(); + } + + /** + * The owner (sender) of the Escrow object. + * + * @return The {@link Address} of the owner. + */ + Address owner(); + + /** + * The Sequence Number of the transaction that created the Escrow object. + * + * @return An {@link UnsignedInteger}. + */ + UnsignedInteger seq(); + +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryRequestParams.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryRequestParams.java new file mode 100644 index 000000000..9a74f558c --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryRequestParams.java @@ -0,0 +1,456 @@ +package org.xrpl.xrpl4j.model.client.ledger; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.immutables.value.Value; +import org.immutables.value.Value.Style.BuilderVisibility; +import org.xrpl.xrpl4j.model.client.XrplRequestParams; +import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; +import org.xrpl.xrpl4j.model.ledger.AccountRootObject; +import org.xrpl.xrpl4j.model.ledger.AmmObject; +import org.xrpl.xrpl4j.model.ledger.CheckObject; +import org.xrpl.xrpl4j.model.ledger.DepositPreAuthObject; +import org.xrpl.xrpl4j.model.ledger.EscrowObject; +import org.xrpl.xrpl4j.model.ledger.LedgerObject; +import org.xrpl.xrpl4j.model.ledger.NfTokenPageObject; +import org.xrpl.xrpl4j.model.ledger.OfferObject; +import org.xrpl.xrpl4j.model.ledger.PayChannelObject; +import org.xrpl.xrpl4j.model.ledger.RippleStateObject; +import org.xrpl.xrpl4j.model.ledger.TicketObject; +import org.xrpl.xrpl4j.model.transactions.Address; +import org.xrpl.xrpl4j.model.transactions.Hash256; + +import java.util.Optional; + +/** + * Request parameters for the {@code ledger_entry} RPC. + * + *

Unlike most other immutable objects in this library, this class's builder is not accessible to developers. + * Instead, developers should construct instances of {@link LedgerEntryRequestParams} via its various static + * constructors.

+ * + *

Each static constructor constructs {@link LedgerEntryRequestParams} for a particular type of ledger entry + * as described on xrpl.org.

+ * + * @param The type of {@link LedgerObject} that will be returned in the response to the {@code ledger_entry} RPC + * call with these {@link LedgerEntryRequestParams}. + */ +@Value.Immutable +// Note: These parameters should only be constructed via the provided static constructors. Exposing the builder to +// developers allows them to specify multiple types of ledger_entry request, which is unsafe to do. +@Value.Style(builderVisibility = BuilderVisibility.PACKAGE) +@JsonSerialize(as = ImmutableLedgerEntryRequestParams.class) +@JsonDeserialize(as = ImmutableLedgerEntryRequestParams.class) +@SuppressWarnings("OverloadMethodsDeclarationOrder") +public interface LedgerEntryRequestParams extends XrplRequestParams { + + /** + * Construct a {@link LedgerEntryRequestParams} that requests a ledger entry by index. + * + * @param id The index or ID of the ledger entry as a {@link Hash256}. + * @param ledgerSpecifier A {@link LedgerSpecifier} indicating the ledger to query data from. + * @param ledgerObjectClass The class of {@link LedgerObject} that should be returned by rippled as a {@link Class} of + * {@link T}. + * @param The actual type of {@link LedgerObject} that should be returned by rippled. + * + * @return A {@link LedgerEntryRequestParams} for {@link T}. + */ + static LedgerEntryRequestParams index( + Hash256 id, + Class ledgerObjectClass, + LedgerSpecifier ledgerSpecifier + ) { + return ImmutableLedgerEntryRequestParams.builder() + .index(id) + .ledgerObjectClass(ledgerObjectClass) + .ledgerSpecifier(ledgerSpecifier) + .build(); + } + + /** + * Construct a {@link LedgerEntryRequestParams} that requests a ledger entry by index but does not narrow down the + * polymorphic type of {@link LedgerObject} that is returned. These parameters are useful when querying a ledger entry + * by ID that the developer does not know the type of at compile time. + * + * @param id The index or ID of the ledger entry as a {@link Hash256}. + * @param ledgerSpecifier A {@link LedgerSpecifier} indicating the ledger to query data from. + * + * @return A {@link LedgerEntryRequestParams} for {@link LedgerObject}. + */ + static LedgerEntryRequestParams index( + Hash256 id, + LedgerSpecifier ledgerSpecifier + ) { + return ImmutableLedgerEntryRequestParams.builder() + .index(id) + .ledgerSpecifier(ledgerSpecifier) + .build(); + } + + /** + * Construct a {@link LedgerEntryRequestParams} that requests an {@link AccountRootObject} ledger entry by address. + * + * @param address The {@link Address} of the account who owns the {@link AccountRootObject}. + * @param ledgerSpecifier A {@link LedgerSpecifier} indicating the ledger to query data from. + * + * @return A {@link LedgerEntryRequestParams} for {@link AccountRootObject}. + */ + static LedgerEntryRequestParams accountRoot( + Address address, + LedgerSpecifier ledgerSpecifier + ) { + return ImmutableLedgerEntryRequestParams.builder() + .accountRoot(address) + .ledgerSpecifier(ledgerSpecifier) + .build(); + } + + /** + * Construct a {@link LedgerEntryRequestParams} that requests an {@link AmmObject} ledger entry. + * + *

Note that although the rippled API allows you to specify either the AMM's ID or its two assets, this + * class does not allow developers to request an {@link AmmObject} by ID via this method. Instead, developers should + * use {@link LedgerEntryRequestParams#index()} and specify {@link AmmObject} as the {@code ledgerObjectClass} + * parameter.

+ * + * @param params The {@link AmmLedgerEntryParams} that uniquely identify the {@link AmmObject} on ledger. + * @param ledgerSpecifier A {@link LedgerSpecifier} indicating the ledger to query data from. + * + * @return A {@link LedgerEntryRequestParams} for {@link AmmObject}. + */ + static LedgerEntryRequestParams amm( + AmmLedgerEntryParams params, + LedgerSpecifier ledgerSpecifier + ) { + return ImmutableLedgerEntryRequestParams.builder() + .amm(params) + .ledgerSpecifier(ledgerSpecifier) + .build(); + } + + /** + * Construct a {@link LedgerEntryRequestParams} that requests an {@link OfferObject} ledger entry. + * + *

Note that although the rippled API allows you to specify either the Offer's ID or the account that created it + * and the sequence number of the transaction that created it, this class does not allow developers to request an + * {@link OfferObject} by ID via this method. Instead, developers should use {@link LedgerEntryRequestParams#index()} + * and specify {@link OfferObject} as the {@code ledgerObjectClass} parameter.

+ * + * @param params The {@link OfferLedgerEntryParams} that uniquely identify the {@link OfferObject} on + * ledger. + * @param ledgerSpecifier A {@link LedgerSpecifier} indicating the ledger to query data from. + * + * @return A {@link LedgerEntryRequestParams} for {@link OfferObject}. + */ + static LedgerEntryRequestParams offer( + OfferLedgerEntryParams params, + LedgerSpecifier ledgerSpecifier + ) { + return ImmutableLedgerEntryRequestParams.builder() + .offer(params) + .ledgerSpecifier(ledgerSpecifier) + .build(); + } + + /** + * Construct a {@link LedgerEntryRequestParams} that requests a {@link RippleStateObject} ledger entry. + * + * @param params The {@link RippleStateLedgerEntryParams} that uniquely identify the + * {@link RippleStateObject} on ledger. + * @param ledgerSpecifier A {@link LedgerSpecifier} indicating the ledger to query data from. + * + * @return A {@link LedgerEntryRequestParams} for {@link RippleStateObject}. + */ + static LedgerEntryRequestParams rippleState( + RippleStateLedgerEntryParams params, + LedgerSpecifier ledgerSpecifier + ) { + return ImmutableLedgerEntryRequestParams.builder() + .rippleState(params) + .ledgerSpecifier(ledgerSpecifier) + .build(); + } + + /** + * Construct a {@link LedgerEntryRequestParams} that requests a {@link CheckObject} ledger entry. + * + * @param id The index or ID of the {@link CheckObject}. + * @param ledgerSpecifier A {@link LedgerSpecifier} indicating the ledger to query data from. + * + * @return A {@link LedgerEntryRequestParams} for {@link RippleStateObject}. + */ + static LedgerEntryRequestParams check( + Hash256 id, + LedgerSpecifier ledgerSpecifier + ) { + return ImmutableLedgerEntryRequestParams.builder() + .check(id) + .ledgerSpecifier(ledgerSpecifier) + .build(); + } + + /** + * Construct a {@link LedgerEntryRequestParams} that requests a {@link EscrowObject} ledger entry. + * + *

Note that although the rippled API allows you to specify either the Escrow's ID or the owner and sequence + * number of the transaction that created the Escrow, this class does not allow developers to request an + * {@link EscrowObject} by ID via this method. Instead, developers should use {@link LedgerEntryRequestParams#index()} + * and specify {@link EscrowObject} as the {@code ledgerObjectClass} parameter.

+ * + * @param params The {@link EscrowLedgerEntryParams} that uniquely identify the {@link EscrowObject} on + * ledger. + * @param ledgerSpecifier A {@link LedgerSpecifier} indicating the ledger to query data from. + * + * @return A {@link LedgerEntryRequestParams} for {@link EscrowObject}. + */ + static LedgerEntryRequestParams escrow( + EscrowLedgerEntryParams params, + LedgerSpecifier ledgerSpecifier + ) { + return ImmutableLedgerEntryRequestParams.builder() + .escrow(params) + .ledgerSpecifier(ledgerSpecifier) + .build(); + } + + /** + * Construct a {@link LedgerEntryRequestParams} that requests a {@link PayChannelObject} ledger entry. + * + * @param id The index or ID of the {@link PayChannelObject}. + * @param ledgerSpecifier A {@link LedgerSpecifier} indicating the ledger to query data from. + * + * @return A {@link LedgerEntryRequestParams} for {@link PayChannelObject}. + */ + static LedgerEntryRequestParams paymentChannel( + Hash256 id, + LedgerSpecifier ledgerSpecifier + ) { + return ImmutableLedgerEntryRequestParams.builder() + .paymentChannel(id) + .ledgerSpecifier(ledgerSpecifier) + .build(); + } + + /** + * Construct a {@link LedgerEntryRequestParams} that requests a {@link DepositPreAuthObject} ledger entry. + * + *

Note that although the rippled API allows you to specify either the DepositPreAuth's ID or the owner and the + * account that is authorized, this class does not allow developers to request an {@link DepositPreAuthObject} by ID + * via this method. Instead, developers should use {@link LedgerEntryRequestParams#index()} and specify + * {@link DepositPreAuthObject} as the {@code ledgerObjectClass} parameter.

+ * + * @param params The {@link DepositPreAuthLedgerEntryParams} that uniquely identify the + * {@link DepositPreAuthObject} on ledger. + * @param ledgerSpecifier A {@link LedgerSpecifier} indicating the ledger to query data from. + * + * @return A {@link LedgerEntryRequestParams} for {@link DepositPreAuthObject}. + */ + static LedgerEntryRequestParams depositPreAuth( + DepositPreAuthLedgerEntryParams params, + LedgerSpecifier ledgerSpecifier + ) { + return ImmutableLedgerEntryRequestParams.builder() + .depositPreAuth(params) + .ledgerSpecifier(ledgerSpecifier) + .build(); + } + + /** + * Construct a {@link LedgerEntryRequestParams} that requests a {@link TicketObject} ledger entry. + * + *

Note that although the rippled API allows you to specify either the Ticket's ID or the owner of the Ticket and + * the Ticket's sequence, this class does not allow developers to request an {@link TicketObject} by ID via this + * method. Instead, developers should use {@link LedgerEntryRequestParams#index()} and specify {@link TicketObject} as + * the {@code ledgerObjectClass} parameter.

+ * + * @param params The {@link TicketLedgerEntryParams} that uniquely identify the {@link TicketObject} on + * ledger. + * @param ledgerSpecifier A {@link LedgerSpecifier} indicating the ledger to query data from. + * + * @return A {@link LedgerEntryRequestParams} for {@link TicketObject}. + */ + static LedgerEntryRequestParams ticket( + TicketLedgerEntryParams params, + LedgerSpecifier ledgerSpecifier + ) { + return ImmutableLedgerEntryRequestParams.builder() + .ticket(params) + .ledgerSpecifier(ledgerSpecifier) + .build(); + } + + /** + * Construct a {@link LedgerEntryRequestParams} that requests a {@link NfTokenPageObject} ledger entry. + * + * @param id The index or ID of the {@link NfTokenPageObject}. + * @param ledgerSpecifier A {@link LedgerSpecifier} indicating the ledger to query data from. + * + * @return A {@link LedgerEntryRequestParams} for {@link NfTokenPageObject}. + */ + static LedgerEntryRequestParams nftPage( + Hash256 id, + LedgerSpecifier ledgerSpecifier + ) { + return ImmutableLedgerEntryRequestParams.builder() + .nftPage(id) + .ledgerSpecifier(ledgerSpecifier) + .build(); + } + + /** + * Specifies the ledger version to request. A ledger version can be specified by ledger hash, numerical ledger index, + * or a shortcut value. + * + * @return A {@link LedgerSpecifier} specifying the ledger version to request. + */ + @JsonUnwrapped + LedgerSpecifier ledgerSpecifier(); + + /** + * If true, return the requested ledger entry's contents as a hex string in the XRP Ledger's binary format. Otherwise, + * return data in JSON format. This field will always be {@code false}. + * + * @return A boolean. + */ + @Value.Derived + default boolean binary() { + return false; + } + + /** + * Look up a ledger entry by ID/index. + * + * @return An optionally-present {@link Hash256}. + */ + Optional index(); + + /** + * Look up an {@link org.xrpl.xrpl4j.model.ledger.AccountRootObject} by {@link Address}. + * + * @return An optionally-present {@link Address}. + */ + @JsonProperty("account_root") + Optional
accountRoot(); + + /** + * Look up an {@link org.xrpl.xrpl4j.model.ledger.AmmObject} by {@link AmmLedgerEntryParams}. + * + * @return An optionally-present {@link AmmLedgerEntryParams}. + */ + Optional amm(); + + /** + * Look up an {@link org.xrpl.xrpl4j.model.ledger.OfferObject} by {@link OfferLedgerEntryParams}. + * + * @return An optionally-present {@link OfferLedgerEntryParams}. + */ + Optional offer(); + + /** + * Look up a {@link org.xrpl.xrpl4j.model.ledger.RippleStateObject} by {@link RippleStateLedgerEntryParams}. + * + * @return An optionally-present {@link RippleStateLedgerEntryParams}. + */ + @JsonProperty("ripple_state") + Optional rippleState(); + + /** + * Look up a {@link org.xrpl.xrpl4j.model.ledger.CheckObject} by ID. + * + * @return An optionally-present {@link Hash256}. + */ + Optional check(); + + /** + * Look up an {@link org.xrpl.xrpl4j.model.ledger.EscrowObject} by {@link EscrowLedgerEntryParams}. + * + * @return An optionally-present {@link EscrowLedgerEntryParams}. + */ + Optional escrow(); + + /** + * Look up a {@link org.xrpl.xrpl4j.model.ledger.PayChannelObject} by ID. + * + * @return An optionally-present {@link Hash256}. + */ + @JsonProperty("payment_channel") + Optional paymentChannel(); + + /** + * Look up an {@link org.xrpl.xrpl4j.model.ledger.NfTokenPageObject} by ID. + * + * @return An optionally-present {@link Hash256}. + */ + @JsonProperty("nft_page") + Optional nftPage(); + + /** + * Look up a {@link org.xrpl.xrpl4j.model.ledger.DepositPreAuthObject} by {@link DepositPreAuthLedgerEntryParams}. + * + * @return An optionally-present {@link DepositPreAuthLedgerEntryParams}. + */ + @JsonProperty("deposit_preauth") + Optional depositPreAuth(); + + /** + * Look up a {@link org.xrpl.xrpl4j.model.ledger.TicketObject} by {@link TicketLedgerEntryParams}. + * + * @return An optionally-present {@link TicketLedgerEntryParams}. + */ + Optional ticket(); + + /** + * The {@link Class} of {@link T}. This field is helpful when telling Jackson how to deserialize rippled's response to + * a {@link T}. + * + * @return A {@link Class} of type {@link T}. + */ + @JsonIgnore + @Value.Default + default Class ledgerObjectClass() { + if (accountRoot().isPresent()) { + return (Class) AccountRootObject.class; + } + + if (amm().isPresent()) { + return (Class) AmmObject.class; + } + + if (offer().isPresent()) { + return (Class) OfferObject.class; + } + + if (rippleState().isPresent()) { + return (Class) RippleStateObject.class; + } + + if (check().isPresent()) { + return (Class) CheckObject.class; + } + + if (escrow().isPresent()) { + return (Class) EscrowObject.class; + } + + if (paymentChannel().isPresent()) { + return (Class) PayChannelObject.class; + } + + if (nftPage().isPresent()) { + return (Class) NfTokenPageObject.class; + } + + if (depositPreAuth().isPresent()) { + return (Class) DepositPreAuthObject.class; + } + + if (ticket().isPresent()) { + return (Class) TicketObject.class; + } + + return (Class) LedgerObject.class; + } +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryResult.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryResult.java new file mode 100644 index 000000000..49d71c663 --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryResult.java @@ -0,0 +1,126 @@ +package org.xrpl.xrpl4j.model.client.ledger; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.immutables.value.Value; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.client.XrplResult; +import org.xrpl.xrpl4j.model.client.common.LedgerIndex; +import org.xrpl.xrpl4j.model.ledger.LedgerObject; +import org.xrpl.xrpl4j.model.transactions.Hash256; + +import java.util.Optional; + +/** + * The result of a {@code ledger_entry} RPC call. + * + * @param The type of {@link LedgerObject} contained in the result. + */ +@Immutable +@JsonSerialize(as = ImmutableLedgerEntryResult.class) +@JsonDeserialize(as = ImmutableLedgerEntryResult.class) +public interface LedgerEntryResult extends XrplResult { + + /** + * Construct a {@code LedgerEntryResult} builder. + * + * @return An {@link ImmutableLedgerEntryResult.Builder}. + */ + static ImmutableLedgerEntryResult.Builder builder() { + return ImmutableLedgerEntryResult.builder(); + } + + /** + * The ledger entry returned, as a {@link T}. + * + * @return A {@link T}. + */ + T node(); + + /** + * Unique identifying hash of the entire ledger. + * + * @return A {@link Hash256} containing the ledger hash. + */ + @JsonProperty("ledger_hash") + Optional ledgerHash(); + + /** + * Get {@link #ledgerHash()}, or throw an {@link IllegalStateException} if {@link #ledgerHash()} is empty. + * + * @return The value of {@link #ledgerHash()}. + * + * @throws IllegalStateException If {@link #ledgerHash()} is empty. + */ + @JsonIgnore + @Value.Auxiliary + default Hash256 ledgerHashSafe() { + return ledgerHash() + .orElseThrow(() -> new IllegalStateException("Result did not contain a ledgerHash.")); + } + + /** + * The {@link LedgerIndex} of this ledger. + * + * @return The {@link LedgerIndex} of this ledger. + */ + @JsonProperty("ledger_index") + Optional ledgerIndex(); + + /** + * Get {@link #ledgerIndex()}, or throw an {@link IllegalStateException} if {@link #ledgerIndex()} is empty. + * + * @return The value of {@link #ledgerIndex()}. + * + * @throws IllegalStateException If {@link #ledgerIndex()} is empty. + */ + @JsonIgnore + @Value.Auxiliary + default LedgerIndex ledgerIndexSafe() { + return ledgerIndex() + .orElseThrow(() -> new IllegalStateException("Result did not contain a ledgerIndex.")); + } + + /** + * The {@link LedgerIndex} of this ledger, if the ledger is the current ledger. Only present on a current ledger + * response. + * + * @return A {@link LedgerIndex} if this result is for the current ledger, otherwise {@link Optional#empty()}. + */ + @JsonProperty("ledger_current_index") + Optional ledgerCurrentIndex(); + + /** + * Get {@link #ledgerCurrentIndex()}, or throw an {@link IllegalStateException} if {@link #ledgerCurrentIndex()} is + * empty. + * + * @return The value of {@link #ledgerCurrentIndex()}. + * + * @throws IllegalStateException If {@link #ledgerCurrentIndex()} is empty. + */ + @JsonIgnore + @Value.Auxiliary + default LedgerIndex ledgerCurrentIndexSafe() { + return ledgerCurrentIndex() + .orElseThrow(() -> new IllegalStateException("Result did not contain a ledgerCurrentIndex.")); + } + + /** + * True if this data is from a validated ledger version; if false, this data is not final. + * + * @return {@code true} if this data is from a validated ledger version, otherwise {@code false}. + */ + @Value.Default + default boolean validated() { + return false; + } + + /** + * The ID of the ledger entry returned. + * + * @return The {@link Hash256} representing the ID of the ledger entry. + */ + Hash256 index(); +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/OfferLedgerEntryParams.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/OfferLedgerEntryParams.java new file mode 100644 index 000000000..8227de2ba --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/OfferLedgerEntryParams.java @@ -0,0 +1,41 @@ +package org.xrpl.xrpl4j.model.client.ledger; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.google.common.primitives.UnsignedInteger; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.transactions.Address; + +/** + * Parameters that uniquely identify an {@link org.xrpl.xrpl4j.model.ledger.OfferObject} on ledger that can be used in a + * {@link LedgerEntryRequestParams} to request an {@link org.xrpl.xrpl4j.model.ledger.OfferObject}. + */ +@Immutable +@JsonSerialize(as = ImmutableOfferLedgerEntryParams.class) +@JsonDeserialize(as = ImmutableOfferLedgerEntryParams.class) +public interface OfferLedgerEntryParams { + + /** + * Construct a {@code OfferLedgerEntryParams} builder. + * + * @return An {@link ImmutableOfferLedgerEntryParams.Builder}. + */ + static ImmutableOfferLedgerEntryParams.Builder builder() { + return ImmutableOfferLedgerEntryParams.builder(); + } + + /** + * The account that placed the offer. + * + * @return The {@link Address} of the account. + */ + Address account(); + + /** + * The Sequence Number of the transaction that created the Offer entry. + * + * @return An {@link UnsignedInteger}. + */ + UnsignedInteger seq(); + +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/RippleStateLedgerEntryParams.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/RippleStateLedgerEntryParams.java new file mode 100644 index 000000000..7824b388d --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/RippleStateLedgerEntryParams.java @@ -0,0 +1,78 @@ +package org.xrpl.xrpl4j.model.client.ledger; + +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.transactions.Address; + +import java.util.List; + +/** + * Parameters that uniquely identify a {@link org.xrpl.xrpl4j.model.ledger.RippleStateObject} on ledger that can be used + * in a {@link LedgerEntryRequestParams} to request a {@link org.xrpl.xrpl4j.model.ledger.RippleStateObject}. + */ +@Immutable +@JsonSerialize(as = ImmutableRippleStateLedgerEntryParams.class) +@JsonDeserialize(as = ImmutableRippleStateLedgerEntryParams.class) +public interface RippleStateLedgerEntryParams { + + /** + * Construct a {@code RippleStateLedgerEntryParams} builder. + * + * @return An {@link ImmutableRippleStateLedgerEntryParams.Builder}. + */ + static ImmutableRippleStateLedgerEntryParams.Builder builder() { + return ImmutableRippleStateLedgerEntryParams.builder(); + } + + /** + * A {@link RippleStateAccounts} containing the two accounts linked by the + * {@link org.xrpl.xrpl4j.model.ledger.RippleStateObject}. + * + * @return A {@link RippleStateAccounts}. + */ + @JsonUnwrapped + RippleStateAccounts accounts(); + + /** + * The currency code of the {@link org.xrpl.xrpl4j.model.ledger.RippleStateObject} to retrieve. + * + * @return A {@link String}. + */ + String currency(); + + /** + * Specifies two {@link Address}es of accounts that are linked by a + * {@link org.xrpl.xrpl4j.model.ledger.RippleStateObject}. + */ + @Immutable + @JsonSerialize(as = ImmutableRippleStateAccounts.class) + @JsonDeserialize(as = ImmutableRippleStateAccounts.class) + interface RippleStateAccounts { + + /** + * Construct a new {@link RippleStateAccounts}. + * + * @param account The {@link Address} of one of the accounts linked in the object. + * @param otherAccount The {@link Address} of the other account linked in the object. + * + * @return A {@link RippleStateAccounts}. + */ + static RippleStateAccounts of(Address account, Address otherAccount) { + return ImmutableRippleStateAccounts.builder() + .addAccounts(account, otherAccount) + .build(); + } + + /** + * The {@link Address}es of the accounts linked by the {@link org.xrpl.xrpl4j.model.ledger.RippleStateObject}. + * + *

Note that this is typed as a {@link List} so that this object is serialized as a JSON array.

+ * + * @return A {@link List} of {@link Address}es. + */ + List
accounts(); + + } +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/TicketLedgerEntryParams.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/TicketLedgerEntryParams.java new file mode 100644 index 000000000..67d29c44b --- /dev/null +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/client/ledger/TicketLedgerEntryParams.java @@ -0,0 +1,43 @@ +package org.xrpl.xrpl4j.model.client.ledger; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.google.common.primitives.UnsignedInteger; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.transactions.Address; + +/** + * Parameters that uniquely identify a {@link org.xrpl.xrpl4j.model.ledger.TicketObject} on ledger that can be used in a + * {@link LedgerEntryRequestParams} to request a {@link org.xrpl.xrpl4j.model.ledger.TicketObject}. + */ +@Immutable +@JsonSerialize(as = ImmutableTicketLedgerEntryParams.class) +@JsonDeserialize(as = ImmutableTicketLedgerEntryParams.class) +public interface TicketLedgerEntryParams { + + /** + * Construct a {@code TicketLedgerEntryParams} builder. + * + * @return An {@link ImmutableTicketLedgerEntryParams.Builder}. + */ + static ImmutableTicketLedgerEntryParams.Builder builder() { + return ImmutableTicketLedgerEntryParams.builder(); + } + + /** + * The owner of the Ticket. + * + * @return The {@link Address} of the owner. + */ + Address account(); + + /** + * The Ticket Sequence number of the Ticket to retrieve. + * + * @return An {@link UnsignedInteger}. + */ + @JsonProperty("ticket_seq") + UnsignedInteger ticketSeq(); + +} diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/AmmObject.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/AmmObject.java index dd696fe18..3466b047c 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/AmmObject.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/AmmObject.java @@ -8,6 +8,7 @@ import org.immutables.value.Value; import org.xrpl.xrpl4j.model.flags.Flags; import org.xrpl.xrpl4j.model.transactions.Address; +import org.xrpl.xrpl4j.model.transactions.Hash256; import org.xrpl.xrpl4j.model.transactions.IssuedCurrencyAmount; import org.xrpl.xrpl4j.model.transactions.TradingFee; @@ -19,7 +20,7 @@ * Represents an AMM ledger object, which describes a single Automated Market Maker instance. * *

This class will be marked {@link Beta} until the AMM amendment is enabled on mainnet. Its API is subject to - * change.

+ * change.

*/ @Value.Immutable @JsonSerialize(as = ImmutableAmmObject.class) @@ -91,9 +92,9 @@ default Flags flags() { Optional auctionSlot(); /** - * The total outstanding balance of liquidity provider tokens from this AMM instance. The holders of these tokens - * can vote on the AMM's trading fee in proportion to their holdings, or redeem the tokens for a share of the AMM's - * assets which grows with the trading fees collected. + * The total outstanding balance of liquidity provider tokens from this AMM instance. The holders of these tokens can + * vote on the AMM's trading fee in proportion to their holdings, or redeem the tokens for a share of the AMM's assets + * which grows with the trading fees collected. * * @return An {@link IssuedCurrencyAmount}. */ @@ -117,6 +118,18 @@ default Flags flags() { @JsonProperty("VoteSlots") List voteSlots(); + /** + * A hint indicating which page of the sender's owner directory links to this object, in case the directory consists + * of multiple pages. + * + *

Note: The object does not contain a direct link to the owner directory containing it, since that value can be + * derived from the Account. + * + * @return A {@link String} containing the owner node hint. + */ + @JsonProperty("OwnerNode") + String ownerNode(); + /** * Unwraps the {@link VoteEntryWrapper}s in {@link #voteSlots()} for easier access to {@link VoteEntry}s. * @@ -129,4 +142,11 @@ default List voteSlotsUnwrapped() { .map(VoteEntryWrapper::voteEntry) .collect(Collectors.toList()); } + + /** + * The unique ID of the {@link AmmObject}. + * + * @return A {@link Hash256}. + */ + Hash256 index(); } diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/TicketObject.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/TicketObject.java index 0a3a834fe..f103989d7 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/TicketObject.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/ledger/TicketObject.java @@ -110,4 +110,11 @@ default Flags flags() { */ @JsonProperty("TicketSequence") UnsignedInteger ticketSequence(); + + /** + * The unique ID of the {@link TicketObject}. + * + * @return A {@link Hash256}. + */ + Hash256 index(); } diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/crypto/TestConstants.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/crypto/TestConstants.java index 1de8d07cb..534fb6d5f 100644 --- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/crypto/TestConstants.java +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/crypto/TestConstants.java @@ -26,6 +26,7 @@ import org.xrpl.xrpl4j.crypto.keys.PrivateKey; import org.xrpl.xrpl4j.crypto.keys.PublicKey; import org.xrpl.xrpl4j.model.transactions.Address; +import org.xrpl.xrpl4j.model.transactions.Hash256; /** * Constants used for testing. @@ -74,4 +75,9 @@ static PrivateKey getEcPrivateKey() { // Both generated from Passphrase.of("hello") Address ED_ADDRESS = Address.of("rwGWYtRR6jJJJq7FKQg74YwtkiPyUqJ466"); Address EC_ADDRESS = Address.of("rD8ATvjj9mfnFuYYTGRNb9DygnJW9JNN1C"); + + /** + * A sample {@link Hash256}. + */ + Hash256 HASH_256 = Hash256.of("6B1011EF3BC3ED619B15979EF75C1C60D9181F3DDE641AD3019318D3900CEE2E"); } diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryRequestParamsTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryRequestParamsTest.java new file mode 100644 index 000000000..1955540d3 --- /dev/null +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryRequestParamsTest.java @@ -0,0 +1,432 @@ +package org.xrpl.xrpl4j.model.client.ledger; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.xrpl.xrpl4j.crypto.TestConstants.ED_ADDRESS; +import static org.xrpl.xrpl4j.crypto.TestConstants.HASH_256; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.primitives.UnsignedInteger; +import org.json.JSONException; +import org.junit.jupiter.api.Test; +import org.skyscreamer.jsonassert.JSONAssert; +import org.skyscreamer.jsonassert.JSONCompareMode; +import org.xrpl.xrpl4j.model.AbstractJsonTest; +import org.xrpl.xrpl4j.model.client.XrplRequestParams; +import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; +import org.xrpl.xrpl4j.model.client.ledger.RippleStateLedgerEntryParams.RippleStateAccounts; +import org.xrpl.xrpl4j.model.ledger.AccountRootObject; +import org.xrpl.xrpl4j.model.ledger.AmmObject; +import org.xrpl.xrpl4j.model.ledger.CheckObject; +import org.xrpl.xrpl4j.model.ledger.DepositPreAuthObject; +import org.xrpl.xrpl4j.model.ledger.EscrowObject; +import org.xrpl.xrpl4j.model.ledger.Issue; +import org.xrpl.xrpl4j.model.ledger.LedgerObject; +import org.xrpl.xrpl4j.model.ledger.NfTokenPageObject; +import org.xrpl.xrpl4j.model.ledger.OfferObject; +import org.xrpl.xrpl4j.model.ledger.PayChannelObject; +import org.xrpl.xrpl4j.model.ledger.RippleStateObject; +import org.xrpl.xrpl4j.model.ledger.TicketObject; +import org.xrpl.xrpl4j.model.transactions.Address; + +class LedgerEntryRequestParamsTest extends AbstractJsonTest { + + @Test + void testTypedIndexParams() throws JSONException, JsonProcessingException { + LedgerEntryRequestParams params = LedgerEntryRequestParams.index(HASH_256, AmmObject.class, + LedgerSpecifier.VALIDATED); + assertThat(params.index()).isNotEmpty().get().isEqualTo(HASH_256); + assertThat(params.ledgerObjectClass()).isEqualTo(AmmObject.class); + + assertThat(params.accountRoot()).isEmpty(); + assertThat(params.amm()).isEmpty(); + assertThat(params.offer()).isEmpty(); + assertThat(params.rippleState()).isEmpty(); + assertThat(params.check()).isEmpty(); + assertThat(params.escrow()).isEmpty(); + assertThat(params.paymentChannel()).isEmpty(); + assertThat(params.depositPreAuth()).isEmpty(); + assertThat(params.ticket()).isEmpty(); + assertThat(params.nftPage()).isEmpty(); + + String json = String.format("{\n" + + " \"index\": \"%s\",\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + " }", HASH_256); + + String serialized = objectMapper.writeValueAsString(params); + JSONAssert.assertEquals(json, serialized, JSONCompareMode.STRICT); + + // Note that when deserializing from JSON, we cannot figure out what ledgerObjectClass should be based on the JSON. + // This is likely fine because request params should never really be getting deserialized by this library. + XrplRequestParams deserialized = objectMapper.readValue(serialized, params.getClass()); + assertThat(deserialized).usingRecursiveComparison().ignoringFields("ledgerObjectClass") + .isEqualTo(params); + } + + @Test + void testUntypedIndexParams() throws JSONException, JsonProcessingException { + LedgerEntryRequestParams params = LedgerEntryRequestParams.index(HASH_256, LedgerSpecifier.VALIDATED); + assertThat(params.index()).isNotEmpty().get().isEqualTo(HASH_256); + assertThat(params.ledgerObjectClass()).isEqualTo(LedgerObject.class); + + assertThat(params.accountRoot()).isEmpty(); + assertThat(params.amm()).isEmpty(); + assertThat(params.offer()).isEmpty(); + assertThat(params.rippleState()).isEmpty(); + assertThat(params.check()).isEmpty(); + assertThat(params.escrow()).isEmpty(); + assertThat(params.paymentChannel()).isEmpty(); + assertThat(params.depositPreAuth()).isEmpty(); + assertThat(params.ticket()).isEmpty(); + assertThat(params.nftPage()).isEmpty(); + + String json = String.format("{\n" + + " \"index\": \"%s\",\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + " }", HASH_256); + + assertCanSerializeAndDeserialize(params, json); + } + + @Test + void testAccountRootParams() throws JSONException, JsonProcessingException { + LedgerEntryRequestParams params = LedgerEntryRequestParams.accountRoot( + ED_ADDRESS, LedgerSpecifier.VALIDATED + ); + assertThat(params.accountRoot()).isNotEmpty().get().isEqualTo(ED_ADDRESS); + assertThat(params.ledgerObjectClass()).isEqualTo(AccountRootObject.class); + + assertThat(params.index()).isEmpty(); + assertThat(params.amm()).isEmpty(); + assertThat(params.offer()).isEmpty(); + assertThat(params.rippleState()).isEmpty(); + assertThat(params.check()).isEmpty(); + assertThat(params.escrow()).isEmpty(); + assertThat(params.paymentChannel()).isEmpty(); + assertThat(params.depositPreAuth()).isEmpty(); + assertThat(params.ticket()).isEmpty(); + assertThat(params.nftPage()).isEmpty(); + + String json = String.format("{\n" + + " \"account_root\": \"%s\",\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + " }", ED_ADDRESS); + + assertCanSerializeAndDeserialize(params, json); + } + + @Test + void testAmmParams() throws JSONException, JsonProcessingException { + AmmLedgerEntryParams ammParams = AmmLedgerEntryParams.builder() + .asset(Issue.XRP) + .asset2( + Issue.builder() + .currency("TST") + .issuer(Address.of("rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd")) + .build() + ) + .build(); + + LedgerEntryRequestParams params = LedgerEntryRequestParams.amm(ammParams, LedgerSpecifier.VALIDATED); + assertThat(params.amm()).isNotEmpty().get().isEqualTo(ammParams); + assertThat(params.ledgerObjectClass()).isEqualTo(AmmObject.class); + + assertThat(params.index()).isEmpty(); + assertThat(params.accountRoot()).isEmpty(); + assertThat(params.offer()).isEmpty(); + assertThat(params.rippleState()).isEmpty(); + assertThat(params.check()).isEmpty(); + assertThat(params.escrow()).isEmpty(); + assertThat(params.paymentChannel()).isEmpty(); + assertThat(params.depositPreAuth()).isEmpty(); + assertThat(params.ticket()).isEmpty(); + assertThat(params.nftPage()).isEmpty(); + + String json = "{\n" + + " \"amm\": {\n" + + " \"asset\": {\n" + + " \"currency\": \"XRP\"\n" + + " },\n" + + " \"asset2\": {\n" + + " \"currency\" : \"TST\",\n" + + " \"issuer\" : \"rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd\"\n" + + " }\n" + + " },\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + " }"; + + assertCanSerializeAndDeserialize(params, json); + } + + @Test + void testOfferParams() throws JSONException, JsonProcessingException { + OfferLedgerEntryParams offerParams = OfferLedgerEntryParams.builder() + .account(Address.of("rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn")) + .seq(UnsignedInteger.valueOf(359)) + .build(); + + LedgerEntryRequestParams params = LedgerEntryRequestParams.offer( + offerParams, LedgerSpecifier.VALIDATED + ); + assertThat(params.offer()).isNotEmpty().get().isEqualTo(offerParams); + assertThat(params.ledgerObjectClass()).isEqualTo(OfferObject.class); + + assertThat(params.index()).isEmpty(); + assertThat(params.accountRoot()).isEmpty(); + assertThat(params.amm()).isEmpty(); + assertThat(params.rippleState()).isEmpty(); + assertThat(params.check()).isEmpty(); + assertThat(params.escrow()).isEmpty(); + assertThat(params.paymentChannel()).isEmpty(); + assertThat(params.depositPreAuth()).isEmpty(); + assertThat(params.ticket()).isEmpty(); + assertThat(params.nftPage()).isEmpty(); + + String json = "{\n" + + " \"offer\": {\n" + + " \"account\": \"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\",\n" + + " \"seq\": 359\n" + + " },\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + " }"; + + assertCanSerializeAndDeserialize(params, json); + } + + @Test + void testRippleStateParams() throws JSONException, JsonProcessingException { + RippleStateLedgerEntryParams rippleStateParams = RippleStateLedgerEntryParams.builder() + .accounts(RippleStateAccounts.of( + Address.of("rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"), + Address.of("rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW") + )) + .currency("USD") + .build(); + + LedgerEntryRequestParams params = LedgerEntryRequestParams.rippleState( + rippleStateParams, LedgerSpecifier.VALIDATED + ); + assertThat(params.rippleState()).isNotEmpty().get().isEqualTo(rippleStateParams); + assertThat(params.ledgerObjectClass()).isEqualTo(RippleStateObject.class); + + assertThat(params.index()).isEmpty(); + assertThat(params.accountRoot()).isEmpty(); + assertThat(params.amm()).isEmpty(); + assertThat(params.offer()).isEmpty(); + assertThat(params.check()).isEmpty(); + assertThat(params.escrow()).isEmpty(); + assertThat(params.paymentChannel()).isEmpty(); + assertThat(params.depositPreAuth()).isEmpty(); + assertThat(params.ticket()).isEmpty(); + assertThat(params.nftPage()).isEmpty(); + + String json = "{\n" + + " \"ripple_state\": {\n" + + " \"accounts\": [\n" + + " \"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\",\n" + + " \"rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW\"\n" + + " ],\n" + + " \"currency\": \"USD\"\n" + + " },\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + " }"; + + assertCanSerializeAndDeserialize(params, json); + } + + @Test + void testCheckParams() throws JSONException, JsonProcessingException { + LedgerEntryRequestParams params = LedgerEntryRequestParams.check(HASH_256, LedgerSpecifier.VALIDATED); + assertThat(params.check()).isNotEmpty().get().isEqualTo(HASH_256); + assertThat(params.ledgerObjectClass()).isEqualTo(CheckObject.class); + + assertThat(params.index()).isEmpty(); + assertThat(params.accountRoot()).isEmpty(); + assertThat(params.amm()).isEmpty(); + assertThat(params.offer()).isEmpty(); + assertThat(params.rippleState()).isEmpty(); + assertThat(params.escrow()).isEmpty(); + assertThat(params.paymentChannel()).isEmpty(); + assertThat(params.depositPreAuth()).isEmpty(); + assertThat(params.ticket()).isEmpty(); + assertThat(params.nftPage()).isEmpty(); + + String json = String.format("{\n" + + " \"check\": \"%s\",\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + " }", HASH_256); + + assertCanSerializeAndDeserialize(params, json); + } + + @Test + void testEscrowParams() throws JSONException, JsonProcessingException { + EscrowLedgerEntryParams escrowParams = EscrowLedgerEntryParams.builder() + .owner(Address.of("rL4fPHi2FWGwRGRQSH7gBcxkuo2b9NTjKK")) + .seq(UnsignedInteger.valueOf(126)) + .build(); + LedgerEntryRequestParams params = LedgerEntryRequestParams.escrow( + escrowParams, LedgerSpecifier.VALIDATED + ); + assertThat(params.escrow()).isNotEmpty().get().isEqualTo(escrowParams); + assertThat(params.ledgerObjectClass()).isEqualTo(EscrowObject.class); + + assertThat(params.index()).isEmpty(); + assertThat(params.accountRoot()).isEmpty(); + assertThat(params.amm()).isEmpty(); + assertThat(params.offer()).isEmpty(); + assertThat(params.rippleState()).isEmpty(); + assertThat(params.check()).isEmpty(); + assertThat(params.paymentChannel()).isEmpty(); + assertThat(params.depositPreAuth()).isEmpty(); + assertThat(params.ticket()).isEmpty(); + assertThat(params.nftPage()).isEmpty(); + + String json = "{\n" + + " \"escrow\": {\n" + + " \"owner\": \"rL4fPHi2FWGwRGRQSH7gBcxkuo2b9NTjKK\",\n" + + " \"seq\": 126\n" + + " },\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + " }"; + + assertCanSerializeAndDeserialize(params, json); + } + + @Test + void testPaymentChannelParams() throws JSONException, JsonProcessingException { + LedgerEntryRequestParams params = LedgerEntryRequestParams.paymentChannel( + HASH_256, LedgerSpecifier.VALIDATED + ); + assertThat(params.paymentChannel()).isNotEmpty().get().isEqualTo(HASH_256); + assertThat(params.ledgerObjectClass()).isEqualTo(PayChannelObject.class); + + assertThat(params.index()).isEmpty(); + assertThat(params.accountRoot()).isEmpty(); + assertThat(params.amm()).isEmpty(); + assertThat(params.offer()).isEmpty(); + assertThat(params.rippleState()).isEmpty(); + assertThat(params.check()).isEmpty(); + assertThat(params.escrow()).isEmpty(); + assertThat(params.depositPreAuth()).isEmpty(); + assertThat(params.ticket()).isEmpty(); + assertThat(params.nftPage()).isEmpty(); + + String json = String.format("{\n" + + " \"payment_channel\": \"%s\",\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + " }", HASH_256); + + assertCanSerializeAndDeserialize(params, json); + } + + @Test + void testDepositPreAuthParams() throws JSONException, JsonProcessingException { + DepositPreAuthLedgerEntryParams depositPreAuthParams = DepositPreAuthLedgerEntryParams.builder() + .owner(Address.of("rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn")) + .authorized(Address.of("ra5nK24KXen9AHvsdFTKHSANinZseWnPcX")) + .build(); + LedgerEntryRequestParams params = LedgerEntryRequestParams.depositPreAuth( + depositPreAuthParams, + LedgerSpecifier.VALIDATED + ); + assertThat(params.depositPreAuth()).isNotEmpty().get().isEqualTo(depositPreAuthParams); + assertThat(params.ledgerObjectClass()).isEqualTo(DepositPreAuthObject.class); + + assertThat(params.index()).isEmpty(); + assertThat(params.accountRoot()).isEmpty(); + assertThat(params.amm()).isEmpty(); + assertThat(params.offer()).isEmpty(); + assertThat(params.rippleState()).isEmpty(); + assertThat(params.check()).isEmpty(); + assertThat(params.escrow()).isEmpty(); + assertThat(params.paymentChannel()).isEmpty(); + assertThat(params.ticket()).isEmpty(); + assertThat(params.nftPage()).isEmpty(); + + String json = "{\n" + + " \"deposit_preauth\": {\n" + + " \"owner\": \"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\",\n" + + " \"authorized\": \"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX\"\n" + + " },\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + " }"; + + assertCanSerializeAndDeserialize(params, json); + } + + @Test + void testTicketParams() throws JSONException, JsonProcessingException { + TicketLedgerEntryParams ticketParams = TicketLedgerEntryParams.builder() + .account(Address.of("rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn")) + .ticketSeq(UnsignedInteger.valueOf(389)) + .build(); + LedgerEntryRequestParams params = LedgerEntryRequestParams.ticket( + ticketParams, + LedgerSpecifier.VALIDATED + ); + assertThat(params.ticket()).isNotEmpty().get().isEqualTo(ticketParams); + assertThat(params.ledgerObjectClass()).isEqualTo(TicketObject.class); + + assertThat(params.index()).isEmpty(); + assertThat(params.accountRoot()).isEmpty(); + assertThat(params.amm()).isEmpty(); + assertThat(params.offer()).isEmpty(); + assertThat(params.rippleState()).isEmpty(); + assertThat(params.check()).isEmpty(); + assertThat(params.escrow()).isEmpty(); + assertThat(params.paymentChannel()).isEmpty(); + assertThat(params.depositPreAuth()).isEmpty(); + assertThat(params.nftPage()).isEmpty(); + + String json = "{\n" + + " \"ticket\": {\n" + + " \"account\": \"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\",\n" + + " \"ticket_seq\": 389\n" + + " },\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + " }"; + + assertCanSerializeAndDeserialize(params, json); + } + + @Test + void testNftPageParams() throws JSONException, JsonProcessingException { + LedgerEntryRequestParams params = LedgerEntryRequestParams.nftPage( + HASH_256, + LedgerSpecifier.VALIDATED + ); + assertThat(params.nftPage()).isNotEmpty().get().isEqualTo(HASH_256); + assertThat(params.ledgerObjectClass()).isEqualTo(NfTokenPageObject.class); + + assertThat(params.index()).isEmpty(); + assertThat(params.accountRoot()).isEmpty(); + assertThat(params.amm()).isEmpty(); + assertThat(params.offer()).isEmpty(); + assertThat(params.rippleState()).isEmpty(); + assertThat(params.check()).isEmpty(); + assertThat(params.escrow()).isEmpty(); + assertThat(params.paymentChannel()).isEmpty(); + assertThat(params.depositPreAuth()).isEmpty(); + assertThat(params.ticket()).isEmpty(); + + String json = String.format("{\n" + + " \"nft_page\": \"%s\",\n" + + " \"binary\": false,\n" + + " \"ledger_index\": \"validated\"\n" + + " }", HASH_256); + + assertCanSerializeAndDeserialize(params, json); + } +} \ No newline at end of file diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryResultTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryResultTest.java new file mode 100644 index 000000000..8bcfb0a8d --- /dev/null +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/ledger/LedgerEntryResultTest.java @@ -0,0 +1,676 @@ +package org.xrpl.xrpl4j.model.client.ledger; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.xrpl.xrpl4j.crypto.TestConstants.HASH_256; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.google.common.primitives.UnsignedInteger; +import com.google.common.primitives.UnsignedLong; +import org.json.JSONException; +import org.junit.jupiter.api.Test; +import org.xrpl.xrpl4j.model.AbstractJsonTest; +import org.xrpl.xrpl4j.model.client.common.LedgerIndex; +import org.xrpl.xrpl4j.model.flags.AccountRootFlags; +import org.xrpl.xrpl4j.model.flags.OfferFlags; +import org.xrpl.xrpl4j.model.flags.RippleStateFlags; +import org.xrpl.xrpl4j.model.ledger.AccountRootObject; +import org.xrpl.xrpl4j.model.ledger.AmmObject; +import org.xrpl.xrpl4j.model.ledger.AuctionSlot; +import org.xrpl.xrpl4j.model.ledger.CheckObject; +import org.xrpl.xrpl4j.model.ledger.DepositPreAuthObject; +import org.xrpl.xrpl4j.model.ledger.EscrowObject; +import org.xrpl.xrpl4j.model.ledger.Issue; +import org.xrpl.xrpl4j.model.ledger.LedgerObject; +import org.xrpl.xrpl4j.model.ledger.NfToken; +import org.xrpl.xrpl4j.model.ledger.NfTokenPageObject; +import org.xrpl.xrpl4j.model.ledger.NfTokenWrapper; +import org.xrpl.xrpl4j.model.ledger.OfferObject; +import org.xrpl.xrpl4j.model.ledger.PayChannelObject; +import org.xrpl.xrpl4j.model.ledger.RippleStateObject; +import org.xrpl.xrpl4j.model.ledger.TicketObject; +import org.xrpl.xrpl4j.model.ledger.VoteEntry; +import org.xrpl.xrpl4j.model.ledger.VoteEntryWrapper; +import org.xrpl.xrpl4j.model.transactions.Address; +import org.xrpl.xrpl4j.model.transactions.Hash256; +import org.xrpl.xrpl4j.model.transactions.IssuedCurrencyAmount; +import org.xrpl.xrpl4j.model.transactions.NfTokenId; +import org.xrpl.xrpl4j.model.transactions.NfTokenUri; +import org.xrpl.xrpl4j.model.transactions.TradingFee; +import org.xrpl.xrpl4j.model.transactions.VoteWeight; +import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount; + +class LedgerEntryResultTest extends AbstractJsonTest { + + @Test + void testAccountRootResult() throws JSONException, JsonProcessingException { + LedgerEntryResult result = LedgerEntryResult.builder() + .ledgerIndex(LedgerIndex.of(UnsignedInteger.valueOf(83125250))) + .ledgerHash(Hash256.of("783625588CF01BD3D0E9C2719B92098A6A87649AEFF5AE970CD68B911436C1D7")) + .validated(true) + .index(Hash256.of("13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8")) + .node( + AccountRootObject.builder() + .account(Address.of("rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn")) + .accountTransactionId(Hash256.of("932CC7E9BAC1F7B9FA5381679F293EEC0A646E5E7F2F6D14C85FEE2102F0E66C")) + .balance(XrpCurrencyAmount.ofDrops(1066107694)) + .domain("6D64756F31332E636F6D") + .emailHash("98B4375E1D753E5B91627516F6D70977") + .flags(AccountRootFlags.of(9568256)) + .messageKey("0000000000000000000000070000000300") + .ownerCount(UnsignedInteger.valueOf(17)) + .previousTransactionId(Hash256.of("7E5F3FB60E1177F8AF8A9EAC7982F27FA5494FDEA871B23B4B149939A5A7A7BB")) + .previousTransactionLedgerSequence(UnsignedInteger.valueOf(82357607)) + .regularKey(Address.of("rD9iJmieYHn8jTtPjwwkW2Wm9sVDvPXLoJ")) + .sequence(UnsignedInteger.valueOf(393)) + .ticketCount(UnsignedInteger.valueOf(5)) + .transferRate(UnsignedInteger.valueOf(4294967295L)) + .index(Hash256.of("13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8")) + .build() + ) + .status("success") + .build(); + + String json = "{\n" + + " \"ledger_hash\": \"783625588CF01BD3D0E9C2719B92098A6A87649AEFF5AE970CD68B911436C1D7\",\n" + + " \"ledger_index\": 83125250,\n" + + " \"validated\": true,\n" + + " \"index\": \"13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8\",\n" + + " \"node\": {\n" + + " \"Account\": \"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\",\n" + + " \"AccountTxnID\": \"932CC7E9BAC1F7B9FA5381679F293EEC0A646E5E7F2F6D14C85FEE2102F0E66C\",\n" + + " \"Balance\": \"1066107694\",\n" + + " \"Domain\": \"6D64756F31332E636F6D\",\n" + + " \"EmailHash\": \"98B4375E1D753E5B91627516F6D70977\",\n" + + " \"Flags\": 9568256,\n" + + " \"LedgerEntryType\": \"AccountRoot\",\n" + + " \"MessageKey\": \"0000000000000000000000070000000300\",\n" + + " \"OwnerCount\": 17,\n" + + " \"PreviousTxnID\": \"7E5F3FB60E1177F8AF8A9EAC7982F27FA5494FDEA871B23B4B149939A5A7A7BB\",\n" + + " \"PreviousTxnLgrSeq\": 82357607,\n" + + " \"RegularKey\": \"rD9iJmieYHn8jTtPjwwkW2Wm9sVDvPXLoJ\",\n" + + " \"Sequence\": 393,\n" + + " \"TicketCount\": 5,\n" + + " \"TransferRate\": 4294967295,\n" + + " \"index\": \"13F1A95D7AAB7108D5CE7EEAF504B2894B8C674E6D68499076441C4837282BF8\"\n" + + " },\n" + + " \"status\": \"success\"\n" + + " }"; + + assertCanSerializeAndDeserialize(result, json); + } + + @Test + void testAmmResult() throws JSONException, JsonProcessingException { + LedgerEntryResult result = LedgerEntryResult.builder() + .ledgerIndex(LedgerIndex.of(UnsignedInteger.valueOf(607272))) + .ledgerHash(Hash256.of("EEB650A0FD3CF0A5CE68B3DBD67C902FEC85E6AFAE1D0A7A7AF4BAD2F38557C7")) + .validated(true) + .index(Hash256.of("6BCD7E451DDA015FB307DAD9208A98A2DC3AC4D1448E624B42C89246DCF08692")) + .node( + AmmObject.builder() + .account(Address.of("rNqXnvSYbjZeJQ6jWcf6T5mnNMRPzHXaZW")) + .asset(Issue.XRP) + .asset2( + Issue.builder() + .currency("7872706C346A436F696E00000000000000000000") + .issuer(Address.of("rDeo7rDoYw6AUKGneWwfkHPsMJagxcGWy1")) + .build() + ) + .auctionSlot( + AuctionSlot.builder() + .account(Address.of("rDeo7rDoYw6AUKGneWwfkHPsMJagxcGWy1")) + .discountedFee(TradingFee.of(UnsignedInteger.valueOf(77))) + .expiration(UnsignedInteger.valueOf(750359162)) + .price( + IssuedCurrencyAmount.builder() + .currency("03DCF8F3910BFE6AB56136A90BD41E0902E23C4F") + .value("0") + .issuer(Address.of("rNqXnvSYbjZeJQ6jWcf6T5mnNMRPzHXaZW")) + .build() + ) + .build() + ) + .lpTokenBalance( + IssuedCurrencyAmount.builder() + .currency("03DCF8F3910BFE6AB56136A90BD41E0902E23C4F") + .issuer(Address.of("rNqXnvSYbjZeJQ6jWcf6T5mnNMRPzHXaZW")) + .value("70606.68056410846") + .build() + ) + .ownerNode("0") + .tradingFee(TradingFee.of(UnsignedInteger.valueOf(778))) + .addVoteSlots( + VoteEntryWrapper.of(VoteEntry.builder() + .account(Address.of("rDeo7rDoYw6AUKGneWwfkHPsMJagxcGWy1")) + .tradingFee(TradingFee.of(UnsignedInteger.valueOf(1000))) + .voteWeight(VoteWeight.of(UnsignedInteger.valueOf(70815))) + .build()), + VoteEntryWrapper.of(VoteEntry.builder() + .account(Address.of("rHPoJo9R3QdQjK6XdWL5hY2eTc4wUeNYzW")) + .tradingFee(TradingFee.of(UnsignedInteger.valueOf(240))) + .voteWeight(VoteWeight.of(UnsignedInteger.valueOf(29185))) + .build()) + ) + .index(Hash256.of("6BCD7E451DDA015FB307DAD9208A98A2DC3AC4D1448E624B42C89246DCF08692")) + .build() + ) + .status("success") + .build(); + + String json = "{\n" + + " \"index\": \"6BCD7E451DDA015FB307DAD9208A98A2DC3AC4D1448E624B42C89246DCF08692\",\n" + + " \"ledger_hash\": \"EEB650A0FD3CF0A5CE68B3DBD67C902FEC85E6AFAE1D0A7A7AF4BAD2F38557C7\",\n" + + " \"ledger_index\": 607272,\n" + + " \"node\": {\n" + + " \"Account\": \"rNqXnvSYbjZeJQ6jWcf6T5mnNMRPzHXaZW\",\n" + + " \"Asset\": {\n" + + " \"currency\": \"XRP\"\n" + + " },\n" + + " \"Asset2\": {\n" + + " \"currency\": \"7872706C346A436F696E00000000000000000000\",\n" + + " \"issuer\": \"rDeo7rDoYw6AUKGneWwfkHPsMJagxcGWy1\"\n" + + " },\n" + + " \"AuctionSlot\": {\n" + + " \"Account\": \"rDeo7rDoYw6AUKGneWwfkHPsMJagxcGWy1\",\n" + + " \"DiscountedFee\": 77,\n" + + " \"Expiration\": 750359162,\n" + + " \"Price\": {\n" + + " \"currency\": \"03DCF8F3910BFE6AB56136A90BD41E0902E23C4F\",\n" + + " \"issuer\": \"rNqXnvSYbjZeJQ6jWcf6T5mnNMRPzHXaZW\",\n" + + " \"value\": \"0\"\n" + + " }\n" + + " },\n" + + " \"Flags\": 0,\n" + + " \"LPTokenBalance\": {\n" + + " \"currency\": \"03DCF8F3910BFE6AB56136A90BD41E0902E23C4F\",\n" + + " \"issuer\": \"rNqXnvSYbjZeJQ6jWcf6T5mnNMRPzHXaZW\",\n" + + " \"value\": \"70606.68056410846\"\n" + + " },\n" + + " \"LedgerEntryType\": \"AMM\",\n" + + " \"OwnerNode\": \"0\",\n" + + " \"TradingFee\": 778,\n" + + " \"VoteSlots\": [\n" + + " {\n" + + " \"VoteEntry\": {\n" + + " \"Account\": \"rDeo7rDoYw6AUKGneWwfkHPsMJagxcGWy1\",\n" + + " \"TradingFee\": 1000,\n" + + " \"VoteWeight\": 70815\n" + + " }\n" + + " },\n" + + " {\n" + + " \"VoteEntry\": {\n" + + " \"Account\": \"rHPoJo9R3QdQjK6XdWL5hY2eTc4wUeNYzW\",\n" + + " \"TradingFee\": 240,\n" + + " \"VoteWeight\": 29185\n" + + " }\n" + + " }\n" + + " ],\n" + + " \"index\": \"6BCD7E451DDA015FB307DAD9208A98A2DC3AC4D1448E624B42C89246DCF08692\"\n" + + " },\n" + + " \"status\": \"success\",\n" + + " \"validated\": true\n" + + " }"; + + assertCanSerializeAndDeserialize(result, json); + } + + @Test + void testOfferResult() throws JSONException, JsonProcessingException { + LedgerEntryResult result = LedgerEntryResult.builder() + .ledgerIndex(LedgerIndex.of(UnsignedInteger.valueOf(41931093))) + .ledgerHash(Hash256.of("54FE89D2FF925D623D386A03B402FDB22B2D2D058A62AEE441CA52CC9AA92BB1")) + .validated(true) + .index(Hash256.of("066B61CF7248A5A08672541077E3C58EAFD1FA52DDF6B4FD93595E16542C0A14")) + .node( + OfferObject.builder() + .account(Address.of("rNdCZMZqHCo5VkrvsmNVt8ZtdpahT7rDKx")) + .bookDirectory(Hash256.of("D30EF7A9BFCCEE47AF722871D91E1E21522DF5141CA29AB05B071AFD498D0000")) + .bookNode("0") + .flags(OfferFlags.of(131072)) + .ownerNode("0") + .previousTransactionId(Hash256.of("9D613D7E1E34DA5BE421E06002441F1E183C0B7B3323E1898FC585144A7C13B1")) + .previousTransactionLedgerSequence(UnsignedInteger.valueOf(41931065)) + .sequence(UnsignedInteger.valueOf(41931063)) + .takerGets( + IssuedCurrencyAmount.builder() + .currency("USD") + .issuer(Address.of("rNdCZMZqHCo5VkrvsmNVt8ZtdpahT7rDKx")) + .value("100") + .build() + ) + .takerPays(XrpCurrencyAmount.ofDrops(200000000)) + .index(Hash256.of("066B61CF7248A5A08672541077E3C58EAFD1FA52DDF6B4FD93595E16542C0A14")) + .build() + ) + .status("success") + .build(); + + String json = "{\n" + + " \"index\": \"066B61CF7248A5A08672541077E3C58EAFD1FA52DDF6B4FD93595E16542C0A14\",\n" + + " \"ledger_hash\": \"54FE89D2FF925D623D386A03B402FDB22B2D2D058A62AEE441CA52CC9AA92BB1\",\n" + + " \"ledger_index\": 41931093,\n" + + " \"node\": {\n" + + " \"Account\": \"rNdCZMZqHCo5VkrvsmNVt8ZtdpahT7rDKx\",\n" + + " \"BookDirectory\": \"D30EF7A9BFCCEE47AF722871D91E1E21522DF5141CA29AB05B071AFD498D0000\",\n" + + " \"BookNode\": \"0\",\n" + + " \"Flags\": 131072,\n" + + " \"LedgerEntryType\": \"Offer\",\n" + + " \"OwnerNode\": \"0\",\n" + + " \"PreviousTxnID\": \"9D613D7E1E34DA5BE421E06002441F1E183C0B7B3323E1898FC585144A7C13B1\",\n" + + " \"PreviousTxnLgrSeq\": 41931065,\n" + + " \"Sequence\": 41931063,\n" + + " \"TakerGets\": {\n" + + " \"currency\": \"USD\",\n" + + " \"issuer\": \"rNdCZMZqHCo5VkrvsmNVt8ZtdpahT7rDKx\",\n" + + " \"value\": \"100\"\n" + + " },\n" + + " \"TakerPays\": \"200000000\",\n" + + " \"index\": \"066B61CF7248A5A08672541077E3C58EAFD1FA52DDF6B4FD93595E16542C0A14\"\n" + + " },\n" + + " \"status\": \"success\",\n" + + " \"validated\": true\n" + + " }"; + + assertCanSerializeAndDeserialize(result, json); + } + + @Test + void testRippleStateResult() throws JSONException, JsonProcessingException { + LedgerEntryResult result = LedgerEntryResult.builder() + .ledgerIndex(LedgerIndex.of(UnsignedInteger.valueOf(83126482))) + .ledgerHash(Hash256.of("995F5C7565065ED88C251225C15A02C95D6AADD4AC75E199A9234FA8322B5F84")) + .validated(true) + .index(Hash256.of("6A409D31A016227B74D6A14C307239B2BBBE0CFBFCF7C271BFAF20CAA7A1E6DA")) + .node( + RippleStateObject.builder() + .balance( + IssuedCurrencyAmount.builder() + .currency("CNY") + .issuer(Address.of("rrrrrrrrrrrrrrrrrrrrBZbvji")) + .value("0") + .build() + ) + .flags(RippleStateFlags.of(2228224)) + .highLimit( + IssuedCurrencyAmount.builder() + .currency("CNY") + .issuer(Address.of("rHzKtpcB1KC1YuU4PBhk9m2abqrf2kZsfV")) + .value("1000000000") + .build() + ) + .highNode("0") + .lowLimit( + IssuedCurrencyAmount.builder() + .currency("CNY") + .issuer(Address.of("rJ1adrpGS3xsnQMb9Cw54tWJVFPuSdZHK")) + .value("0") + .build() + ) + .lowNode("2") + .previousTransactionId(Hash256.of("9A5E68C795D68665A648A5A05E5BC94AA3681400353236F75139BD102D9406FD")) + .previousTransactionLedgerSequence(UnsignedInteger.valueOf(69746363)) + .index(Hash256.of("6A409D31A016227B74D6A14C307239B2BBBE0CFBFCF7C271BFAF20CAA7A1E6DA")) + .build() + ) + .status("success") + .build(); + + String json = "{\n" + + " \"ledger_hash\": \"995F5C7565065ED88C251225C15A02C95D6AADD4AC75E199A9234FA8322B5F84\",\n" + + " \"ledger_index\": 83126482,\n" + + " \"validated\": true,\n" + + " \"index\": \"6A409D31A016227B74D6A14C307239B2BBBE0CFBFCF7C271BFAF20CAA7A1E6DA\",\n" + + " \"node\": {\n" + + " \"Balance\": {\n" + + " \"currency\": \"CNY\",\n" + + " \"issuer\": \"rrrrrrrrrrrrrrrrrrrrBZbvji\",\n" + + " \"value\": \"0\"\n" + + " },\n" + + " \"Flags\": 2228224,\n" + + " \"HighLimit\": {\n" + + " \"currency\": \"CNY\",\n" + + " \"issuer\": \"rHzKtpcB1KC1YuU4PBhk9m2abqrf2kZsfV\",\n" + + " \"value\": \"1000000000\"\n" + + " },\n" + + " \"HighNode\": \"0\",\n" + + " \"LedgerEntryType\": \"RippleState\",\n" + + " \"LowLimit\": {\n" + + " \"currency\": \"CNY\",\n" + + " \"issuer\": \"rJ1adrpGS3xsnQMb9Cw54tWJVFPuSdZHK\",\n" + + " \"value\": \"0\"\n" + + " },\n" + + " \"LowNode\": \"2\",\n" + + " \"PreviousTxnID\": \"9A5E68C795D68665A648A5A05E5BC94AA3681400353236F75139BD102D9406FD\",\n" + + " \"PreviousTxnLgrSeq\": 69746363,\n" + + " \"index\": \"6A409D31A016227B74D6A14C307239B2BBBE0CFBFCF7C271BFAF20CAA7A1E6DA\"\n" + + " },\n" + + " \"status\": \"success\"\n" + + " }"; + + assertCanSerializeAndDeserialize(result, json); + } + + @Test + void testCheckResult() throws JSONException, JsonProcessingException { + LedgerEntryResult result = LedgerEntryResult.builder() + .ledgerIndex(LedgerIndex.of(UnsignedInteger.valueOf(83126482))) + .ledgerHash(Hash256.of("995F5C7565065ED88C251225C15A02C95D6AADD4AC75E199A9234FA8322B5F84")) + .validated(true) + .index(Hash256.of("56B5D2CC81461E339424869D0F5A2F4F24095B74FCD6F79960EF2D5EA10FBE00")) + .node( + CheckObject.builder() + .account(Address.of("rJk8P3yazgCSSvWXavKKCY5Y3tk4UGCiFF")) + .destination(Address.of("rHr2n1zVm5nzadgtJY5G2mUYnmWcrxfTbQ")) + .destinationNode("0") + .invoiceId(Hash256.of("5D059E085A91283DA8F2C1B8DB973994A3250ABDEDB934799A9C3EE243D3DFBD")) + .ownerNode("0") + .previousTxnId(Hash256.of("7F2DB52CA2D2C600748D7B1DF060964C74BF0219B8EF055DAB151F8A23CA1B09")) + .previousTransactionLedgerSequence(UnsignedInteger.valueOf(41931458)) + .sendMax(XrpCurrencyAmount.ofDrops(12345)) + .sequence(UnsignedInteger.valueOf(41931456)) + .index(Hash256.of("56B5D2CC81461E339424869D0F5A2F4F24095B74FCD6F79960EF2D5EA10FBE00")) + .build() + ) + .status("success") + .build(); + + String json = "{\n" + + " \"ledger_hash\": \"995F5C7565065ED88C251225C15A02C95D6AADD4AC75E199A9234FA8322B5F84\",\n" + + " \"ledger_index\": 83126482,\n" + + " \"validated\": true,\n" + + " \"index\": \"56B5D2CC81461E339424869D0F5A2F4F24095B74FCD6F79960EF2D5EA10FBE00\",\n" + + " \"node\": {\n" + + " \"Account\": \"rJk8P3yazgCSSvWXavKKCY5Y3tk4UGCiFF\",\n" + + " \"Destination\": \"rHr2n1zVm5nzadgtJY5G2mUYnmWcrxfTbQ\",\n" + + " \"DestinationNode\": \"0\",\n" + + " \"Flags\": 0,\n" + + " \"InvoiceID\": \"5D059E085A91283DA8F2C1B8DB973994A3250ABDEDB934799A9C3EE243D3DFBD\",\n" + + " \"LedgerEntryType\": \"Check\",\n" + + " \"OwnerNode\": \"0\",\n" + + " \"PreviousTxnID\": \"7F2DB52CA2D2C600748D7B1DF060964C74BF0219B8EF055DAB151F8A23CA1B09\",\n" + + " \"PreviousTxnLgrSeq\": 41931458,\n" + + " \"SendMax\": \"12345\",\n" + + " \"Sequence\": 41931456,\n" + + " \"index\": \"56B5D2CC81461E339424869D0F5A2F4F24095B74FCD6F79960EF2D5EA10FBE00\"\n" + + " },\n" + + " \"status\": \"success\"\n" + + " }"; + + assertCanSerializeAndDeserialize(result, json); + } + + @Test + void testEscrowResult() throws JSONException, JsonProcessingException { + LedgerEntryResult result = LedgerEntryResult.builder() + .ledgerIndex(LedgerIndex.of(UnsignedInteger.valueOf(83126482))) + .ledgerHash(Hash256.of("995F5C7565065ED88C251225C15A02C95D6AADD4AC75E199A9234FA8322B5F84")) + .validated(true) + .index(Hash256.of("ABC67054C15F79FEE9183B44D2E16CA06A1804E023E6A2EDB288F4976B1BFEC5")) + .node( + EscrowObject.builder() + .account(Address.of("rEWt92vANNAghT9CC83DtnDDWZcJEL5gk1")) + .amount(XrpCurrencyAmount.ofDrops(123456)) + .cancelAfter(UnsignedLong.valueOf(750277784)) + .destination(Address.of("rMuTP1PFEFMVhDYKNBLgmre5YrCzVoCYjm")) + .destinationNode("0") + .finishAfter(UnsignedLong.valueOf(750277689)) + .ownerNode("0") + .previousTransactionId(Hash256.of("466C5F96809D62385073F6BA43F5A3C217C96C4A493B7F64F6CE5B5B64278AAA")) + .previousTransactionLedgerSequence(UnsignedInteger.valueOf(41931760)) + .index(Hash256.of("ABC67054C15F79FEE9183B44D2E16CA06A1804E023E6A2EDB288F4976B1BFEC5")) + .build() + ) + .status("success") + .build(); + + String json = "{\n" + + " \"ledger_hash\": \"995F5C7565065ED88C251225C15A02C95D6AADD4AC75E199A9234FA8322B5F84\",\n" + + " \"ledger_index\": 83126482,\n" + + " \"validated\": true,\n" + + " \"index\": \"ABC67054C15F79FEE9183B44D2E16CA06A1804E023E6A2EDB288F4976B1BFEC5\",\n" + + " \"node\": {\n" + + " \"Account\": \"rEWt92vANNAghT9CC83DtnDDWZcJEL5gk1\",\n" + + " \"Amount\": \"123456\",\n" + + " \"CancelAfter\": 750277784,\n" + + " \"Destination\": \"rMuTP1PFEFMVhDYKNBLgmre5YrCzVoCYjm\",\n" + + " \"DestinationNode\": \"0\",\n" + + " \"FinishAfter\": 750277689,\n" + + " \"Flags\": 0,\n" + + " \"LedgerEntryType\": \"Escrow\",\n" + + " \"OwnerNode\": \"0\",\n" + + " \"PreviousTxnID\": \"466C5F96809D62385073F6BA43F5A3C217C96C4A493B7F64F6CE5B5B64278AAA\",\n" + + " \"PreviousTxnLgrSeq\": 41931760,\n" + + " \"index\": \"ABC67054C15F79FEE9183B44D2E16CA06A1804E023E6A2EDB288F4976B1BFEC5\"\n" + + " },\n" + + " \"status\": \"success\"\n" + + " }"; + + assertCanSerializeAndDeserialize(result, json); + } + + @Test + void testPaymentChannelResult() throws JSONException, JsonProcessingException { + LedgerEntryResult result = LedgerEntryResult.builder() + .ledgerIndex(LedgerIndex.of(UnsignedInteger.valueOf(83126482))) + .ledgerHash(Hash256.of("995F5C7565065ED88C251225C15A02C95D6AADD4AC75E199A9234FA8322B5F84")) + .validated(true) + .index(Hash256.of("7474D1ED2DE25B055AD3C8473DDD69553ACD7325BF2B15C83D54E743C576C615")) + .node( + PayChannelObject.builder() + .account(Address.of("rtqQepGRnrvaHCDyLHcc8xY7uCTnV1aRT")) + .amount(XrpCurrencyAmount.ofDrops(10000)) + .balance(XrpCurrencyAmount.ofDrops(0)) + .cancelAfter(UnsignedLong.valueOf(533171558)) + .destination(Address.of("r4sxKQshFFUvN8xDiP6KsnUKTyFi1un8UQ")) + .ownerNode("0") + .previousTransactionId(Hash256.of("987A299731B80FF14519FF28F18C14B0C55487133E086EAC895099428D57737C")) + .previousTransactionLedgerSequence(UnsignedInteger.valueOf(41931835)) + .publicKey("EDAF1B0148D4FBB6BC0FCDA97C917C0BD831A654EBFD9B7D84FCB13ADE1BCB5C44") + .settleDelay(UnsignedLong.ONE) + .index(Hash256.of("7474D1ED2DE25B055AD3C8473DDD69553ACD7325BF2B15C83D54E743C576C615")) + .build() + ) + .status("success") + .build(); + + String json = "{\n" + + " \"ledger_hash\": \"995F5C7565065ED88C251225C15A02C95D6AADD4AC75E199A9234FA8322B5F84\",\n" + + " \"ledger_index\": 83126482,\n" + + " \"validated\": true,\n" + + " \"index\": \"7474D1ED2DE25B055AD3C8473DDD69553ACD7325BF2B15C83D54E743C576C615\",\n" + + " \"node\": {\n" + + " \"Account\": \"rtqQepGRnrvaHCDyLHcc8xY7uCTnV1aRT\",\n" + + " \"Amount\": \"10000\",\n" + + " \"Balance\": \"0\",\n" + + " \"CancelAfter\": 533171558,\n" + + " \"Destination\": \"r4sxKQshFFUvN8xDiP6KsnUKTyFi1un8UQ\",\n" + + " \"Flags\": 0,\n" + + " \"LedgerEntryType\": \"PayChannel\",\n" + + " \"OwnerNode\": \"0\",\n" + + " \"PreviousTxnID\": \"987A299731B80FF14519FF28F18C14B0C55487133E086EAC895099428D57737C\",\n" + + " \"PreviousTxnLgrSeq\": 41931835,\n" + + " \"PublicKey\": \"EDAF1B0148D4FBB6BC0FCDA97C917C0BD831A654EBFD9B7D84FCB13ADE1BCB5C44\",\n" + + " \"SettleDelay\": 1,\n" + + " \"index\": \"7474D1ED2DE25B055AD3C8473DDD69553ACD7325BF2B15C83D54E743C576C615\"\n" + + " },\n" + + " \"status\": \"success\"\n" + + " }"; + + assertCanSerializeAndDeserialize(result, json); + } + + @Test + void testDepositPreAuthResult() throws JSONException, JsonProcessingException { + LedgerEntryResult result = LedgerEntryResult.builder() + .ledgerIndex(LedgerIndex.of(UnsignedInteger.valueOf(83126482))) + .ledgerHash(Hash256.of("995F5C7565065ED88C251225C15A02C95D6AADD4AC75E199A9234FA8322B5F84")) + .validated(true) + .index(Hash256.of("4CFA41F0CEB3BBECB0799BCD4E70057A80B98E762AD655D005BE90992E32CDF7")) + .node( + DepositPreAuthObject.builder() + .account(Address.of("rnmLMp1znQHpSM7xKzL1rg9unXiu1o8ptU")) + .authorize(Address.of("r4yaMT4QVKFQsyw5sLrJMETe3Wx1L5P9Pe")) + .ownerNode("0") + .previousTransactionId(Hash256.of("8D2D634EC7E5B4C6BCB5D4DD72575D42A60A11AD91E5A991692E525E6BF463BA")) + .previousTransactionLedgerSequence(UnsignedInteger.valueOf(41931900)) + .index(Hash256.of("4CFA41F0CEB3BBECB0799BCD4E70057A80B98E762AD655D005BE90992E32CDF7")) + .build() + ) + .status("success") + .build(); + + String json = "{\n" + + " \"ledger_hash\": \"995F5C7565065ED88C251225C15A02C95D6AADD4AC75E199A9234FA8322B5F84\",\n" + + " \"ledger_index\": 83126482,\n" + + " \"validated\": true,\n" + + " \"index\": \"4CFA41F0CEB3BBECB0799BCD4E70057A80B98E762AD655D005BE90992E32CDF7\",\n" + + " \"node\": {\n" + + " \"Account\": \"rnmLMp1znQHpSM7xKzL1rg9unXiu1o8ptU\",\n" + + " \"Authorize\": \"r4yaMT4QVKFQsyw5sLrJMETe3Wx1L5P9Pe\",\n" + + " \"Flags\": 0,\n" + + " \"LedgerEntryType\": \"DepositPreauth\",\n" + + " \"OwnerNode\": \"0\",\n" + + " \"PreviousTxnID\": \"8D2D634EC7E5B4C6BCB5D4DD72575D42A60A11AD91E5A991692E525E6BF463BA\",\n" + + " \"PreviousTxnLgrSeq\": 41931900,\n" + + " \"index\": \"4CFA41F0CEB3BBECB0799BCD4E70057A80B98E762AD655D005BE90992E32CDF7\"\n" + + " },\n" + + " \"status\": \"success\"\n" + + " }"; + + assertCanSerializeAndDeserialize(result, json); + } + + @Test + void testTicketResult() throws JSONException, JsonProcessingException { + LedgerEntryResult result = LedgerEntryResult.builder() + .ledgerIndex(LedgerIndex.of(UnsignedInteger.valueOf(83126482))) + .ledgerHash(Hash256.of("995F5C7565065ED88C251225C15A02C95D6AADD4AC75E199A9234FA8322B5F84")) + .validated(true) + .index(Hash256.of("8A0FB133F2D9875961990CE1F6CBB08120C7BD9B330B5D2C9718DE2A4ABCFC47")) + .node( + TicketObject.builder() + .account(Address.of("rKfyHN2fbAJuHtSc1gStGDxxbq4kf9VPFQ")) + .ownerNode("0") + .previousTransactionId(Hash256.of("AB5B87765DABF11B9FE5B40506E355532BBE3CF0ADC8C15AF1CD82F4F68CD13D")) + .previousTransactionLedgerSequence(UnsignedInteger.valueOf(41932010)) + .ticketSequence(UnsignedInteger.valueOf(41932009)) + .index(Hash256.of("8A0FB133F2D9875961990CE1F6CBB08120C7BD9B330B5D2C9718DE2A4ABCFC47")) + .build() + ) + .status("success") + .build(); + + String json = "{\n" + + " \"ledger_hash\": \"995F5C7565065ED88C251225C15A02C95D6AADD4AC75E199A9234FA8322B5F84\",\n" + + " \"ledger_index\": 83126482,\n" + + " \"validated\": true,\n" + + " \"index\": \"8A0FB133F2D9875961990CE1F6CBB08120C7BD9B330B5D2C9718DE2A4ABCFC47\",\n" + + " \"node\": {\n" + + " \"Account\": \"rKfyHN2fbAJuHtSc1gStGDxxbq4kf9VPFQ\",\n" + + " \"Flags\": 0,\n" + + " \"LedgerEntryType\": \"Ticket\",\n" + + " \"OwnerNode\": \"0\",\n" + + " \"PreviousTxnID\": \"AB5B87765DABF11B9FE5B40506E355532BBE3CF0ADC8C15AF1CD82F4F68CD13D\",\n" + + " \"PreviousTxnLgrSeq\": 41932010,\n" + + " \"TicketSequence\": 41932009,\n" + + " \"index\": \"8A0FB133F2D9875961990CE1F6CBB08120C7BD9B330B5D2C9718DE2A4ABCFC47\"\n" + + " },\n" + + " \"status\": \"success\"\n" + + " }"; + + assertCanSerializeAndDeserialize(result, json); + } + + @Test + void testNftPageResult() throws JSONException, JsonProcessingException { + LedgerEntryResult result = LedgerEntryResult.builder() + .ledgerIndex(LedgerIndex.of(UnsignedInteger.valueOf(83126482))) + .ledgerHash(Hash256.of("995F5C7565065ED88C251225C15A02C95D6AADD4AC75E199A9234FA8322B5F84")) + .validated(true) + .index(Hash256.of("4070656F661A60726DBB384E09F6E36B88071072FFFFFFFFFFFFFFFFFFFFFFFF")) + .node( + NfTokenPageObject.builder() + .addNfTokens( + NfTokenWrapper.of( + NfToken.builder() + .nfTokenId(NfTokenId.of("000000004070656F661A60726DBB384E09F6E36B880710720000099A00000000")) + .uri(NfTokenUri.of( + "697066733A2F2F62616679626569676479727A74357366703775646D376875373675683779323" + + "66E6634646675796C71616266336F636C67747179353566627A6469") + ) + .build() + ) + ) + + .previousTransactionId(Hash256.of("C94CF5BC9DF78A75997E93C71CDD8A2776E2DF279DD6F394BF4976045D960C4B")) + .previousTransactionLedgerSequence(LedgerIndex.of(UnsignedInteger.valueOf(41932089))) + .index(Hash256.of("4070656F661A60726DBB384E09F6E36B88071072FFFFFFFFFFFFFFFFFFFFFFFF")) + .build() + ) + .status("success") + .build(); + + String json = "{\n" + + " \"ledger_hash\": \"995F5C7565065ED88C251225C15A02C95D6AADD4AC75E199A9234FA8322B5F84\",\n" + + " \"ledger_index\": 83126482,\n" + + " \"validated\": true,\n" + + " \"index\": \"4070656F661A60726DBB384E09F6E36B88071072FFFFFFFFFFFFFFFFFFFFFFFF\",\n" + + " \"node\": {\n" + + " \"LedgerEntryType\": \"NFTokenPage\",\n" + + " \"NFTokens\": [\n" + + " {\n" + + " \"NFToken\": {\n" + + " \"NFTokenID\": \"000000004070656F661A60726DBB384E09F6E36B880710720000099A00000000\",\n" + + " \"URI\": \"697066733A2F2F62616679626569676479727A74357366703775646D37687537367568377" + + "932366E6634646675796C71616266336F636C67747179353566627A6469\"\n" + + " }\n" + + " }\n" + + " ],\n" + + " \"PreviousTxnID\": \"C94CF5BC9DF78A75997E93C71CDD8A2776E2DF279DD6F394BF4976045D960C4B\",\n" + + " \"PreviousTxnLgrSeq\": 41932089,\n" + + " \"index\": \"4070656F661A60726DBB384E09F6E36B88071072FFFFFFFFFFFFFFFFFFFFFFFF\"\n" + + " },\n" + + " \"status\": \"success\"\n" + + " }"; + + assertCanSerializeAndDeserialize(result, json); + } + + @Test + void testWithHashAndLedgerIndex() { + LedgerEntryResult result = LedgerEntryResult.builder() + .node(mock(LedgerObject.class)) + .ledgerHash(HASH_256) + .ledgerIndex(LedgerIndex.of(UnsignedInteger.ONE)) + .index(HASH_256) + .build(); + + assertThat(result.ledgerHash()).isNotEmpty().get().isEqualTo(result.ledgerHashSafe()); + assertThat(result.ledgerIndex()).isNotEmpty().get().isEqualTo(result.ledgerIndexSafe()); + assertThat(result.ledgerCurrentIndex()).isEmpty(); + assertThatThrownBy(result::ledgerCurrentIndexSafe) + .isInstanceOf(IllegalStateException.class) + .hasMessage("Result did not contain a ledgerCurrentIndex."); + } + + @Test + void testWithLedgerCurrentIndex() { + LedgerEntryResult result = LedgerEntryResult.builder() + .node(mock(LedgerObject.class)) + .ledgerCurrentIndex(LedgerIndex.of(UnsignedInteger.ONE)) + .index(HASH_256) + .build(); + + assertThat(result.ledgerCurrentIndex()).isNotEmpty().get().isEqualTo(result.ledgerCurrentIndexSafe()); + assertThat(result.ledgerHash()).isEmpty(); + assertThat(result.ledgerIndex()).isEmpty(); + assertThatThrownBy(result::ledgerHashSafe) + .isInstanceOf(IllegalStateException.class) + .hasMessage("Result did not contain a ledgerHash."); + assertThatThrownBy(result::ledgerIndexSafe) + .isInstanceOf(IllegalStateException.class) + .hasMessage("Result did not contain a ledgerIndex."); + } +} \ No newline at end of file diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/ledger/AmmObjectTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/ledger/AmmObjectTest.java index d6d1bd329..ec28b7e23 100644 --- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/ledger/AmmObjectTest.java +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/ledger/AmmObjectTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.mockito.Mockito.mock; +import static org.xrpl.xrpl4j.crypto.TestConstants.HASH_256; import com.fasterxml.jackson.core.JsonProcessingException; import com.google.common.primitives.UnsignedInteger; @@ -37,6 +38,8 @@ void voteSlotsUnwrapped() { VoteEntryWrapper.of(voteEntry1), VoteEntryWrapper.of(voteEntry2) ) + .index(HASH_256) + .ownerNode("0") .build(); assertThat(ammObject.voteSlotsUnwrapped()).asList() @@ -97,9 +100,11 @@ void testJson() throws JSONException, JsonProcessingException { .expiration(UnsignedInteger.valueOf(721870180)) .build() ) + .index(HASH_256) + .ownerNode("0") .build(); - String json = "{\n" + + String json = String.format("{\n" + " \"Account\" : \"rE54zDvgnghAoPopCgvtiqWNq3dU5y836S\",\n" + " \"LedgerEntryType\" : \"AMM\",\n" + " \"Asset\" : " + objectMapper.writeValueAsString(ammObject.asset()) + "," + @@ -108,10 +113,12 @@ void testJson() throws JSONException, JsonProcessingException { " \"Flags\" : 0,\n" + " \"LPTokenBalance\" : " + objectMapper.writeValueAsString(ammObject.lpTokenBalance()) + "," + " \"TradingFee\" : 600,\n" + + " \"index\" : %s,\n" + + " \"OwnerNode\" : \"0\",\n" + " \"VoteSlots\" : [\n" + objectMapper.writeValueAsString(ammObject.voteSlots().get(0)) + " ]\n" + - "}"; + "}", HASH_256); assertCanSerializeAndDeserialize(ammObject, json); } diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/ledger/TicketObjectJsonTests.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/ledger/TicketObjectJsonTests.java index 3d2ba457e..3fb25e4a2 100644 --- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/ledger/TicketObjectJsonTests.java +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/ledger/TicketObjectJsonTests.java @@ -20,6 +20,8 @@ * =========================LICENSE_END================================== */ +import static org.xrpl.xrpl4j.crypto.TestConstants.HASH_256; + import com.fasterxml.jackson.core.JsonProcessingException; import com.google.common.primitives.UnsignedInteger; import org.json.JSONException; @@ -38,17 +40,19 @@ void testJson() throws JSONException, JsonProcessingException { .previousTransactionId(Hash256.of("F19AD4577212D3BEACA0F75FE1BA1644F2E854D46E8D62E9C95D18E9708CBFB1")) .previousTransactionLedgerSequence(UnsignedInteger.valueOf(4)) .ticketSequence(UnsignedInteger.valueOf(3)) + .index(HASH_256) .build(); - String json = "{\n" + + String json = String.format("{\n" + " \"Account\" : \"rEhxGqkqPPSxQ3P25J66ft5TwpzV14k2de\",\n" + " \"Flags\" : 0,\n" + " \"LedgerEntryType\" : \"Ticket\",\n" + " \"OwnerNode\" : \"0000000000000000\",\n" + " \"PreviousTxnID\" : \"F19AD4577212D3BEACA0F75FE1BA1644F2E854D46E8D62E9C95D18E9708CBFB1\",\n" + " \"PreviousTxnLgrSeq\" : 4,\n" + + " \"index\" : %s,\n" + " \"TicketSequence\" : 3\n" + - "}"; + "}", HASH_256); assertCanSerializeAndDeserialize(ticketObject, json); } diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountSetIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountSetIT.java index 0ea468489..18e2e9378 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountSetIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountSetIT.java @@ -30,12 +30,17 @@ import org.xrpl.xrpl4j.crypto.signing.SingleSignedTransaction; import org.xrpl.xrpl4j.crypto.signing.bc.BcSignatureService; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; +import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; import org.xrpl.xrpl4j.model.client.fees.FeeResult; import org.xrpl.xrpl4j.model.client.fees.FeeUtils; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryRequestParams; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryResult; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.client.transactions.TransactionResult; import org.xrpl.xrpl4j.model.flags.AccountRootFlags; import org.xrpl.xrpl4j.model.flags.AccountSetTransactionFlags; +import org.xrpl.xrpl4j.model.ledger.AccountRootObject; +import org.xrpl.xrpl4j.model.ledger.LedgerObject; import org.xrpl.xrpl4j.model.transactions.AccountSet; import org.xrpl.xrpl4j.model.transactions.AccountSet.AccountSetFlag; @@ -59,9 +64,12 @@ public void enableAllAndDisableOne() throws JsonRpcClientErrorException, JsonPro AccountInfoResult accountInfo = this.scanForResult( () -> this.getValidatedAccountInfo(keyPair.publicKey().deriveAddress()) ); + assertThat(accountInfo.status()).isNotEmpty().get().isEqualTo("success"); assertThat(accountInfo.accountData().flags().lsfGlobalFreeze()).isEqualTo(false); + assertEntryEqualsAccountInfo(keyPair, accountInfo); + UnsignedInteger sequence = accountInfo.accountData().sequence(); ////////////////////// // Set asfAccountTxnID (no corresponding ledger flag) @@ -384,10 +392,34 @@ void submitAndRetrieveAccountSetWithZeroClearFlagAndSetFlag() assertThat(accountSetTransactionResult.transaction().clearFlag()).isNotEmpty().get().isEqualTo(AccountSetFlag.NONE); } + ////////////////////// // Test Helpers ////////////////////// + private void assertEntryEqualsAccountInfo( + KeyPair keyPair, + AccountInfoResult accountInfo + ) throws JsonRpcClientErrorException { + LedgerEntryResult accountRoot = xrplClient.ledgerEntry( + LedgerEntryRequestParams.accountRoot(keyPair.publicKey().deriveAddress(), LedgerSpecifier.VALIDATED) + ); + + assertThat(accountInfo.accountData()).isEqualTo(accountRoot.node()); + + LedgerEntryResult entryByIndex = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(accountRoot.index(), AccountRootObject.class, LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(accountRoot.node()); + + LedgerEntryResult entryByIndexUnTyped = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(accountRoot.index(), LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(entryByIndexUnTyped.node()); + } + private void assertSetFlag( final KeyPair keyPair, final UnsignedInteger sequence, @@ -420,12 +452,14 @@ private void assertSetFlag( ///////////////////////// // Validate Account State - this.scanForResult( + AccountInfoResult accountInfo = this.scanForResult( () -> this.getValidatedAccountInfo(keyPair.publicKey().deriveAddress()), accountInfoResult -> { logger.info("AccountInfoResponse Flags: {}", accountInfoResult.accountData().flags()); return accountInfoResult.accountData().flags().isSet(accountRootFlag); }); + + assertEntryEqualsAccountInfo(keyPair, accountInfo); } private void assertClearFlag( @@ -460,11 +494,13 @@ private void assertClearFlag( ///////////////////////// // Validate Account State - this.scanForResult( + AccountInfoResult accountInfo = this.scanForResult( () -> this.getValidatedAccountInfo(keyPair.publicKey().deriveAddress()), accountInfoResult -> { logger.info("AccountInfoResponse Flags: {}", accountInfoResult.accountData().flags()); return !accountInfoResult.accountData().flags().isSet(accountRootFlag); }); + + assertEntryEqualsAccountInfo(keyPair, accountInfo); } } diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AmmIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AmmIT.java index 861930403..074472a7c 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AmmIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AmmIT.java @@ -6,6 +6,7 @@ import com.google.common.base.Strings; import com.google.common.io.BaseEncoding; import com.google.common.primitives.UnsignedInteger; +import com.google.common.primitives.UnsignedLong; import org.junit.jupiter.api.Test; import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; import org.xrpl.xrpl4j.crypto.keys.KeyPair; @@ -18,27 +19,32 @@ import org.xrpl.xrpl4j.model.client.accounts.AccountLinesResult; import org.xrpl.xrpl4j.model.client.accounts.TrustLine; import org.xrpl.xrpl4j.model.client.amm.AmmInfoAuctionSlot; +import org.xrpl.xrpl4j.model.client.amm.AmmInfoAuthAccount; import org.xrpl.xrpl4j.model.client.amm.AmmInfoRequestParams; import org.xrpl.xrpl4j.model.client.amm.AmmInfoResult; -import org.xrpl.xrpl4j.model.client.common.LedgerIndex; import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; +import org.xrpl.xrpl4j.model.client.common.TimeUtils; import org.xrpl.xrpl4j.model.client.fees.FeeResult; import org.xrpl.xrpl4j.model.client.fees.FeeUtils; +import org.xrpl.xrpl4j.model.client.ledger.AmmLedgerEntryParams; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryRequestParams; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryResult; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.flags.AmmDepositFlags; import org.xrpl.xrpl4j.model.flags.AmmWithdrawFlags; +import org.xrpl.xrpl4j.model.ledger.AmmObject; +import org.xrpl.xrpl4j.model.ledger.AuctionSlot; import org.xrpl.xrpl4j.model.ledger.AuthAccount; import org.xrpl.xrpl4j.model.ledger.AuthAccountWrapper; import org.xrpl.xrpl4j.model.ledger.Issue; +import org.xrpl.xrpl4j.model.ledger.LedgerObject; import org.xrpl.xrpl4j.model.transactions.AccountSet; import org.xrpl.xrpl4j.model.transactions.AccountSet.AccountSetFlag; import org.xrpl.xrpl4j.model.transactions.AmmBid; import org.xrpl.xrpl4j.model.transactions.AmmCreate; -import org.xrpl.xrpl4j.model.transactions.AmmDelete; import org.xrpl.xrpl4j.model.transactions.AmmDeposit; import org.xrpl.xrpl4j.model.transactions.AmmVote; import org.xrpl.xrpl4j.model.transactions.AmmWithdraw; -import org.xrpl.xrpl4j.model.transactions.ImmutableAmmWithdraw; import org.xrpl.xrpl4j.model.transactions.IssuedCurrencyAmount; import org.xrpl.xrpl4j.model.transactions.TradingFee; import org.xrpl.xrpl4j.model.transactions.TransactionResultCodes; @@ -46,6 +52,7 @@ import java.math.BigDecimal; import java.math.RoundingMode; +import java.util.stream.Collectors; public class AmmIT extends AbstractIT { @@ -405,6 +412,62 @@ private AmmInfoResult getAmmInfo(KeyPair issuerKeyPair) throws JsonRpcClientErro assertThat(ammInfoByAccount).isEqualTo(ammInfoResult); + LedgerEntryResult ammObject = xrplClient.ledgerEntry( + LedgerEntryRequestParams.amm( + AmmLedgerEntryParams.builder() + .asset(Issue.XRP) + .asset2( + Issue.builder() + .issuer(issuerKeyPair.publicKey().deriveAddress()) + .currency(xrpl4jCoin) + .build() + ) + .build(), + LedgerSpecifier.VALIDATED + ) + ); + + assertThat(ammObject.node().account()).isEqualTo(ammInfoByAccount.amm().account()); + assertThat(ammObject.node().asset()).isEqualTo(Issue.XRP); + assertThat(ammObject.node().asset2()).isEqualTo( + Issue.builder() + .issuer(((IssuedCurrencyAmount) ammInfoByAccount.amm().amount2()).issuer()) + .currency(((IssuedCurrencyAmount) ammInfoByAccount.amm().amount2()).currency()) + .build() + ); + assertThat( + (ammObject.node().auctionSlot().isPresent() && ammInfoByAccount.amm().auctionSlot().isPresent()) || + (!ammObject.node().auctionSlot().isPresent() && !ammInfoByAccount.amm().auctionSlot().isPresent()) + ).isTrue(); + if (ammObject.node().auctionSlot().isPresent()) { + AuctionSlot entryAuctionSlot = ammObject.node().auctionSlot().get(); + AmmInfoAuctionSlot infoAuctionSlot = ammInfoByAccount.amm().auctionSlot().get(); + assertThat(entryAuctionSlot.account()).isEqualTo(infoAuctionSlot.account()); + assertThat(entryAuctionSlot.authAccountsAddresses()).isEqualTo(infoAuctionSlot.authAccounts().stream().map( + AmmInfoAuthAccount::account).collect(Collectors.toList())); + assertThat(entryAuctionSlot.price()).isEqualTo(infoAuctionSlot.price()); + assertThat(TimeUtils.xrplTimeToZonedDateTime(UnsignedLong.valueOf(entryAuctionSlot.expiration().longValue()))) + .isEqualTo(infoAuctionSlot.expiration()); + assertThat(entryAuctionSlot.discountedFee()).isEqualTo(infoAuctionSlot.discountedFee()); + } + + assertThat(ammObject.node().lpTokenBalance()).isEqualTo(ammInfoByAccount.amm().lpToken()); + assertThat(ammObject.node().tradingFee()).isEqualTo(ammInfoByAccount.amm().tradingFee()); + + assertThat(ammObject.node().voteSlots().size()).isEqualTo(ammInfoByAccount.amm().voteSlots().size()); + + LedgerEntryResult entryByIndex = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(ammObject.index(), AmmObject.class, LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(ammObject.node()); + + LedgerEntryResult entryByIndexUnTyped = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(ammObject.index(), LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(entryByIndexUnTyped.node()); + return ammInfoResult; } diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/CheckIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/CheckIT.java index 40928ec6d..5e4b93ee2 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/CheckIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/CheckIT.java @@ -30,10 +30,14 @@ import org.xrpl.xrpl4j.crypto.keys.KeyPair; import org.xrpl.xrpl4j.crypto.signing.SingleSignedTransaction; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; +import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; import org.xrpl.xrpl4j.model.client.fees.FeeResult; import org.xrpl.xrpl4j.model.client.fees.FeeUtils; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryRequestParams; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryResult; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.ledger.CheckObject; +import org.xrpl.xrpl4j.model.ledger.EscrowObject; import org.xrpl.xrpl4j.model.ledger.LedgerObject; import org.xrpl.xrpl4j.model.transactions.CheckCancel; import org.xrpl.xrpl4j.model.transactions.CheckCash; @@ -97,6 +101,8 @@ public void createXrpCheckAndCash() throws JsonRpcClientErrorException, JsonProc .filter(findCheck(sourceKeyPair, destinationKeyPair, invoiceId)) .findFirst().get(); + assertEntryEqualsObjectFromAccountObjects(checkObject); + ////////////////////// // Destination wallet cashes the Check feeResult = xrplClient.fee(); @@ -188,6 +194,8 @@ public void createCheckAndSourceCancels() throws JsonRpcClientErrorException, Js .filter(findCheck(sourceKeyPair, destinationKeyPair, invoiceId)) .findFirst().get(); + assertEntryEqualsObjectFromAccountObjects(checkObject); + ////////////////////// // Source account cancels the Check feeResult = xrplClient.fee(); @@ -265,6 +273,8 @@ public void createCheckAndDestinationCancels() throws JsonRpcClientErrorExceptio .filter(findCheck(sourceKeyPair, destinationKeyPair, invoiceId)) .findFirst().get(); + assertEntryEqualsObjectFromAccountObjects(checkObject); + ////////////////////// // Destination account cancels the Check feeResult = xrplClient.fee(); @@ -304,4 +314,23 @@ private Predicate findCheck(KeyPair sourceKeyPair, KeyPair destina ((CheckObject) object).destination().equals(destinationKeyPair.publicKey().deriveAddress()); } + + private void assertEntryEqualsObjectFromAccountObjects(CheckObject checkObject) throws JsonRpcClientErrorException { + LedgerEntryResult checkEntry = xrplClient.ledgerEntry( + LedgerEntryRequestParams.check(checkObject.index(), LedgerSpecifier.CURRENT)); + + assertThat(checkEntry.node()).isEqualTo(checkObject); + + LedgerEntryResult entryByIndex = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(checkObject.index(), CheckObject.class, LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(checkEntry.node()); + + LedgerEntryResult entryByIndexUnTyped = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(checkObject.index(), LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(entryByIndexUnTyped.node()); + } } diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/DepositPreAuthIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/DepositPreAuthIT.java index 28c86519e..5b9c2d618 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/DepositPreAuthIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/DepositPreAuthIT.java @@ -33,10 +33,16 @@ import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; import org.xrpl.xrpl4j.model.client.fees.FeeResult; import org.xrpl.xrpl4j.model.client.fees.FeeUtils; +import org.xrpl.xrpl4j.model.client.ledger.DepositPreAuthLedgerEntryParams; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryRequestParams; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryResult; import org.xrpl.xrpl4j.model.client.path.DepositAuthorizedRequestParams; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.client.transactions.TransactionResult; +import org.xrpl.xrpl4j.model.ledger.CheckObject; import org.xrpl.xrpl4j.model.ledger.DepositPreAuthObject; +import org.xrpl.xrpl4j.model.ledger.EscrowObject; +import org.xrpl.xrpl4j.model.ledger.LedgerObject; import org.xrpl.xrpl4j.model.transactions.AccountSet; import org.xrpl.xrpl4j.model.transactions.DepositPreAuth; import org.xrpl.xrpl4j.model.transactions.Hash256; @@ -85,14 +91,20 @@ public void preauthorizeAccountAndReceivePayment() throws JsonRpcClientErrorExce ///////////////////////// // Validate that the DepositPreAuthObject was added to the receiver's account objects - this.scanForResult( + DepositPreAuthObject preAuthObject = (DepositPreAuthObject) this.scanForResult( () -> this.getValidatedAccountObjects(receiverKeyPair.publicKey().deriveAddress()), accountObjects -> accountObjects.accountObjects().stream().anyMatch(object -> DepositPreAuthObject.class.isAssignableFrom(object.getClass()) && ((DepositPreAuthObject) object).authorize().equals(senderKeyPair.publicKey().deriveAddress()) ) - ); + ).accountObjects().stream() + .filter(object -> DepositPreAuthObject.class.isAssignableFrom(object.getClass()) && + ((DepositPreAuthObject) object).authorize().equals(senderKeyPair.publicKey().deriveAddress())) + .findFirst() + .get(); + + assertEntryEqualsObjectFromAccountObjects(depositPreAuth, preAuthObject); ///////////////////////// // Validate that the `deposit_authorized` client call is implemented properly by ensuring it aligns with the @@ -266,4 +278,34 @@ private AccountInfoResult enableDepositPreauth( accountInfo -> accountInfo.accountData().flags().lsfDepositAuth() ); } + + private void assertEntryEqualsObjectFromAccountObjects( + DepositPreAuth depositPreAuth, + DepositPreAuthObject preAuthObject + ) + throws JsonRpcClientErrorException { + LedgerEntryResult preAuthEntry = xrplClient.ledgerEntry( + LedgerEntryRequestParams.depositPreAuth( + DepositPreAuthLedgerEntryParams.builder() + .owner(depositPreAuth.account()) + .authorized(depositPreAuth.authorize().get()) + .build(), + LedgerSpecifier.CURRENT + ) + ); + + assertThat(preAuthEntry.node()).isEqualTo(preAuthObject); + + LedgerEntryResult entryByIndex = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(preAuthObject.index(), DepositPreAuthObject.class, LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(preAuthEntry.node()); + + LedgerEntryResult entryByIndexUnTyped = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(preAuthObject.index(), LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(entryByIndexUnTyped.node()); + } } diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/EscrowIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/EscrowIT.java index fed3ba8a9..5d34919b7 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/EscrowIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/EscrowIT.java @@ -9,9 +9,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,16 +27,25 @@ import com.google.common.primitives.UnsignedLong; import com.ripple.cryptoconditions.PreimageSha256Fulfillment; import org.junit.jupiter.api.Test; +import org.testcontainers.shaded.com.github.dockerjava.core.dockerfile.DockerfileStatement.Add; import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; import org.xrpl.xrpl4j.crypto.keys.KeyPair; import org.xrpl.xrpl4j.crypto.signing.SingleSignedTransaction; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; +import org.xrpl.xrpl4j.model.client.accounts.AccountObjectsRequestParams; +import org.xrpl.xrpl4j.model.client.accounts.AccountObjectsRequestParams.AccountObjectType; +import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.ledger.EscrowLedgerEntryParams; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryRequestParams; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryResult; import org.xrpl.xrpl4j.model.client.ledger.LedgerResult; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.client.transactions.TransactionResult; import org.xrpl.xrpl4j.model.immutables.FluentCompareTo; import org.xrpl.xrpl4j.model.ledger.EscrowObject; +import org.xrpl.xrpl4j.model.ledger.LedgerObject; +import org.xrpl.xrpl4j.model.transactions.Address; import org.xrpl.xrpl4j.model.transactions.EscrowCancel; import org.xrpl.xrpl4j.model.transactions.EscrowCreate; import org.xrpl.xrpl4j.model.transactions.EscrowFinish; @@ -92,6 +101,11 @@ public void createAndFinishTimeBasedEscrow() throws JsonRpcClientErrorException, () -> this.getValidatedTransaction(createResult.transactionResult().hash(), EscrowCreate.class) ); + assertEntryEqualsObjectFromAccountObjects( + senderKeyPair.publicKey().deriveAddress(), + escrowCreate.sequence() + ); + ////////////////////// // Wait until the close time on the current validated ledger is after the finishAfter time on the Escrow this.scanForResult( @@ -188,7 +202,7 @@ public void createAndCancelTimeBasedEscrow() throws JsonRpcClientErrorException, ////////////////////// // Then wait until the transaction gets committed to a validated ledger - TransactionResult result = this.scanForResult( + final TransactionResult result = this.scanForResult( () -> this.getValidatedTransaction(createResult.transactionResult().hash(), EscrowCreate.class) ); @@ -201,6 +215,11 @@ public void createAndCancelTimeBasedEscrow() throws JsonRpcClientErrorException, ) ); + assertEntryEqualsObjectFromAccountObjects( + senderKeyPair.publicKey().deriveAddress(), + escrowCreate.sequence() + ); + ////////////////////// // Wait until the close time on the current validated ledger is after the cancelAfter time on the Escrow this.scanForResult( @@ -298,6 +317,11 @@ public void createAndFinishCryptoConditionBasedEscrow() throws JsonRpcClientErro () -> this.getValidatedTransaction(createResult.transactionResult().hash(), EscrowCreate.class) ); + assertEntryEqualsObjectFromAccountObjects( + senderKeyPair.publicKey().deriveAddress(), + escrowCreate.sequence() + ); + ////////////////////// // Wait until the close time on the current validated ledger is after the finishAfter time on the Escrow this.scanForResult( @@ -405,6 +429,11 @@ public void createAndCancelCryptoConditionBasedEscrow() throws JsonRpcClientErro () -> this.getValidatedTransaction(createResult.transactionResult().hash(), EscrowCreate.class) ); + assertEntryEqualsObjectFromAccountObjects( + senderKeyPair.publicKey().deriveAddress(), + escrowCreate.sequence() + ); + ////////////////////// // Wait until the close time on the current validated ledger is after the cancelAfter time on the Escrow this.scanForResult( @@ -475,4 +504,41 @@ private Instant getMinExpirationTime() { return closeTime.isBefore(now) ? now : closeTime; } + + private void assertEntryEqualsObjectFromAccountObjects( + Address escrowOwner, + UnsignedInteger createSequence + ) throws JsonRpcClientErrorException { + + EscrowObject escrowObject = (EscrowObject) xrplClient.accountObjects(AccountObjectsRequestParams.builder() + .type(AccountObjectType.ESCROW) + .account(escrowOwner) + .ledgerSpecifier(LedgerSpecifier.VALIDATED) + .build() + ).accountObjects().get(0); + + LedgerEntryResult escrowEntry = xrplClient.ledgerEntry( + LedgerEntryRequestParams.escrow( + EscrowLedgerEntryParams.builder() + .owner(escrowOwner) + .seq(createSequence) + .build(), + LedgerSpecifier.VALIDATED + ) + ); + + assertThat(escrowEntry.node()).isEqualTo(escrowObject); + + LedgerEntryResult entryByIndex = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(escrowObject.index(), EscrowObject.class, LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(escrowEntry.node()); + + LedgerEntryResult entryByIndexUnTyped = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(escrowObject.index(), LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(entryByIndexUnTyped.node()); + } } diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/IssuedCurrencyIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/IssuedCurrencyIT.java index 17f84fa61..670135a9a 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/IssuedCurrencyIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/IssuedCurrencyIT.java @@ -25,6 +25,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.google.common.base.Strings; import com.google.common.io.BaseEncoding; +import com.google.common.primitives.UnsignedInteger; import org.junit.jupiter.api.Test; import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; import org.xrpl.xrpl4j.crypto.keys.KeyPair; @@ -32,12 +33,21 @@ import org.xrpl.xrpl4j.model.client.accounts.AccountCurrenciesRequestParams; import org.xrpl.xrpl4j.model.client.accounts.AccountCurrenciesResult; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; +import org.xrpl.xrpl4j.model.client.accounts.AccountObjectsRequestParams; import org.xrpl.xrpl4j.model.client.accounts.TrustLine; import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; import org.xrpl.xrpl4j.model.client.fees.FeeResult; import org.xrpl.xrpl4j.model.client.fees.FeeUtils; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryRequestParams; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryResult; +import org.xrpl.xrpl4j.model.client.ledger.RippleStateLedgerEntryParams; +import org.xrpl.xrpl4j.model.client.ledger.RippleStateLedgerEntryParams.RippleStateAccounts; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; +import org.xrpl.xrpl4j.model.ledger.EscrowObject; +import org.xrpl.xrpl4j.model.ledger.LedgerObject; +import org.xrpl.xrpl4j.model.ledger.RippleStateObject; import org.xrpl.xrpl4j.model.transactions.AccountSet; +import org.xrpl.xrpl4j.model.transactions.Address; import org.xrpl.xrpl4j.model.transactions.IssuedCurrencyAmount; import org.xrpl.xrpl4j.model.transactions.PathStep; import org.xrpl.xrpl4j.model.transactions.Payment; @@ -77,6 +87,12 @@ void createTrustlineWithMaxLimit() throws JsonRpcClientErrorException, JsonProce FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); + assertThatEntryEqualsObjectFromAccountObjects( + trustLine, + issuerKeyPair.publicKey().deriveAddress(), + xrpl4jCoin + ); + assertThat(trustLine.limitPeer()).isEqualTo("9999999999999999e80"); } @@ -102,6 +118,12 @@ void createTrustlineWithMaxLimitMinusOneExponent() throws JsonRpcClientErrorExce FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); + assertThatEntryEqualsObjectFromAccountObjects( + trustLine, + issuerKeyPair.publicKey().deriveAddress(), + xrpl4jCoin + ); + assertThat(trustLine.limitPeer()).isEqualTo("9999999999999999e79"); } @@ -127,6 +149,12 @@ void createTrustlineWithSmallestPositiveLimit() throws JsonRpcClientErrorExcepti FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); + assertThatEntryEqualsObjectFromAccountObjects( + trustLine, + issuerKeyPair.publicKey().deriveAddress(), + xrpl4jCoin + ); + assertThat(trustLine.limitPeer()).isEqualTo("1000000000000000e-96"); } @@ -154,6 +182,12 @@ void createTrustlineWithSmalletPositiveLimitPlusOne() throws JsonRpcClientErrorE FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); + assertThatEntryEqualsObjectFromAccountObjects( + trustLine, + issuerKeyPair.publicKey().deriveAddress(), + xrpl4jCoin + ); + assertThat(trustLine.limitPeer()).isEqualTo("1100000000000000e-96"); } @@ -179,6 +213,12 @@ public void issueIssuedCurrencyBalance() throws JsonRpcClientErrorException, Jso FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); + assertThatEntryEqualsObjectFromAccountObjects( + trustLine, + issuerKeyPair.publicKey().deriveAddress(), + xrpl4jCoin + ); + /////////////////////////// // Send some xrpl4jCoin to the counterparty account. sendIssuedCurrency( @@ -189,9 +229,19 @@ public void issueIssuedCurrencyBalance() throws JsonRpcClientErrorException, Jso /////////////////////////// // Validate that the TrustLine balance was updated as a result of the Payment. // The trust line returned is from the perspective of the issuer, so the balance should be negative. - this.scanForResult(() -> getValidatedAccountLines(issuerKeyPair.publicKey().deriveAddress(), - counterpartyKeyPair.publicKey().deriveAddress()), + TrustLine trustLineAfterPayment = this.scanForResult( + () -> getValidatedAccountLines(issuerKeyPair.publicKey().deriveAddress(), + counterpartyKeyPair.publicKey().deriveAddress()), linesResult -> linesResult.lines().stream().anyMatch(line -> line.balance().equals("-" + trustLine.limitPeer())) + ).lines().stream() + .filter(line -> line.balance().equals("-" + trustLine.limitPeer())) + .findFirst() + .get(); + + assertThatEntryEqualsObjectFromAccountObjects( + trustLineAfterPayment, + issuerKeyPair.publicKey().deriveAddress(), + xrpl4jCoin ); /////////////////////////// @@ -559,4 +609,42 @@ public void setDefaultRipple(KeyPair issuerKeyPair, FeeResult feeResult) ); } + + private void assertThatEntryEqualsObjectFromAccountObjects( + TrustLine trustLine, + Address peerAddress, + String currency + ) throws JsonRpcClientErrorException { + RippleStateObject rippleStateObject = (RippleStateObject) xrplClient.accountObjects( + AccountObjectsRequestParams.of(trustLine.account()) + ).accountObjects().stream() + .filter(object -> RippleStateObject.class.isAssignableFrom(object.getClass()) /*&&*/ + /*((RippleStateObject) object).previousTransactionLedgerSequence().equals(lastSequence)*/) + .findFirst() + .get(); + + LedgerEntryResult entry = xrplClient.ledgerEntry( + LedgerEntryRequestParams.rippleState( + RippleStateLedgerEntryParams.builder() + .accounts(RippleStateAccounts.of(trustLine.account(), peerAddress)) + .currency(currency) + .build(), + LedgerSpecifier.VALIDATED + ) + ); + + assertThat(entry.node()).isEqualTo(rippleStateObject); + + LedgerEntryResult entryByIndex = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(rippleStateObject.index(), RippleStateObject.class, LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(entry.node()); + + LedgerEntryResult entryByIndexUnTyped = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(rippleStateObject.index(), LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(entryByIndexUnTyped.node()); + } } diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/NfTokenIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/NfTokenIT.java index cc0a143d2..5204f08f8 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/NfTokenIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/NfTokenIT.java @@ -26,6 +26,7 @@ import com.google.common.collect.Lists; import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedLong; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; import org.xrpl.xrpl4j.crypto.keys.KeyPair; @@ -34,9 +35,11 @@ import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.accounts.AccountNftsResult; import org.xrpl.xrpl4j.model.client.accounts.AccountObjectsRequestParams; -import org.xrpl.xrpl4j.model.client.accounts.AccountObjectsResult; import org.xrpl.xrpl4j.model.client.accounts.NfTokenObject; +import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; import org.xrpl.xrpl4j.model.client.fees.FeeUtils; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryRequestParams; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryResult; import org.xrpl.xrpl4j.model.client.nft.NftBuyOffersRequestParams; import org.xrpl.xrpl4j.model.client.nft.NftBuyOffersResult; import org.xrpl.xrpl4j.model.client.nft.NftSellOffersRequestParams; @@ -112,7 +115,7 @@ void mint() throws JsonRpcClientErrorException, JsonProcessingException { }, result -> result.accountNfts().stream() .anyMatch(nft -> nft.uri().get().equals(uri)) - ) + ) .accountNfts() .stream().filter(nft -> nft.uri().get().equals(uri)) .findFirst() @@ -121,22 +124,7 @@ void mint() throws JsonRpcClientErrorException, JsonProcessingException { assertThat(validatedMint.metadata().flatMap(TransactionMetadata::nfTokenId)) .isNotEmpty().get().isEqualTo(nfToken.nfTokenId()); - Optional maybeNfTokenPage = xrplClient.accountObjects( - AccountObjectsRequestParams.of(nfTokenMint.account()) - ).accountObjects().stream() - .filter(object -> NfTokenPageObject.class.isAssignableFrom(object.getClass())) - .map(object -> (NfTokenPageObject) object) - .findFirst(); - - assertThat(maybeNfTokenPage).isNotEmpty(); - assertThat(maybeNfTokenPage.get().nfTokens()).contains( - NfTokenWrapper.of( - NfToken.builder() - .nfTokenId(nfToken.nfTokenId()) - .uri(nfToken.uri()) - .build() - ) - ); + assertEntryEqualsObjectFromAccountObjects(keyPair.publicKey().deriveAddress(), nfToken); AccountInfoResult minterAccountInfo = xrplClient.accountInfo( AccountInfoRequestParams.of(keyPair.publicKey().deriveAddress()) @@ -198,18 +186,9 @@ void mintFromOtherMinterAccount() throws JsonRpcClientErrorException, JsonProces assertThat(mintSubmitResult.engineResult()).isEqualTo(TransactionResultCodes.TES_SUCCESS); assertThat(signedMint.hash()).isEqualTo(mintSubmitResult.transactionResult().hash()); - this.scanForResult( - () -> { - try { - return xrplClient.accountNfts(minterKeyPair.publicKey().deriveAddress()); - } catch (JsonRpcClientErrorException e) { - logger.error("Exception occurred while getting account nfts: {}", e.getMessage(), e); - throw new RuntimeException(e); - } - }, - result -> result.accountNfts().stream() - .anyMatch(nft -> nft.uri().get().equals(uri)) - ); + NfTokenObject nfToken = scanForNfToken(minterKeyPair, uri); + + assertEntryEqualsObjectFromAccountObjects(minterKeyPair.publicKey().deriveAddress(), nfToken); AccountInfoResult sourceAccountInfoAfterMint = xrplClient.accountInfo( AccountInfoRequestParams.of(keyPair.publicKey().deriveAddress()) @@ -247,19 +226,11 @@ void mintAndBurn() throws JsonRpcClientErrorException, JsonProcessingException { assertThat(mintSubmitResult.engineResult()).isEqualTo(TransactionResultCodes.TES_SUCCESS); assertThat(signedMint.hash()).isEqualTo(mintSubmitResult.transactionResult().hash()); - this.scanForResult( - () -> { - try { - return xrplClient.accountNfts(keyPair.publicKey().deriveAddress()); - } catch (JsonRpcClientErrorException e) { - throw new RuntimeException(e); - } - }, - result -> result.accountNfts().stream() - .anyMatch(nft -> nft.uri().get().equals(uri)) - ); + NfTokenObject nfToken = scanForNfToken(keyPair, uri); logger.info("NFT was minted successfully."); + assertEntryEqualsObjectFromAccountObjects(keyPair.publicKey().deriveAddress(), nfToken); + // nft burn AccountNftsResult accountNftsResult = xrplClient.accountNfts(keyPair.publicKey().deriveAddress()); @@ -335,19 +306,11 @@ void mintAndCreateOffer() throws JsonRpcClientErrorException, JsonProcessingExce mintSubmitResult.transactionResult().hash() ); - this.scanForResult( - () -> { - try { - return xrplClient.accountNfts(keyPair.publicKey().deriveAddress()); - } catch (JsonRpcClientErrorException e) { - throw new RuntimeException(e); - } - }, - result -> result.accountNfts().stream() - .anyMatch(nft -> nft.uri().get().equals(uri)) - ); + NfTokenObject nfToken = scanForNfToken(keyPair, uri); logger.info("NFT was minted successfully."); + assertEntryEqualsObjectFromAccountObjects(keyPair.publicKey().deriveAddress(), nfToken); + //create a sell offer for the NFT that was created above NfTokenId tokenId = xrplClient.accountNfts(keyPair.publicKey().deriveAddress()).accountNfts().get(0).nfTokenId(); @@ -394,6 +357,12 @@ void mintAndCreateOffer() throws JsonRpcClientErrorException, JsonProcessingExce .findFirst() .get(); + LedgerEntryResult entry = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(nfTokenOffer.index(), NfTokenOfferObject.class, LedgerSpecifier.VALIDATED) + ); + + assertThat(entry.node()).isEqualTo(nfTokenOffer); + assertThat(validatedOfferCreate.metadata().flatMap(TransactionMetadata::offerId)).isNotEmpty().get() .isEqualTo(nfTokenOffer.index()); logger.info("NFTokenOffer object was found in account's objects."); @@ -401,20 +370,20 @@ void mintAndCreateOffer() throws JsonRpcClientErrorException, JsonProcessingExce @Test void mintAndCreateThenAcceptOffer() throws JsonRpcClientErrorException, JsonProcessingException { - KeyPair wallet = createRandomAccountEd25519(); + KeyPair keypair = createRandomAccountEd25519(); //mint NFT from one account AccountInfoResult accountInfoResult = this.scanForResult( - () -> this.getValidatedAccountInfo(wallet.publicKey().deriveAddress()) + () -> this.getValidatedAccountInfo(keypair.publicKey().deriveAddress()) ); NfTokenUri uri = NfTokenUri.ofPlainText("ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf4dfuylqabf3oclgtqy55fbzdi"); //Nft mint transaction NfTokenMint nfTokenMint = NfTokenMint.builder() .tokenTaxon(UnsignedLong.ONE) - .account(wallet.publicKey().deriveAddress()) + .account(keypair.publicKey().deriveAddress()) .fee(XrpCurrencyAmount.ofDrops(50)) - .signingPublicKey(wallet.publicKey()) + .signingPublicKey(keypair.publicKey()) .sequence(accountInfoResult.accountData().sequence()) .flags(NfTokenMintFlags.builder() .tfTransferable(true) @@ -422,27 +391,19 @@ void mintAndCreateThenAcceptOffer() throws JsonRpcClientErrorException, JsonProc .uri(uri) .build(); - SingleSignedTransaction signedMint = signatureService.sign(wallet.privateKey(), nfTokenMint); + SingleSignedTransaction signedMint = signatureService.sign(keypair.privateKey(), nfTokenMint); SubmitResult mintSubmitResult = xrplClient.submit(signedMint); assertThat(mintSubmitResult.engineResult()).isEqualTo(TransactionResultCodes.TES_SUCCESS); assertThat(signedMint.hash()).isEqualTo(mintSubmitResult.transactionResult().hash()); - this.scanForResult( - () -> { - try { - return xrplClient.accountNfts(wallet.publicKey().deriveAddress()); - } catch (JsonRpcClientErrorException e) { - throw new RuntimeException(e); - } - }, - result -> result.accountNfts().stream() - .anyMatch(nft -> nft.uri().get().equals(uri)) - ); + NfTokenObject nfToken = scanForNfToken(keypair, uri); logger.info("NFT was minted successfully."); + assertEntryEqualsObjectFromAccountObjects(keypair.publicKey().deriveAddress(), nfToken); + //create a sell offer for the NFT that was created above - NfTokenId tokenId = xrplClient.accountNfts(wallet.publicKey().deriveAddress()).accountNfts().get(0).nfTokenId(); + NfTokenId tokenId = xrplClient.accountNfts(keypair.publicKey().deriveAddress()).accountNfts().get(0).nfTokenId(); // create buy offer from another account KeyPair wallet2 = createRandomAccountEd25519(); @@ -453,7 +414,7 @@ void mintAndCreateThenAcceptOffer() throws JsonRpcClientErrorException, JsonProc NfTokenCreateOffer nfTokenCreateOffer = NfTokenCreateOffer.builder() .account(wallet2.publicKey().deriveAddress()) - .owner(wallet.publicKey().deriveAddress()) + .owner(keypair.publicKey().deriveAddress()) .nfTokenId(tokenId) .fee(XrpCurrencyAmount.ofDrops(50)) .sequence(accountInfoResult2.accountData().sequence()) @@ -478,14 +439,26 @@ void mintAndCreateThenAcceptOffer() throws JsonRpcClientErrorException, JsonProc ); logger.info("NFT Create Offer (Buy) transaction was validated successfully."); - this.scanForResult( + NfTokenOfferObject nfTokenOffer = (NfTokenOfferObject) this.scanForResult( () -> this.getValidatedAccountObjects(wallet2.publicKey().deriveAddress()), objectsResult -> objectsResult.accountObjects().stream() .anyMatch(object -> NfTokenOfferObject.class.isAssignableFrom(object.getClass()) && ((NfTokenOfferObject) object).owner().equals(wallet2.publicKey().deriveAddress()) ) + ).accountObjects() + .stream() + .filter(object -> NfTokenOfferObject.class.isAssignableFrom(object.getClass()) && + ((NfTokenOfferObject) object).owner().equals(wallet2.publicKey().deriveAddress())) + .findFirst() + .get(); + + LedgerEntryResult entry = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(nfTokenOffer.index(), NfTokenOfferObject.class, LedgerSpecifier.VALIDATED) ); + + assertThat(entry.node()).isEqualTo(nfTokenOffer); + logger.info("NFTokenOffer object was found in account's objects."); NftBuyOffersResult nftBuyOffersResult = xrplClient.nftBuyOffers(NftBuyOffersRequestParams.builder() @@ -495,15 +468,15 @@ void mintAndCreateThenAcceptOffer() throws JsonRpcClientErrorException, JsonProc // accept offer from a different account NfTokenAcceptOffer nfTokenAcceptOffer = NfTokenAcceptOffer.builder() - .account(wallet.publicKey().deriveAddress()) + .account(keypair.publicKey().deriveAddress()) .buyOffer(nftBuyOffersResult.offers().get(0).nftOfferIndex()) .fee(XrpCurrencyAmount.ofDrops(50)) .sequence(accountInfoResult.accountData().sequence().plus(UnsignedInteger.ONE)) - .signingPublicKey(wallet.publicKey()) + .signingPublicKey(keypair.publicKey()) .build(); SingleSignedTransaction signedAccept = signatureService.sign( - wallet.privateKey(), + keypair.privateKey(), nfTokenAcceptOffer ); SubmitResult nfTokenAcceptOfferSubmitResult = xrplClient.submit(signedAccept); @@ -518,7 +491,7 @@ void mintAndCreateThenAcceptOffer() throws JsonRpcClientErrorException, JsonProc ); logger.info("NFT Accept Offer transaction was validated successfully."); - assertThat(xrplClient.accountNfts(wallet.publicKey().deriveAddress()).accountNfts().size()).isEqualTo(0); + assertThat(xrplClient.accountNfts(keypair.publicKey().deriveAddress()).accountNfts().size()).isEqualTo(0); assertThat(xrplClient.accountNfts(wallet2.publicKey().deriveAddress()).accountNfts().size()).isEqualTo(1); logger.info("The NFT ownership was transferred."); @@ -526,58 +499,49 @@ void mintAndCreateThenAcceptOffer() throws JsonRpcClientErrorException, JsonProc @Test void mintAndCreateOfferThenCancelOffer() throws JsonRpcClientErrorException, JsonProcessingException { - KeyPair wallet = createRandomAccountEd25519(); + KeyPair keypair = createRandomAccountEd25519(); //mint NFT from one account AccountInfoResult accountInfoResult = this.scanForResult( - () -> this.getValidatedAccountInfo(wallet.publicKey().deriveAddress()) + () -> this.getValidatedAccountInfo(keypair.publicKey().deriveAddress()) ); NfTokenUri uri = NfTokenUri.ofPlainText("ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf4dfuylqabf3oclgtqy55fbzdi"); //Nft mint transaction NfTokenMint nfTokenMint = NfTokenMint.builder() .tokenTaxon(UnsignedLong.ONE) - .account(wallet.publicKey().deriveAddress()) + .account(keypair.publicKey().deriveAddress()) .fee(XrpCurrencyAmount.ofDrops(50)) - .signingPublicKey(wallet.publicKey()) + .signingPublicKey(keypair.publicKey()) .sequence(accountInfoResult.accountData().sequence()) .uri(uri) .build(); - SingleSignedTransaction signedMint = signatureService.sign(wallet.privateKey(), nfTokenMint); + SingleSignedTransaction signedMint = signatureService.sign(keypair.privateKey(), nfTokenMint); SubmitResult mintSubmitResult = xrplClient.submit(signedMint); assertThat(mintSubmitResult.engineResult()).isEqualTo(TransactionResultCodes.TES_SUCCESS); assertThat(signedMint.hash()).isEqualTo(mintSubmitResult.transactionResult().hash()); - this.scanForResult( - () -> { - try { - return xrplClient.accountNfts(wallet.publicKey().deriveAddress()); - } catch (JsonRpcClientErrorException e) { - throw new RuntimeException(e); - } - }, - result -> result.accountNfts().stream() - .anyMatch(nft -> nft.uri().get().equals(uri)) - ); + scanForNfToken(keypair, uri); + logger.info("NFT was minted successfully."); //create a sell offer for the NFT that was created above - NfTokenId tokenId = xrplClient.accountNfts(wallet.publicKey().deriveAddress()).accountNfts().get(0).nfTokenId(); + NfTokenId tokenId = xrplClient.accountNfts(keypair.publicKey().deriveAddress()).accountNfts().get(0).nfTokenId(); NfTokenCreateOffer nfTokenCreateOffer = NfTokenCreateOffer.builder() - .account(wallet.publicKey().deriveAddress()) + .account(keypair.publicKey().deriveAddress()) .nfTokenId(tokenId) .fee(XrpCurrencyAmount.ofDrops(50)) .sequence(accountInfoResult.accountData().sequence().plus(UnsignedInteger.ONE)) .amount(XrpCurrencyAmount.ofDrops(1000)) .flags(NfTokenCreateOfferFlags.SELL_NFTOKEN) - .signingPublicKey(wallet.publicKey()) + .signingPublicKey(keypair.publicKey()) .build(); SingleSignedTransaction signedOffer = signatureService.sign( - wallet.privateKey(), + keypair.privateKey(), nfTokenCreateOffer ); SubmitResult nfTokenCreateOfferSubmitResult = xrplClient.submit(signedOffer); @@ -594,11 +558,11 @@ void mintAndCreateOfferThenCancelOffer() throws JsonRpcClientErrorException, Jso logger.info("NFT Create Offer (Sell) transaction was validated successfully."); this.scanForResult( - () -> this.getValidatedAccountObjects(wallet.publicKey().deriveAddress()), + () -> this.getValidatedAccountObjects(keypair.publicKey().deriveAddress()), objectsResult -> objectsResult.accountObjects().stream() .anyMatch(object -> NfTokenOfferObject.class.isAssignableFrom(object.getClass()) && - ((NfTokenOfferObject) object).owner().equals(wallet.publicKey().deriveAddress()) + ((NfTokenOfferObject) object).owner().equals(keypair.publicKey().deriveAddress()) ) ); logger.info("NFTokenOffer object was found in account's objects."); @@ -610,14 +574,14 @@ void mintAndCreateOfferThenCancelOffer() throws JsonRpcClientErrorException, Jso // cancel the created offer NfTokenCancelOffer nfTokenCancelOffer = NfTokenCancelOffer.builder() .addTokenOffers(nftSellOffersResult.offers().get(0).nftOfferIndex()) - .account(wallet.publicKey().deriveAddress()) + .account(keypair.publicKey().deriveAddress()) .fee(XrpCurrencyAmount.ofDrops(50)) .sequence(accountInfoResult.accountData().sequence().plus(UnsignedInteger.valueOf(2))) - .signingPublicKey(wallet.publicKey()) + .signingPublicKey(keypair.publicKey()) .build(); SingleSignedTransaction signedCancel = signatureService.sign( - wallet.privateKey(), + keypair.privateKey(), nfTokenCancelOffer ); SubmitResult nfTokenCancelOfferSubmitResult = xrplClient.submit(signedCancel); @@ -637,7 +601,7 @@ void mintAndCreateOfferThenCancelOffer() throws JsonRpcClientErrorException, Jso .isEqualTo(Lists.newArrayList(tokenId)); this.scanForResult( - () -> this.getValidatedAccountObjects(wallet.publicKey().deriveAddress()), + () -> this.getValidatedAccountObjects(keypair.publicKey().deriveAddress()), objectsResult -> objectsResult.accountObjects().stream() .noneMatch(object -> NfTokenOfferObject.class.isAssignableFrom(object.getClass()) @@ -648,20 +612,20 @@ void mintAndCreateOfferThenCancelOffer() throws JsonRpcClientErrorException, Jso @Test void acceptOfferDirectModeWithBrokerFee() throws JsonRpcClientErrorException, JsonProcessingException { - KeyPair wallet = createRandomAccountEd25519(); + KeyPair keypair = createRandomAccountEd25519(); //mint NFT from one account AccountInfoResult accountInfoResult = this.scanForResult( - () -> this.getValidatedAccountInfo(wallet.publicKey().deriveAddress()) + () -> this.getValidatedAccountInfo(keypair.publicKey().deriveAddress()) ); NfTokenUri uri = NfTokenUri.ofPlainText("ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf4dfuylqabf3oclgtqy55fbzdi"); //Nft mint transaction NfTokenMint nfTokenMint = NfTokenMint.builder() .tokenTaxon(UnsignedLong.ONE) - .account(wallet.publicKey().deriveAddress()) + .account(keypair.publicKey().deriveAddress()) .fee(XrpCurrencyAmount.ofDrops(50)) - .signingPublicKey(wallet.publicKey()) + .signingPublicKey(keypair.publicKey()) .sequence(accountInfoResult.accountData().sequence()) .flags(NfTokenMintFlags.builder() .tfTransferable(true) @@ -669,41 +633,32 @@ void acceptOfferDirectModeWithBrokerFee() throws JsonRpcClientErrorException, Js .uri(uri) .build(); - SingleSignedTransaction signedMint = signatureService.sign(wallet.privateKey(), nfTokenMint); + SingleSignedTransaction signedMint = signatureService.sign(keypair.privateKey(), nfTokenMint); SubmitResult mintSubmitResult = xrplClient.submit(signedMint); assertThat(mintSubmitResult.engineResult()).isEqualTo(TransactionResultCodes.TES_SUCCESS); assertThat(signedMint.hash()).isEqualTo(mintSubmitResult.transactionResult().hash()); - this.scanForResult( - () -> { - try { - return xrplClient.accountNfts(wallet.publicKey().deriveAddress()); - } catch (JsonRpcClientErrorException e) { - throw new RuntimeException(e); - } - }, - result -> result.accountNfts().stream() - .anyMatch(nft -> nft.uri().get().equals(uri)) - ); + scanForNfToken(keypair, uri); + logger.info("NFT was minted successfully."); //create a sell offer for the NFT that was created above - NfTokenId tokenId = xrplClient.accountNfts(wallet.publicKey().deriveAddress()).accountNfts().get(0).nfTokenId(); + NfTokenId tokenId = xrplClient.accountNfts(keypair.publicKey().deriveAddress()).accountNfts().get(0).nfTokenId(); // the owner creates the sell offer NfTokenCreateOffer nfTokenCreateSellOffer = NfTokenCreateOffer.builder() - .account(wallet.publicKey().deriveAddress()) + .account(keypair.publicKey().deriveAddress()) .nfTokenId(tokenId) .fee(XrpCurrencyAmount.ofDrops(50)) .sequence(accountInfoResult.accountData().sequence().plus(UnsignedInteger.ONE)) .amount(XrpCurrencyAmount.ofDrops(1000)) - .signingPublicKey(wallet.publicKey()) + .signingPublicKey(keypair.publicKey()) .flags(NfTokenCreateOfferFlags.SELL_NFTOKEN) .build(); SingleSignedTransaction signedOffer = signatureService.sign( - wallet.privateKey(), + keypair.privateKey(), nfTokenCreateSellOffer ); SubmitResult nfTokenCreateSellOfferSubmitResult = xrplClient.submit(signedOffer); @@ -720,11 +675,11 @@ void acceptOfferDirectModeWithBrokerFee() throws JsonRpcClientErrorException, Js logger.info("NFT Create Offer (Sell) transaction was validated successfully."); this.scanForResult( - () -> this.getValidatedAccountObjects(wallet.publicKey().deriveAddress()), + () -> this.getValidatedAccountObjects(keypair.publicKey().deriveAddress()), objectsResult -> objectsResult.accountObjects().stream() .anyMatch(object -> NfTokenOfferObject.class.isAssignableFrom(object.getClass()) && - ((NfTokenOfferObject) object).owner().equals(wallet.publicKey().deriveAddress()) + ((NfTokenOfferObject) object).owner().equals(keypair.publicKey().deriveAddress()) ) ); logger.info("NFTokenOffer object was found in account's objects."); @@ -735,24 +690,24 @@ void acceptOfferDirectModeWithBrokerFee() throws JsonRpcClientErrorException, Js assertThat(nftSellOffersResult.offers().size()).isEqualTo(1); // create buy offer from another account - KeyPair wallet2 = createRandomAccountEd25519(); + KeyPair keypair2 = createRandomAccountEd25519(); AccountInfoResult accountInfoResult2 = this.scanForResult( - () -> this.getValidatedAccountInfo(wallet2.publicKey().deriveAddress()) + () -> this.getValidatedAccountInfo(keypair2.publicKey().deriveAddress()) ); NfTokenCreateOffer nfTokenCreateOffer = NfTokenCreateOffer.builder() - .account(wallet2.publicKey().deriveAddress()) - .owner(wallet.publicKey().deriveAddress()) + .account(keypair2.publicKey().deriveAddress()) + .owner(keypair.publicKey().deriveAddress()) .nfTokenId(tokenId) .fee(XrpCurrencyAmount.ofDrops(50)) .sequence(accountInfoResult2.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(1000)) - .signingPublicKey(wallet2.publicKey()) + .signingPublicKey(keypair2.publicKey()) .build(); SingleSignedTransaction signedOffer2 = signatureService.sign( - wallet2.privateKey(), + keypair2.privateKey(), nfTokenCreateOffer ); SubmitResult nfTokenCreateOfferSubmitResult = xrplClient.submit(signedOffer2); @@ -769,11 +724,11 @@ void acceptOfferDirectModeWithBrokerFee() throws JsonRpcClientErrorException, Js logger.info("NFT Create Offer (Buy) transaction was validated successfully."); this.scanForResult( - () -> this.getValidatedAccountObjects(wallet2.publicKey().deriveAddress()), + () -> this.getValidatedAccountObjects(keypair2.publicKey().deriveAddress()), objectsResult -> objectsResult.accountObjects().stream() .anyMatch(object -> NfTokenOfferObject.class.isAssignableFrom(object.getClass()) && - ((NfTokenOfferObject) object).owner().equals(wallet2.publicKey().deriveAddress()) + ((NfTokenOfferObject) object).owner().equals(keypair2.publicKey().deriveAddress()) ) ); logger.info("NFTokenOffer object was found in account's objects."); @@ -814,17 +769,64 @@ void acceptOfferDirectModeWithBrokerFee() throws JsonRpcClientErrorException, Js ) ); - this.scanForResult( + scanForNfToken(keypair2, uri); + logger.info("NFT was transferred successfully."); + } + + private void assertEntryEqualsObjectFromAccountObjects(Address owner, NfTokenObject nfToken) + throws JsonRpcClientErrorException { + Optional maybeNfTokenPage = xrplClient.accountObjects( + AccountObjectsRequestParams.of(owner) + ).accountObjects().stream() + .filter(object -> NfTokenPageObject.class.isAssignableFrom(object.getClass())) + .map(object -> (NfTokenPageObject) object) + .findFirst(); + + assertThat(maybeNfTokenPage).isNotEmpty(); + NfTokenPageObject nfTokenPageObject = maybeNfTokenPage.get(); + assertThat(nfTokenPageObject.nfTokens()).contains( + NfTokenWrapper.of( + NfToken.builder() + .nfTokenId(nfToken.nfTokenId()) + .uri(nfToken.uri()) + .build() + ) + ); + + LedgerEntryResult entry = xrplClient.ledgerEntry( + LedgerEntryRequestParams.nftPage(nfTokenPageObject.index(), LedgerSpecifier.CURRENT) + ); + + assertThat(entry.node()).isEqualTo(nfTokenPageObject); + + LedgerEntryResult entryByIndex = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(nfTokenPageObject.index(), NfTokenPageObject.class, LedgerSpecifier.CURRENT) + ); + + assertThat(entryByIndex.node()).isEqualTo(entry.node()); + + LedgerEntryResult entryByIndexUnTyped = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(nfTokenPageObject.index(), LedgerSpecifier.CURRENT) + ); + + assertThat(entryByIndex.node()).isEqualTo(entryByIndexUnTyped.node()); + } + + private NfTokenObject scanForNfToken(KeyPair minterKeyPair, NfTokenUri uri) { + return this.scanForResult( () -> { try { - return xrplClient.accountNfts(wallet2.publicKey().deriveAddress()); + return xrplClient.accountNfts(minterKeyPair.publicKey().deriveAddress()); } catch (JsonRpcClientErrorException e) { + logger.error("Exception occurred while getting account nfts: {}", e.getMessage(), e); throw new RuntimeException(e); } }, result -> result.accountNfts().stream() .anyMatch(nft -> nft.uri().get().equals(uri)) - ); - logger.info("NFT was transferred successfully."); + ).accountNfts() + .stream().filter(nft -> nft.uri().get().equals(uri)) + .findFirst() + .get(); } } diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/OfferIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/OfferIT.java index 6b54508a8..7cc13c93c 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/OfferIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/OfferIT.java @@ -36,12 +36,17 @@ import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; import org.xrpl.xrpl4j.model.client.fees.FeeResult; import org.xrpl.xrpl4j.model.client.fees.FeeUtils; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryRequestParams; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryResult; +import org.xrpl.xrpl4j.model.client.ledger.OfferLedgerEntryParams; import org.xrpl.xrpl4j.model.client.path.BookOffersRequestParams; import org.xrpl.xrpl4j.model.client.path.BookOffersResult; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.flags.OfferCreateFlags; import org.xrpl.xrpl4j.model.flags.OfferFlags; +import org.xrpl.xrpl4j.model.ledger.EscrowObject; import org.xrpl.xrpl4j.model.ledger.Issue; +import org.xrpl.xrpl4j.model.ledger.LedgerObject; import org.xrpl.xrpl4j.model.ledger.OfferObject; import org.xrpl.xrpl4j.model.ledger.RippleStateObject; import org.xrpl.xrpl4j.model.transactions.Address; @@ -197,45 +202,9 @@ public void createOpenOfferAndCancel() throws JsonRpcClientErrorException, JsonP assertThat(offerObject.takerGets()).isEqualTo(offerCreate.takerGets()); assertThat(offerObject.takerPays()).isEqualTo(offerCreate.takerPays()); - cancelOffer(purchaser, offerObject.sequence(), "tesSUCCESS"); - } - - /** - * Cancels an offer and verifies the offer no longer exists on ledger for the account. - * - * @param purchaser The {@link KeyPair} of the buyer. - * @param offerSequence The sequence of the offer. - * - * @throws JsonRpcClientErrorException If anything goes wrong while communicating with rippled. - */ - private void cancelOffer( - KeyPair purchaser, - UnsignedInteger offerSequence, - String expectedResult - ) throws JsonRpcClientErrorException, JsonProcessingException { - AccountInfoResult infoResult = this.scanForResult( - () -> this.getValidatedAccountInfo(purchaser.publicKey().deriveAddress())); - UnsignedInteger nextSequence = infoResult.accountData().sequence(); - - FeeResult feeResult = xrplClient.fee(); - - OfferCancel offerCancel = OfferCancel.builder() - .account(purchaser.publicKey().deriveAddress()) - .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) - .sequence(nextSequence) - .offerSequence(offerSequence) - .signingPublicKey(purchaser.publicKey()) - .build(); - - SingleSignedTransaction signedOfferCancel = signatureService.sign( - purchaser.privateKey(), offerCancel - ); - SubmitResult cancelResponse = xrplClient.submit(signedOfferCancel); - assertThat(cancelResponse.engineResult()).isEqualTo(expectedResult); + assertThatEntryEqualsObjectFromAccountObjects(offerObject); - assertEmptyResults(() -> this.getValidatedAccountObjects(purchaser.publicKey().deriveAddress(), OfferObject.class)); - assertEmptyResults( - () -> this.getValidatedAccountObjects(purchaser.publicKey().deriveAddress(), RippleStateObject.class)); + cancelOffer(purchaser, offerObject.sequence(), "tesSUCCESS"); } @Test @@ -289,17 +258,6 @@ public void createUnmatchedKillOrFill() throws JsonRpcClientErrorException, Json () -> this.getValidatedAccountObjects(purchaser.publicKey().deriveAddress(), RippleStateObject.class)); } - /** - * Asserts the supplier returns empty results, waiting up to 10 seconds for that condition to be true. - * - * @param supplier results supplier. - */ - private void assertEmptyResults(Supplier> supplier) { - Awaitility.await() - .atMost(Durations.TEN_SECONDS) - .until(supplier::get, Matchers.empty()); - } - @Test public void createFullyMatchedOffer() throws JsonRpcClientErrorException, JsonProcessingException { // GIVEN a buy offer that has a really great exchange rate @@ -404,4 +362,77 @@ public RippleStateObject scanForIssuedCurrency(KeyPair purchaser, String currenc .orElse(null)); } + + private void assertThatEntryEqualsObjectFromAccountObjects(OfferObject offerObject) + throws JsonRpcClientErrorException { + LedgerEntryResult entry = xrplClient.ledgerEntry( + LedgerEntryRequestParams.offer( + OfferLedgerEntryParams.builder() + .account(offerObject.account()) + .seq(offerObject.sequence()) + .build(), + LedgerSpecifier.VALIDATED + ) + ); + LedgerEntryResult entryByIndex = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(offerObject.index(), OfferObject.class, LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(entry.node()); + + LedgerEntryResult entryByIndexUnTyped = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(offerObject.index(), LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(entryByIndexUnTyped.node()); + } + + /** + * Asserts the supplier returns empty results, waiting up to 10 seconds for that condition to be true. + * + * @param supplier results supplier. + */ + private void assertEmptyResults(Supplier> supplier) { + Awaitility.await() + .atMost(Durations.TEN_SECONDS) + .until(supplier::get, Matchers.empty()); + } + + /** + * Cancels an offer and verifies the offer no longer exists on ledger for the account. + * + * @param purchaser The {@link KeyPair} of the buyer. + * @param offerSequence The sequence of the offer. + * + * @throws JsonRpcClientErrorException If anything goes wrong while communicating with rippled. + */ + private void cancelOffer( + KeyPair purchaser, + UnsignedInteger offerSequence, + String expectedResult + ) throws JsonRpcClientErrorException, JsonProcessingException { + AccountInfoResult infoResult = this.scanForResult( + () -> this.getValidatedAccountInfo(purchaser.publicKey().deriveAddress())); + UnsignedInteger nextSequence = infoResult.accountData().sequence(); + + FeeResult feeResult = xrplClient.fee(); + + OfferCancel offerCancel = OfferCancel.builder() + .account(purchaser.publicKey().deriveAddress()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) + .sequence(nextSequence) + .offerSequence(offerSequence) + .signingPublicKey(purchaser.publicKey()) + .build(); + + SingleSignedTransaction signedOfferCancel = signatureService.sign( + purchaser.privateKey(), offerCancel + ); + SubmitResult cancelResponse = xrplClient.submit(signedOfferCancel); + assertThat(cancelResponse.engineResult()).isEqualTo(expectedResult); + + assertEmptyResults(() -> this.getValidatedAccountObjects(purchaser.publicKey().deriveAddress(), OfferObject.class)); + assertEmptyResults( + () -> this.getValidatedAccountObjects(purchaser.publicKey().deriveAddress(), RippleStateObject.class)); + } } diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/PaymentChannelIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/PaymentChannelIT.java index 3e3fb0994..6ddfc914a 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/PaymentChannelIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/PaymentChannelIT.java @@ -25,6 +25,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedLong; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; import org.xrpl.xrpl4j.crypto.keys.KeyPair; @@ -40,7 +41,12 @@ import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; import org.xrpl.xrpl4j.model.client.fees.FeeResult; import org.xrpl.xrpl4j.model.client.fees.FeeUtils; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryRequestParams; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryResult; +import org.xrpl.xrpl4j.model.client.ledger.OfferLedgerEntryParams; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; +import org.xrpl.xrpl4j.model.ledger.LedgerObject; +import org.xrpl.xrpl4j.model.ledger.OfferObject; import org.xrpl.xrpl4j.model.ledger.PayChannelObject; import org.xrpl.xrpl4j.model.transactions.PaymentChannelClaim; import org.xrpl.xrpl4j.model.transactions.PaymentChannelCreate; @@ -114,14 +120,8 @@ public void createPaymentChannel() throws JsonRpcClientErrorException, JsonProce ////////////////////////// // Also validate that the channel exists in the account's objects - scanForResult( - () -> getValidatedAccountObjects(sourceKeyPair.publicKey().deriveAddress()), - objectsResult -> objectsResult.accountObjects().stream() - .anyMatch(object -> - PayChannelObject.class.isAssignableFrom(object.getClass()) && - ((PayChannelObject) object).destination().equals(destinationKeyPair.publicKey().deriveAddress()) - ) - ); + PayChannelObject payChannelObject = scanForPayChannelObject(sourceKeyPair, destinationKeyPair); + assertThatEntryEqualsObjectFromAccountObjects(payChannelObject); ////////////////////////// // Validate that the amount of the payment channel was deducted from the source @@ -194,6 +194,9 @@ void createAndClaimPaymentChannel() throws JsonRpcClientErrorException, JsonProc assertThat(paymentChannel.publicKeyHex()).isNotEmpty().get().isEqualTo(paymentChannelCreate.publicKey()); assertThat(paymentChannel.cancelAfter()).isNotEmpty().get().isEqualTo(paymentChannelCreate.cancelAfter().get()); + PayChannelObject payChannelObject = scanForPayChannelObject(sourceKeyPair, destinationKeyPair); + assertThatEntryEqualsObjectFromAccountObjects(payChannelObject); + AccountInfoResult destinationAccountInfo = scanForResult( () -> getValidatedAccountInfo(destinationKeyPair.publicKey().deriveAddress()) ); @@ -253,6 +256,9 @@ void createAndClaimPaymentChannel() throws JsonRpcClientErrorException, JsonProc .plus(paymentChannelClaim.balance().get()) ) ); + + PayChannelObject payChannelObjectAfterClaim = scanForPayChannelObject(sourceKeyPair, destinationKeyPair); + assertThatEntryEqualsObjectFromAccountObjects(payChannelObjectAfterClaim); } @Test @@ -312,6 +318,9 @@ void createAddFundsAndSetExpirationToPaymentChannel() throws JsonRpcClientErrorE assertThat(paymentChannel.publicKeyHex()).isNotEmpty().get().isEqualTo(paymentChannelCreate.publicKey()); assertThat(paymentChannel.cancelAfter()).isNotEmpty().get().isEqualTo(paymentChannelCreate.cancelAfter().get()); + PayChannelObject payChannelObject = scanForPayChannelObject(sourceKeyPair, destinationKeyPair); + assertThatEntryEqualsObjectFromAccountObjects(payChannelObject); + PaymentChannelFund paymentChannelFund = PaymentChannelFund.builder() .account(sourceKeyPair.publicKey().deriveAddress()) .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) @@ -345,6 +354,9 @@ void createAddFundsAndSetExpirationToPaymentChannel() throws JsonRpcClientErrorE ) ); + PayChannelObject payChannelObjectAfterFund = scanForPayChannelObject(sourceKeyPair, destinationKeyPair); + assertThatEntryEqualsObjectFromAccountObjects(payChannelObjectAfterFund); + ////////////////////////// // Then set a new expiry on the channel by submitting a PaymentChannelFund // transaction with an expiration and 1 drop of XRP in the amount field @@ -386,6 +398,9 @@ void createAddFundsAndSetExpirationToPaymentChannel() throws JsonRpcClientErrorE channel.expiration().get().equals(newExpiry) ) ); + + PayChannelObject payChannelObjectAfterExpiryBump = scanForPayChannelObject(sourceKeyPair, destinationKeyPair); + assertThatEntryEqualsObjectFromAccountObjects(payChannelObjectAfterExpiryBump); } @Test @@ -449,4 +464,41 @@ void testCurrentAccountChannels() throws JsonRpcClientErrorException, JsonProces assertThat(accountChannelsResult.ledgerIndex()).isEmpty(); assertThat(accountChannelsResult.ledgerCurrentIndex()).isNotEmpty(); } + + private PayChannelObject scanForPayChannelObject(KeyPair sourceKeyPair, KeyPair destinationKeyPair) { + return (PayChannelObject) scanForResult( + () -> getValidatedAccountObjects(sourceKeyPair.publicKey().deriveAddress()), + objectsResult -> objectsResult.accountObjects().stream() + .anyMatch(object -> + PayChannelObject.class.isAssignableFrom(object.getClass()) && + ((PayChannelObject) object).destination().equals(destinationKeyPair.publicKey().deriveAddress()) + ) + ).accountObjects().stream() + .filter(object -> PayChannelObject.class.isAssignableFrom(object.getClass()) && + ((PayChannelObject) object).destination().equals(destinationKeyPair.publicKey().deriveAddress())) + .findFirst() + .get(); + } + + private void assertThatEntryEqualsObjectFromAccountObjects(PayChannelObject payChannelObject) + throws JsonRpcClientErrorException { + LedgerEntryResult entry = xrplClient.ledgerEntry( + LedgerEntryRequestParams.paymentChannel( + payChannelObject.index(), + LedgerSpecifier.VALIDATED + ) + ); + + LedgerEntryResult entryByIndex = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(payChannelObject.index(), PayChannelObject.class, LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(entry.node()); + + LedgerEntryResult entryByIndexUnTyped = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(payChannelObject.index(), LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(entryByIndexUnTyped.node()); + } } diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SetRegularKeyIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SetRegularKeyIT.java index 218335ff9..d96f1140e 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SetRegularKeyIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SetRegularKeyIT.java @@ -30,9 +30,14 @@ import org.xrpl.xrpl4j.crypto.keys.Seed; import org.xrpl.xrpl4j.crypto.signing.SingleSignedTransaction; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; +import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; import org.xrpl.xrpl4j.model.client.fees.FeeResult; import org.xrpl.xrpl4j.model.client.fees.FeeUtils; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryRequestParams; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryResult; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; +import org.xrpl.xrpl4j.model.ledger.AccountRootObject; +import org.xrpl.xrpl4j.model.ledger.LedgerObject; import org.xrpl.xrpl4j.model.transactions.AccountSet; import org.xrpl.xrpl4j.model.transactions.SetRegularKey; @@ -50,6 +55,7 @@ void setRegularKeyOnAccount() throws JsonRpcClientErrorException, JsonProcessing ////////////////////////// // Wait for the account to show up on ledger AccountInfoResult accountInfo = scanForResult(() -> getValidatedAccountInfo(wallet.publicKey().deriveAddress())); + assertEntryEqualsAccountInfo(wallet, accountInfo); ////////////////////////// // Generate a new wallet locally @@ -100,6 +106,10 @@ void setRegularKeyOnAccount() throws JsonRpcClientErrorException, JsonProcessing } ); + AccountInfoResult accountInfoAfterRegKeySet = scanForResult( + () -> getValidatedAccountInfo(wallet.publicKey().deriveAddress()) + ); + assertEntryEqualsAccountInfo(wallet, accountInfoAfterRegKeySet); } @Test @@ -111,6 +121,7 @@ void removeRegularKeyFromAccount() throws JsonRpcClientErrorException, JsonProce ////////////////////////// // Wait for the account to show up on ledger AccountInfoResult accountInfo = scanForResult(() -> getValidatedAccountInfo(wallet.publicKey().deriveAddress())); + assertEntryEqualsAccountInfo(wallet, accountInfo); ////////////////////////// // Generate a new wallet locally @@ -162,6 +173,11 @@ void removeRegularKeyFromAccount() throws JsonRpcClientErrorException, JsonProce } ); + AccountInfoResult accountInfoAfterSet = scanForResult( + () -> getValidatedAccountInfo(wallet.publicKey().deriveAddress()) + ); + assertEntryEqualsAccountInfo(wallet, accountInfoAfterSet); + SetRegularKey removeRegularKey = SetRegularKey.builder() .account(wallet.publicKey().deriveAddress()) .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) @@ -178,9 +194,34 @@ void removeRegularKeyFromAccount() throws JsonRpcClientErrorException, JsonProce removeResult.transactionResult().hash() ); - scanForResult( + AccountInfoResult accountInfoAfterRemoving = scanForResult( () -> getValidatedAccountInfo(wallet.publicKey().deriveAddress()), infoResult -> !infoResult.accountData().regularKey().isPresent() ); + + assertEntryEqualsAccountInfo(wallet, accountInfoAfterRemoving); + } + + private void assertEntryEqualsAccountInfo( + KeyPair keyPair, + AccountInfoResult accountInfo + ) throws JsonRpcClientErrorException { + LedgerEntryResult accountRoot = xrplClient.ledgerEntry( + LedgerEntryRequestParams.accountRoot(keyPair.publicKey().deriveAddress(), LedgerSpecifier.VALIDATED) + ); + + assertThat(accountInfo.accountData()).isEqualTo(accountRoot.node()); + + LedgerEntryResult entryByIndex = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(accountRoot.index(), AccountRootObject.class, LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(accountRoot.node()); + + LedgerEntryResult entryByIndexUnTyped = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(accountRoot.index(), LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(entryByIndexUnTyped.node()); } } diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SignerListSetIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SignerListSetIT.java index e7dd423e3..36b7d7bea 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SignerListSetIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SignerListSetIT.java @@ -31,16 +31,26 @@ import org.xrpl.xrpl4j.crypto.signing.MultiSignedTransaction; import org.xrpl.xrpl4j.crypto.signing.SingleSignedTransaction; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; +import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; import org.xrpl.xrpl4j.model.client.fees.FeeResult; import org.xrpl.xrpl4j.model.client.fees.FeeUtils; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryRequestParams; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryResult; import org.xrpl.xrpl4j.model.client.transactions.SubmitMultiSignedResult; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; +import org.xrpl.xrpl4j.model.client.transactions.TransactionResult; +import org.xrpl.xrpl4j.model.ledger.AccountRootObject; +import org.xrpl.xrpl4j.model.ledger.LedgerObject; import org.xrpl.xrpl4j.model.ledger.SignerEntry; import org.xrpl.xrpl4j.model.ledger.SignerEntryWrapper; +import org.xrpl.xrpl4j.model.ledger.SignerListObject; +import org.xrpl.xrpl4j.model.transactions.Hash256; import org.xrpl.xrpl4j.model.transactions.Payment; import org.xrpl.xrpl4j.model.transactions.Signer; import org.xrpl.xrpl4j.model.transactions.SignerListSet; import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount; +import org.xrpl.xrpl4j.model.transactions.metadata.AffectedNode; +import org.xrpl.xrpl4j.model.transactions.metadata.MetaLedgerEntryType; import java.util.Collections; import java.util.Comparator; @@ -118,6 +128,8 @@ void addSignersToSignerListAndSendPayment() throws JsonRpcClientErrorException, infoResult -> infoResult.accountData().signerLists().size() == 1 ); + assertSignerListEntryEqualsAccountInfo(signedSignerListSet.hash(), sourceAccountInfoAfterSignerListSet); + assertThat( sourceAccountInfoAfterSignerListSet.accountData().signerLists().get(0) .signerEntries().stream() @@ -226,6 +238,8 @@ void addSignersToSignerListThenDeleteSignerList() throws JsonRpcClientErrorExcep infoResult -> infoResult.accountData().signerLists().size() == 1 ); + assertSignerListEntryEqualsAccountInfo(signedSignerListSet.hash(), sourceAccountInfoAfterSignerListSet); + assertThat( sourceAccountInfoAfterSignerListSet.accountData().signerLists().get(0) .signerEntries().stream() @@ -263,4 +277,30 @@ void addSignersToSignerListThenDeleteSignerList() throws JsonRpcClientErrorExcep ); } + + private void assertSignerListEntryEqualsAccountInfo(Hash256 signerListSetTx, AccountInfoResult accountInfo) + throws JsonRpcClientErrorException { + + TransactionResult signerListSet = this.getValidatedTransaction(signerListSetTx, + SignerListSet.class); + Hash256 signerListId = signerListSet.metadata().get() + .affectedNodes() + .stream() + .filter(affectedNode -> affectedNode.ledgerEntryType().equals(MetaLedgerEntryType.SIGNER_LIST)) + .findFirst() + .map(AffectedNode::ledgerIndex) + .get(); + + LedgerEntryResult entryByIndex = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(signerListId, SignerListObject.class, LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(accountInfo.accountData().signerLists().get(0)); + + LedgerEntryResult entryByIndexUnTyped = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(signerListId, LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(entryByIndexUnTyped.node()); + } } diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/TicketIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/TicketIT.java index 15fa9e1c8..d4531f336 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/TicketIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/TicketIT.java @@ -29,12 +29,20 @@ import org.xrpl.xrpl4j.crypto.keys.KeyPair; import org.xrpl.xrpl4j.crypto.signing.SingleSignedTransaction; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; +import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; import org.xrpl.xrpl4j.model.client.fees.FeeResult; import org.xrpl.xrpl4j.model.client.fees.FeeUtils; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryRequestParams; +import org.xrpl.xrpl4j.model.client.ledger.LedgerEntryResult; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; +import org.xrpl.xrpl4j.model.ledger.LedgerObject; +import org.xrpl.xrpl4j.model.ledger.SignerListObject; import org.xrpl.xrpl4j.model.ledger.TicketObject; import org.xrpl.xrpl4j.model.transactions.AccountSet; +import org.xrpl.xrpl4j.model.transactions.Hash256; import org.xrpl.xrpl4j.model.transactions.TicketCreate; +import org.xrpl.xrpl4j.model.transactions.metadata.AffectedNode; +import org.xrpl.xrpl4j.model.transactions.metadata.MetaLedgerEntryType; import java.util.List; @@ -71,11 +79,17 @@ void createTicketAndUseSequenceNumber() throws JsonRpcClientErrorException, Json submitResult.transactionResult().hash() ); - this.scanForResult( + Hash256 ticketId = this.scanForResult( () -> this.getValidatedTransaction( submitResult.transactionResult().hash(), TicketCreate.class) - ); + ).metadata().get() + .affectedNodes() + .stream() + .filter(affectedNode -> affectedNode.ledgerEntryType().equals(MetaLedgerEntryType.TICKET)) + .findFirst() + .map(AffectedNode::ledgerIndex) + .get(); AccountInfoResult accountInfoAfterTicketCreate = getValidatedAccountInfo(sourceKeyPair.publicKey().deriveAddress()); assertThat(accountInfoAfterTicketCreate.accountData().ticketCount()).isNotEmpty().get() @@ -87,6 +101,18 @@ void createTicketAndUseSequenceNumber() throws JsonRpcClientErrorException, Json ); assertThat(tickets).asList().hasSize(1); + LedgerEntryResult entryByIndex = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(ticketId, TicketObject.class, LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(tickets.get(0)); + + LedgerEntryResult entryByIndexUnTyped = xrplClient.ledgerEntry( + LedgerEntryRequestParams.index(ticketId, LedgerSpecifier.VALIDATED) + ); + + assertThat(entryByIndex.node()).isEqualTo(entryByIndexUnTyped.node()); + AccountSet accountSet = AccountSet.builder() .account(sourceKeyPair.publicKey().deriveAddress()) .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee())