uri();
+
+ /**
+ * 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")
+ Optional ownerNode();
+
+ /**
+ * The identifying hash of the transaction that most recently modified this object.
+ *
+ * @return A {@link Hash256} containing the previous transaction hash.
+ */
+ @JsonProperty("PreviousTxnID")
+ Optional previousTransactionId();
+
+ /**
+ * The index of the ledger that contains the transaction that most recently modified this object.
+ *
+ * @return An {@link UnsignedInteger} representing the previous transaction ledger sequence.
+ */
+ @JsonProperty("PreviousTxnLgrSeq")
+ Optional previousTransactionLedgerSequence();
+}
diff --git a/xrpl4j-core/src/main/resources/definitions.json b/xrpl4j-core/src/main/resources/definitions.json
index b8fd9a8a1..797be9ce2 100644
--- a/xrpl4j-core/src/main/resources/definitions.json
+++ b/xrpl4j-core/src/main/resources/definitions.json
@@ -23,6 +23,7 @@
"UInt512": 23,
"Issue": 24,
"XChainBridge": 25,
+ "Currency": 26,
"Transaction": 10001,
"LedgerEntry": 10002,
"Validation": 10003,
@@ -51,6 +52,7 @@
"NFTokenOffer": 55,
"AMM": 121,
"DID": 73,
+ "Oracle": 128,
"Any": -3,
"Child": -2,
"Nickname": 110,
@@ -208,6 +210,16 @@
"type": "UInt8"
}
],
+ [
+ "Scale",
+ {
+ "nth": 4,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "UInt8"
+ }
+ ],
[
"TickSize",
{
@@ -498,6 +510,16 @@
"type": "UInt32"
}
],
+ [
+ "LastUpdateTime",
+ {
+ "nth": 15,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "UInt32"
+ }
+ ],
[
"HighQualityIn",
{
@@ -828,6 +850,16 @@
"type": "UInt32"
}
],
+ [
+ "OracleDocumentID",
+ {
+ "nth": 51,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "UInt32"
+ }
+ ],
[
"IndexNext",
{
@@ -1028,6 +1060,16 @@
"type": "UInt64"
}
],
+ [
+ "AssetPrice",
+ {
+ "nth": 23,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "UInt64"
+ }
+ ],
[
"EmailHash",
{
@@ -1918,6 +1960,26 @@
"type": "Blob"
}
],
+ [
+ "AssetClass",
+ {
+ "nth": 28,
+ "isVLEncoded": true,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "Blob"
+ }
+ ],
+ [
+ "Provider",
+ {
+ "nth": 29,
+ "isVLEncoded": true,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "Blob"
+ }
+ ],
[
"Account",
{
@@ -2128,6 +2190,26 @@
"type": "PathSet"
}
],
+ [
+ "BaseAsset",
+ {
+ "nth": 1,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "Currency"
+ }
+ ],
+ [
+ "QuoteAsset",
+ {
+ "nth": 2,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "Currency"
+ }
+ ],
[
"LockingChainIssue",
{
@@ -2458,6 +2540,16 @@
"type": "STObject"
}
],
+ [
+ "PriceData",
+ {
+ "nth": 32,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "STObject"
+ }
+ ],
[
"Signers",
{
@@ -2628,6 +2720,16 @@
"type": "STArray"
}
],
+ [
+ "PriceDataSeries",
+ {
+ "nth": 24,
+ "isVLEncoded": false,
+ "isSerialized": true,
+ "isSigningField": true,
+ "type": "STArray"
+ }
+ ],
[
"AuthAccounts",
{
@@ -2656,6 +2758,7 @@
"telWRONG_NETWORK": -386,
"telREQUIRES_NETWORK_ID": -385,
"telNETWORK_ID_MAKES_TX_NON_CANONICAL": -384,
+ "telENV_RPC_FAILED": -383,
"temMALFORMED": -299,
"temBAD_AMOUNT": -298,
@@ -2703,6 +2806,8 @@
"temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT": -256,
"temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT": -255,
"temEMPTY_DID": -254,
+ "temARRAY_EMPTY": -253,
+ "temARRAY_TOO_LARGE": -252,
"tefFAILURE": -199,
"tefALREADY": -198,
@@ -2739,7 +2844,6 @@
"terQUEUED": -89,
"terPRE_TICKET": -88,
"terNO_AMM": -87,
- "terSUBMITTED": -86,
"tesSUCCESS": 0,
@@ -2815,7 +2919,11 @@
"tecXCHAIN_SELF_COMMIT": 184,
"tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR": 185,
"tecXCHAIN_CREATE_ACCOUNT_DISABLED": 186,
- "tecEMPTY_DID": 187
+ "tecEMPTY_DID": 187,
+ "tecINVALID_UPDATE_TIME": 188,
+ "tecTOKEN_PAIR_NOT_FOUND": 189,
+ "tecARRAY_EMPTY": 190,
+ "tecARRAY_TOO_LARGE": 191
},
"TRANSACTION_TYPES": {
"Invalid": -1,
@@ -2864,6 +2972,8 @@
"XChainCreateBridge": 48,
"DIDSet": 49,
"DIDDelete": 50,
+ "OracleSet": 51,
+ "OracleDelete": 52,
"EnableAmendment": 100,
"SetFee": 101,
"UNLModify": 102
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/BinarySerializationTests.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/BinarySerializationTests.java
index d22654441..524737872 100644
--- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/BinarySerializationTests.java
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/codec/binary/BinarySerializationTests.java
@@ -52,6 +52,7 @@
import org.xrpl.xrpl4j.model.transactions.AccountDelete;
import org.xrpl.xrpl4j.model.transactions.AccountSet;
import org.xrpl.xrpl4j.model.transactions.Address;
+import org.xrpl.xrpl4j.model.transactions.AssetPrice;
import org.xrpl.xrpl4j.model.transactions.CheckCancel;
import org.xrpl.xrpl4j.model.transactions.CheckCash;
import org.xrpl.xrpl4j.model.transactions.CheckCreate;
@@ -68,6 +69,7 @@
import org.xrpl.xrpl4j.model.transactions.Hash256;
import org.xrpl.xrpl4j.model.transactions.ImmutableDidDelete;
import org.xrpl.xrpl4j.model.transactions.ImmutableDidSet;
+import org.xrpl.xrpl4j.model.transactions.ImmutableOracleDelete;
import org.xrpl.xrpl4j.model.transactions.ImmutableXChainAccountCreateCommit;
import org.xrpl.xrpl4j.model.transactions.ImmutableXChainAddClaimAttestation;
import org.xrpl.xrpl4j.model.transactions.ImmutableXChainClaim;
@@ -78,10 +80,16 @@
import org.xrpl.xrpl4j.model.transactions.NetworkId;
import org.xrpl.xrpl4j.model.transactions.OfferCancel;
import org.xrpl.xrpl4j.model.transactions.OfferCreate;
+import org.xrpl.xrpl4j.model.transactions.OracleDelete;
+import org.xrpl.xrpl4j.model.transactions.OracleDocumentId;
+import org.xrpl.xrpl4j.model.transactions.OracleProvider;
+import org.xrpl.xrpl4j.model.transactions.OracleSet;
import org.xrpl.xrpl4j.model.transactions.Payment;
import org.xrpl.xrpl4j.model.transactions.PaymentChannelClaim;
import org.xrpl.xrpl4j.model.transactions.PaymentChannelCreate;
import org.xrpl.xrpl4j.model.transactions.PaymentChannelFund;
+import org.xrpl.xrpl4j.model.transactions.PriceData;
+import org.xrpl.xrpl4j.model.transactions.PriceDataWrapper;
import org.xrpl.xrpl4j.model.transactions.SetRegularKey;
import org.xrpl.xrpl4j.model.transactions.SignerListSet;
import org.xrpl.xrpl4j.model.transactions.Transaction;
@@ -2120,6 +2128,98 @@ void serializeDidDelete() throws JsonProcessingException {
assertSerializesAndDeserializes(transaction, binary);
}
+ @Test
+ void serializeOracleSet() throws JsonProcessingException {
+ OracleSet oracleSet = OracleSet.builder()
+ .account(Address.of("rMS69A6J39RmBg5yWDft5XAM8zTGbtMMZy"))
+ .assetClass("63757272656E6379")
+ .fee(XrpCurrencyAmount.ofDrops(10))
+ .oracleDocumentId(OracleDocumentId.of(UnsignedInteger.valueOf(3)))
+ .provider(OracleProvider.of("68747470733A2F2F74687265657872702E646576"))
+ .sequence(UnsignedInteger.valueOf(2019238))
+ .signingPublicKey(PublicKey.fromBase16EncodedPublicKey(
+ "EDA6501D3E53D47F10AE37A0C6B34194CF8A205DE9611FE81B63E7B62105E90EAC"
+ ))
+ .lastUpdateTime(UnsignedInteger.valueOf(1715785124))
+ .addPriceDataSeries(
+ PriceDataWrapper.of(
+ PriceData.builder()
+ .assetPrice(AssetPrice.of(UnsignedLong.valueOf("1ff4", 16)))
+ .baseAsset("XRP")
+ .quoteAsset("IDR")
+ .build()
+ ),
+ PriceDataWrapper.of(
+ PriceData.builder()
+ .assetPrice(AssetPrice.of(UnsignedLong.valueOf("13652", 16)))
+ .baseAsset("XRP")
+ .quoteAsset("JPY")
+ .scale(UnsignedInteger.valueOf(3))
+ .build()
+ ),
+ PriceDataWrapper.of(
+ PriceData.builder()
+ .assetPrice(AssetPrice.of(UnsignedLong.valueOf("117bf", 16)))
+ .baseAsset("XRP")
+ .quoteAsset("KRW")
+ .scale(UnsignedInteger.valueOf(2))
+ .build()
+ ),
+ PriceDataWrapper.of(
+ PriceData.builder()
+ .assetPrice(AssetPrice.of(UnsignedLong.valueOf("14d6a", 16)))
+ .baseAsset("XRP")
+ .quoteAsset("MXN")
+ .scale(UnsignedInteger.valueOf(4))
+ .build()
+ ),
+ PriceDataWrapper.of(
+ PriceData.builder()
+ .assetPrice(AssetPrice.of(UnsignedLong.valueOf("5dfe", 16)))
+ .baseAsset("XRP")
+ .quoteAsset("MYR")
+ .scale(UnsignedInteger.valueOf(4))
+ .build()
+ )
+ )
+ .transactionSignature(
+ Signature.fromBase16("07205F518BC5D1D4F67E33101F140F69279670627825CDFA4BECDFE7C1D38" +
+ "B40B4101F62264C280C3C9A492B3FF01DCF4AE0CEF12BB6FB2E4EB681B9B81D710A")
+ )
+ .build();
+
+ String expectedBinary = "12003324001ECFA62F6644CDA420330000000368400000000000000A7321EDA6501D3E53D47" +
+ "F10AE37A0C6B34194CF8A205DE9611FE81B63E7B62105E90EAC744007205F518BC5D1D4F67E33101F140F692796706278" +
+ "25CDFA4BECDFE7C1D38B40B4101F62264C280C3C9A492B3FF01DCF4AE0CEF12BB6FB2E4EB681B9B81D710A701C0863757" +
+ "272656E6379701D1468747470733A2F2F74687265657872702E6465768114E03E6F0A3D378C02E4CB7769053F8AA4EB3" +
+ "649ECF018E02030170000000000001FF4011A0000000000000000000000000000000000000000021A000000000000000" +
+ "0000000004944520000000000E1E02030170000000000013652041003011A00000000000000000000000000000000000" +
+ "00000021A0000000000000000000000004A50590000000000E1E020301700000000000117BF041002011A00000000000" +
+ "00000000000000000000000000000021A0000000000000000000000004B52570000000000E1E02030170000000000014" +
+ "D6A041004011A0000000000000000000000000000000000000000021A0000000000000000000000004D584E000000000" +
+ "0E1E02030170000000000005DFE041004011A0000000000000000000000000000000000000000021A000000000000000" +
+ "0000000004D59520000000000E1F1";
+ assertSerializesAndDeserializes(oracleSet, expectedBinary);
+ }
+
+ @Test
+ void serializeOracleDelete() throws JsonProcessingException {
+ OracleDelete oracleDelete = OracleDelete.builder()
+ .account(Address.of("rsTkARdP8evWbRcAoD1ZJX7bDtAf5BVspg"))
+ .fee(XrpCurrencyAmount.ofDrops(15))
+ .sequence(UnsignedInteger.valueOf(10))
+ .lastLedgerSequence(UnsignedInteger.valueOf(17))
+ .signingPublicKey(PublicKey.fromBase16EncodedPublicKey(
+ "ED6884529D21E8059F689827F5BF2AD5CBC138A3427A04C12B004C9BE9BBB22A77"
+ ))
+ .oracleDocumentId(OracleDocumentId.of(UnsignedInteger.ONE))
+ .build();
+ String expectedBinary = "120034240000000A201B0000001120330000000168400000000000000F7321ED6884529" +
+ "D21E8059F689827F5BF2AD5CBC138A3427A04C12B004C9BE9BBB22A7781141AFFBE9553C7325305491B100D02E6BED8CE6BA5";
+
+ assertSerializesAndDeserializes(oracleDelete, expectedBinary);
+ }
+
private void assertSerializesAndDeserializes(
T transaction,
String expectedBinary
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/crypto/signing/SignatureUtilsTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/crypto/signing/SignatureUtilsTest.java
index e23dd9dcd..592b0f5d2 100644
--- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/crypto/signing/SignatureUtilsTest.java
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/crypto/signing/SignatureUtilsTest.java
@@ -93,6 +93,9 @@
import org.xrpl.xrpl4j.model.transactions.NfTokenMint;
import org.xrpl.xrpl4j.model.transactions.OfferCancel;
import org.xrpl.xrpl4j.model.transactions.OfferCreate;
+import org.xrpl.xrpl4j.model.transactions.OracleDelete;
+import org.xrpl.xrpl4j.model.transactions.OracleDocumentId;
+import org.xrpl.xrpl4j.model.transactions.OracleSet;
import org.xrpl.xrpl4j.model.transactions.Payment;
import org.xrpl.xrpl4j.model.transactions.PaymentChannelClaim;
import org.xrpl.xrpl4j.model.transactions.PaymentChannelCreate;
@@ -1109,6 +1112,33 @@ void addSignatureToDidDelete() {
addSignatureToTransactionHelper(transaction);
}
+ @Test
+ void addSignatureToOracleSet() {
+ OracleSet transaction = OracleSet.builder()
+ .account(sourcePublicKey.deriveAddress())
+ .fee(XrpCurrencyAmount.ofDrops(10))
+ .sequence(UnsignedInteger.valueOf(391))
+ .signingPublicKey(sourcePublicKey)
+ .oracleDocumentId(OracleDocumentId.of(UnsignedInteger.ONE))
+ .lastUpdateTime(UnsignedInteger.ONE)
+ .build();
+
+ addSignatureToTransactionHelper(transaction);
+ }
+
+ @Test
+ void addSignatureToOracleDelete() {
+ OracleDelete transaction = OracleDelete.builder()
+ .account(sourcePublicKey.deriveAddress())
+ .fee(XrpCurrencyAmount.ofDrops(10))
+ .sequence(UnsignedInteger.valueOf(391))
+ .signingPublicKey(sourcePublicKey)
+ .oracleDocumentId(OracleDocumentId.of(UnsignedInteger.ONE))
+ .build();
+
+ addSignatureToTransactionHelper(transaction);
+ }
+
@Test
public void addSignatureToTransactionUnsupported() {
assertThrows(IllegalArgumentException.class, () -> addSignatureToTransactionHelper(transactionMock));
@@ -1724,6 +1754,31 @@ void addMultiSignatureToDidDelete() {
addMultiSignatureToTransactionHelper(transaction);
}
+ @Test
+ void addMultiSignatureToOracleSet() {
+ OracleSet transaction = OracleSet.builder()
+ .account(sourcePublicKey.deriveAddress())
+ .fee(XrpCurrencyAmount.ofDrops(10))
+ .sequence(UnsignedInteger.valueOf(391))
+ .oracleDocumentId(OracleDocumentId.of(UnsignedInteger.ONE))
+ .lastUpdateTime(UnsignedInteger.ONE)
+ .build();
+
+ addMultiSignatureToTransactionHelper(transaction);
+ }
+
+ @Test
+ void addMultiSignatureToOracleDelete() {
+ OracleDelete transaction = OracleDelete.builder()
+ .account(sourcePublicKey.deriveAddress())
+ .fee(XrpCurrencyAmount.ofDrops(10))
+ .sequence(UnsignedInteger.valueOf(391))
+ .oracleDocumentId(OracleDocumentId.of(UnsignedInteger.ONE))
+ .build();
+
+ addMultiSignatureToTransactionHelper(transaction);
+ }
+
@Test
public void addMultiSignaturesToTransactionUnsupported() {
when(transactionMock.transactionSignature()).thenReturn(Optional.empty());
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
index 62c272658..2b7cef20c 100644
--- 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
@@ -25,11 +25,13 @@
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.OracleObject;
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.ImmutableXChainBridge;
+import org.xrpl.xrpl4j.model.transactions.OracleDocumentId;
import org.xrpl.xrpl4j.model.transactions.XChainBridge;
class LedgerEntryRequestParamsTest extends AbstractJsonTest {
@@ -54,6 +56,7 @@ void testTypedIndexParams() throws JSONException, JsonProcessingException {
assertThat(params.bridgeAccount()).isEmpty();
assertThat(params.bridge()).isEmpty();
assertThat(params.did()).isEmpty();
+ assertThat(params.oracle()).isEmpty();
String json = String.format("{\n" +
" \"index\": \"%s\",\n" +
@@ -90,6 +93,7 @@ void testUntypedIndexParams() throws JSONException, JsonProcessingException {
assertThat(params.did()).isEmpty();
assertThat(params.bridgeAccount()).isEmpty();
assertThat(params.bridge()).isEmpty();
+ assertThat(params.oracle()).isEmpty();
String json = String.format("{\n" +
" \"index\": \"%s\",\n" +
@@ -121,6 +125,7 @@ void testAccountRootParams() throws JSONException, JsonProcessingException {
assertThat(params.did()).isEmpty();
assertThat(params.bridgeAccount()).isEmpty();
assertThat(params.bridge()).isEmpty();
+ assertThat(params.oracle()).isEmpty();
String json = String.format("{\n" +
" \"account_root\": \"%s\",\n" +
@@ -160,6 +165,7 @@ void testAmmParams() throws JSONException, JsonProcessingException {
assertThat(params.did()).isEmpty();
assertThat(params.bridgeAccount()).isEmpty();
assertThat(params.bridge()).isEmpty();
+ assertThat(params.oracle()).isEmpty();
String json = "{\n" +
" \"amm\": {\n" +
@@ -204,6 +210,7 @@ void testOfferParams() throws JSONException, JsonProcessingException {
assertThat(params.did()).isEmpty();
assertThat(params.bridgeAccount()).isEmpty();
assertThat(params.bridge()).isEmpty();
+ assertThat(params.oracle()).isEmpty();
String json = "{\n" +
" \"offer\": {\n" +
@@ -246,6 +253,7 @@ void testRippleStateParams() throws JSONException, JsonProcessingException {
assertThat(params.did()).isEmpty();
assertThat(params.bridgeAccount()).isEmpty();
assertThat(params.bridge()).isEmpty();
+ assertThat(params.oracle()).isEmpty();
String json = "{\n" +
" \"ripple_state\": {\n" +
@@ -281,6 +289,7 @@ void testCheckParams() throws JSONException, JsonProcessingException {
assertThat(params.did()).isEmpty();
assertThat(params.bridgeAccount()).isEmpty();
assertThat(params.bridge()).isEmpty();
+ assertThat(params.oracle()).isEmpty();
String json = String.format("{\n" +
" \"check\": \"%s\",\n" +
@@ -316,6 +325,7 @@ void testEscrowParams() throws JSONException, JsonProcessingException {
assertThat(params.did()).isEmpty();
assertThat(params.bridgeAccount()).isEmpty();
assertThat(params.bridge()).isEmpty();
+ assertThat(params.oracle()).isEmpty();
String json = "{\n" +
" \"escrow\": {\n" +
@@ -350,6 +360,7 @@ void testPaymentChannelParams() throws JSONException, JsonProcessingException {
assertThat(params.did()).isEmpty();
assertThat(params.bridgeAccount()).isEmpty();
assertThat(params.bridge()).isEmpty();
+ assertThat(params.oracle()).isEmpty();
String json = String.format("{\n" +
" \"payment_channel\": \"%s\",\n" +
@@ -386,6 +397,7 @@ void testDepositPreAuthParams() throws JSONException, JsonProcessingException {
assertThat(params.did()).isEmpty();
assertThat(params.bridgeAccount()).isEmpty();
assertThat(params.bridge()).isEmpty();
+ assertThat(params.oracle()).isEmpty();
String json = "{\n" +
" \"deposit_preauth\": {\n" +
@@ -425,6 +437,7 @@ void testTicketParams() throws JSONException, JsonProcessingException {
assertThat(params.did()).isEmpty();
assertThat(params.bridgeAccount()).isEmpty();
assertThat(params.bridge()).isEmpty();
+ assertThat(params.oracle()).isEmpty();
String json = "{\n" +
" \"ticket\": {\n" +
@@ -460,6 +473,7 @@ void testNftPageParams() throws JSONException, JsonProcessingException {
assertThat(params.did()).isEmpty();
assertThat(params.bridgeAccount()).isEmpty();
assertThat(params.bridge()).isEmpty();
+ assertThat(params.oracle()).isEmpty();
String json = String.format("{\n" +
" \"nft_page\": \"%s\",\n" +
@@ -492,6 +506,7 @@ void testDidParams() throws JSONException, JsonProcessingException {
assertThat(params.nftPage()).isEmpty();
assertThat(params.bridgeAccount()).isEmpty();
assertThat(params.bridge()).isEmpty();
+ assertThat(params.oracle()).isEmpty();
String json = String.format("{\n" +
" \"did\": \"%s\",\n" +
@@ -531,6 +546,7 @@ void testBridgeParams() throws JSONException, JsonProcessingException {
assertThat(params.ticket()).isEmpty();
assertThat(params.nftPage()).isEmpty();
assertThat(params.did()).isEmpty();
+ assertThat(params.oracle()).isEmpty();
String json = String.format("{\n" +
" \"bridge_account\": \"%s\",\n" +
@@ -541,4 +557,40 @@ void testBridgeParams() throws JSONException, JsonProcessingException {
assertCanSerializeAndDeserialize(params, json);
}
+
+ @Test
+ void testOracleParams() throws JSONException, JsonProcessingException {
+ OracleLedgerEntryParams oracleParams = OracleLedgerEntryParams.builder()
+ .account(ED_ADDRESS)
+ .oracleDocumentId(OracleDocumentId.of(UnsignedInteger.ONE))
+ .build();
+ LedgerEntryRequestParams params = LedgerEntryRequestParams.oracle(
+ oracleParams,
+ LedgerSpecifier.VALIDATED
+ );
+ assertThat(params.oracle()).isNotEmpty().get().isEqualTo(oracleParams);
+ assertThat(params.ledgerObjectClass()).isEqualTo(OracleObject.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();
+ assertThat(params.nftPage()).isEmpty();
+ assertThat(params.did()).isEmpty();
+ assertThat(params.bridgeAccount()).isEmpty();
+
+ String json = String.format("{\n" +
+ " \"oracle\" : %s,\n" +
+ " \"binary\": false,\n" +
+ " \"ledger_index\": \"validated\"\n" +
+ " }", objectMapper.writeValueAsString(oracleParams), ED_ADDRESS);
+
+ assertCanSerializeAndDeserialize(params, json);
+ }
}
\ No newline at end of file
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/oracle/AggregatePriceSetTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/oracle/AggregatePriceSetTest.java
new file mode 100644
index 000000000..9f007697d
--- /dev/null
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/oracle/AggregatePriceSetTest.java
@@ -0,0 +1,73 @@
+package org.xrpl.xrpl4j.model.client.oracle;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.google.common.primitives.UnsignedLong;
+import org.assertj.core.api.Assertions;
+import org.immutables.value.Value;
+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.jackson.ObjectMapperFactory;
+
+import java.math.BigDecimal;
+
+/**
+ * Unit tests for {@link AggregatePriceSet}.
+ */
+class AggregatePriceSetTest {
+
+ private final ObjectMapper objectMapper = ObjectMapperFactory.create();
+
+ @Test
+ void testBigDecimalFields() {
+ AggregatePriceSet set = AggregatePriceSet.builder()
+ .meanString("1234.5678")
+ .size(UnsignedLong.ONE)
+ .standardDeviationString("345678.23496")
+ .build();
+
+ assertThat(set.mean()).isEqualTo(BigDecimal.valueOf(1234.5678));
+ assertThat(set.standardDeviation()).isEqualTo(BigDecimal.valueOf(345678.23496));
+ }
+
+ @Test
+ void testJson() throws JSONException, JsonProcessingException {
+ AggregatePriceSet priceSet =
+ AggregatePriceSet.builder()
+ .meanString("200")
+ .size(UnsignedLong.ONE)
+ .standardDeviationString("1.00")
+ .build();
+ AggregatePriceSetWrapper wrapper = AggregatePriceSetWrapper.of(priceSet);
+
+ String json = "{\"value\":{\"mean\":\"200\",\"size\":1,\"standard_deviation\":\"1.00\"}}";
+ assertSerializesAndDeserializes(wrapper, json);
+ }
+
+ private void assertSerializesAndDeserializes(AggregatePriceSetWrapper wrapper, String json)
+ throws JsonProcessingException, JSONException {
+ String serialized = objectMapper.writeValueAsString(wrapper);
+ JSONAssert.assertEquals(json, serialized, JSONCompareMode.STRICT);
+ AggregatePriceSetWrapper deserialized = objectMapper.readValue(serialized, AggregatePriceSetWrapper.class);
+ Assertions.assertThat(deserialized).isEqualTo(wrapper);
+ }
+
+ @Value.Immutable
+ @JsonSerialize(as = ImmutableAggregatePriceSetWrapper.class)
+ @JsonDeserialize(as = ImmutableAggregatePriceSetWrapper.class)
+ interface AggregatePriceSetWrapper {
+
+ static AggregatePriceSetWrapper of(AggregatePriceSet aggregatePriceSet) {
+ return ImmutableAggregatePriceSetWrapper.builder().value(aggregatePriceSet).build();
+ }
+
+ AggregatePriceSet value();
+
+ }
+}
\ No newline at end of file
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/oracle/GetAggregatePriceRequestParamsTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/oracle/GetAggregatePriceRequestParamsTest.java
new file mode 100644
index 000000000..3e6cc2683
--- /dev/null
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/oracle/GetAggregatePriceRequestParamsTest.java
@@ -0,0 +1,45 @@
+package org.xrpl.xrpl4j.model.client.oracle;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.google.common.primitives.UnsignedInteger;
+import org.json.JSONException;
+import org.junit.jupiter.api.Test;
+import org.xrpl.xrpl4j.model.AbstractJsonTest;
+import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier;
+import org.xrpl.xrpl4j.model.client.ledger.OracleLedgerEntryParams;
+import org.xrpl.xrpl4j.model.transactions.Address;
+import org.xrpl.xrpl4j.model.transactions.OracleDocumentId;
+
+class GetAggregatePriceRequestParamsTest extends AbstractJsonTest {
+
+ @Test
+ void testJson() throws JSONException, JsonProcessingException {
+ GetAggregatePriceRequestParams params = GetAggregatePriceRequestParams.builder()
+ .ledgerSpecifier(LedgerSpecifier.CURRENT)
+ .baseAsset("XRP")
+ .quoteAsset("USD")
+ .trim(UnsignedInteger.valueOf(20))
+ .addOracles(
+ OracleLedgerEntryParams.builder()
+ .account(Address.of("rp047ow9WcPmnNpVHMQV5A4BF6vaL9Abm6"))
+ .oracleDocumentId(OracleDocumentId.of(UnsignedInteger.valueOf(34)))
+ .build()
+ )
+ .build();
+
+ String json = "{\n" +
+ " \"ledger_index\": \"current\",\n" +
+ " \"base_asset\": \"XRP\",\n" +
+ " \"quote_asset\": \"USD\",\n" +
+ " \"trim\": 20,\n" +
+ " \"oracles\": [\n" +
+ " {\n" +
+ " \"account\": \"rp047ow9WcPmnNpVHMQV5A4BF6vaL9Abm6\",\n" +
+ " \"oracle_document_id\": 34\n" +
+ " }\n" +
+ " ]\n" +
+ " }";
+
+ assertCanSerializeAndDeserialize(params, json);
+ }
+}
\ No newline at end of file
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/oracle/GetAggregatePriceResultTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/oracle/GetAggregatePriceResultTest.java
new file mode 100644
index 000000000..86cba139f
--- /dev/null
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/client/oracle/GetAggregatePriceResultTest.java
@@ -0,0 +1,107 @@
+package org.xrpl.xrpl4j.model.client.oracle;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
+
+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.client.oracle.ImmutableGetAggregatePriceResult.Builder;
+import org.xrpl.xrpl4j.model.transactions.Hash256;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+class GetAggregatePriceResultTest extends AbstractJsonTest {
+
+ @Test
+ void testJson() throws JSONException, JsonProcessingException {
+ GetAggregatePriceResult result = baseBuilder()
+ .ledgerCurrentIndex(LedgerIndex.of(UnsignedInteger.valueOf(25)))
+ .build();
+
+ String json = "{\n" +
+ " \"entire_set\" : {\n" +
+ " \"mean\" : \"74.75\",\n" +
+ " \"size\" : 10,\n" +
+ " \"standard_deviation\" : \"0.1290994448735806\"\n" +
+ " },\n" +
+ " \"ledger_current_index\" : 25,\n" +
+ " \"median\" : \"74.75\",\n" +
+ " \"status\" : \"success\",\n" +
+ " \"trimmed_set\" : {\n" +
+ " \"mean\" : \"74.75\",\n" +
+ " \"size\" : 6,\n" +
+ " \"standard_deviation\" : \"0.1290994448735806\"\n" +
+ " },\n" +
+ " \"validated\" : false,\n" +
+ " \"time\" : 78937648\n" +
+ "}";
+
+ assertCanSerializeAndDeserialize(result, json);
+ }
+
+ @Test
+ void testWithLedgerCurrentIndex() {
+ GetAggregatePriceResult result = baseBuilder()
+ .ledgerCurrentIndex(LedgerIndex.of(UnsignedInteger.valueOf(25)))
+ .build();
+
+ assertThat(result.ledgerCurrentIndex()).isNotEmpty().get().isEqualTo(result.ledgerCurrentIndexSafe());
+ assertThatThrownBy(result::ledgerIndexSafe).isInstanceOf(IllegalStateException.class);
+ assertThatThrownBy(result::ledgerHashSafe).isInstanceOf(IllegalStateException.class);
+ }
+
+ @Test
+ void testWithLedgerIndex() {
+ GetAggregatePriceResult result = baseBuilder()
+ .ledgerIndex(LedgerIndex.of(UnsignedInteger.valueOf(25)))
+ .build();
+
+ assertThat(result.ledgerIndex()).isNotEmpty().get().isEqualTo(result.ledgerIndexSafe());
+ assertThatThrownBy(result::ledgerCurrentIndexSafe).isInstanceOf(IllegalStateException.class);
+ assertThatThrownBy(result::ledgerHashSafe).isInstanceOf(IllegalStateException.class);
+ }
+
+ @Test
+ void testWithLedgerHash() {
+ GetAggregatePriceResult result = baseBuilder()
+ .ledgerHash(Hash256.of("B9D3D80EDF4083A06B2D51202E0BFB63C46FC0985E015D06767C21A62853BF6D"))
+ .build();
+
+ assertThat(result.ledgerHash()).isNotEmpty().get().isEqualTo(result.ledgerHashSafe());
+ assertThatThrownBy(result::ledgerCurrentIndexSafe).isInstanceOf(IllegalStateException.class);
+ assertThatThrownBy(result::ledgerIndexSafe).isInstanceOf(IllegalStateException.class);
+ }
+
+ @Test
+ void testMedian() {
+ GetAggregatePriceResult result = baseBuilder().build();
+ assertThat(result.median()).isEqualTo(BigDecimal.valueOf(74.75));
+ }
+
+ private static Builder baseBuilder() {
+ return GetAggregatePriceResult.builder()
+ .entireSet(
+ AggregatePriceSet.builder()
+ .meanString("74.75")
+ .size(UnsignedLong.valueOf(10))
+ .standardDeviationString("0.1290994448735806")
+ .build()
+ )
+ .medianString("74.75")
+ .status("success")
+ .trimmedSet(
+ AggregatePriceSet.builder()
+ .meanString("74.75")
+ .size(UnsignedLong.valueOf(6))
+ .standardDeviationString("0.1290994448735806")
+ .build()
+ )
+ .time(UnsignedInteger.valueOf(78937648));
+ }
+}
\ No newline at end of file
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/jackson/modules/AssetPriceDeserializerTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/jackson/modules/AssetPriceDeserializerTest.java
new file mode 100644
index 000000000..ea4c3a0e9
--- /dev/null
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/jackson/modules/AssetPriceDeserializerTest.java
@@ -0,0 +1,43 @@
+package org.xrpl.xrpl4j.model.jackson.modules;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.google.common.primitives.UnsignedLong;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.xrpl.xrpl4j.model.transactions.AssetPrice;
+
+import java.io.IOException;
+
+/**
+ * Unit tests for {@link AssetPriceDeserializer}.
+ */
+class AssetPriceDeserializerTest {
+
+ private AssetPriceDeserializer deserializer;
+
+ @BeforeEach
+ void setUp() {
+ deserializer = new AssetPriceDeserializer();
+ }
+
+ @Test
+ void testDeserialize() throws IOException {
+ JsonParser mockJsonParser = mock(JsonParser.class);
+
+ AssetPrice expected = AssetPrice.of(UnsignedLong.ZERO);
+ when(mockJsonParser.getText()).thenReturn("0");
+ AssetPrice assetPrice = deserializer.deserialize(mockJsonParser, mock(DeserializationContext.class));
+ assertThat(assetPrice).isEqualTo(expected);
+
+ expected = AssetPrice.of(UnsignedLong.MAX_VALUE);
+ when(mockJsonParser.getText()).thenReturn("ffffffffffffffff");
+ assetPrice = deserializer.deserialize(mockJsonParser, mock(DeserializationContext.class));
+ assertThat(assetPrice).isEqualTo(expected);
+ }
+
+}
\ No newline at end of file
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/jackson/modules/AssetPriceSerializerTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/jackson/modules/AssetPriceSerializerTest.java
new file mode 100644
index 000000000..d0e9563ac
--- /dev/null
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/jackson/modules/AssetPriceSerializerTest.java
@@ -0,0 +1,39 @@
+package org.xrpl.xrpl4j.model.jackson.modules;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.google.common.primitives.UnsignedLong;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.xrpl.xrpl4j.model.transactions.AssetPrice;
+
+import java.io.IOException;
+
+/**
+ * Unit test for {@link AssetPriceSerializer}.
+ */
+class AssetPriceSerializerTest {
+
+ private AssetPriceSerializer serializer;
+
+ @BeforeEach
+ void setUp() {
+ serializer = new AssetPriceSerializer();
+ }
+
+ @Test
+ void testSerialize() throws IOException {
+ JsonGenerator jsonGeneratorMock = mock(JsonGenerator.class);
+
+ AssetPrice expected = AssetPrice.of(UnsignedLong.ZERO);
+ serializer.serialize(expected, jsonGeneratorMock, mock(SerializerProvider.class));
+ verify(jsonGeneratorMock).writeString("0");
+
+ expected = AssetPrice.of(UnsignedLong.MAX_VALUE);
+ serializer.serialize(expected, jsonGeneratorMock, mock(SerializerProvider.class));
+ verify(jsonGeneratorMock).writeString("ffffffffffffffff");
+ }
+}
\ No newline at end of file
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/jackson/modules/OracleDocumentIdDeserializerTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/jackson/modules/OracleDocumentIdDeserializerTest.java
new file mode 100644
index 000000000..d1f985a86
--- /dev/null
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/jackson/modules/OracleDocumentIdDeserializerTest.java
@@ -0,0 +1,43 @@
+package org.xrpl.xrpl4j.model.jackson.modules;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.google.common.primitives.UnsignedInteger;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.xrpl.xrpl4j.model.transactions.OracleDocumentId;
+
+import java.io.IOException;
+
+/**
+ * Unit test for {@link OracleDocumentIdDeserializer}.
+ */
+class OracleDocumentIdDeserializerTest {
+
+ private OracleDocumentIdDeserializer deserializer;
+
+ @BeforeEach
+ void setUp() {
+ deserializer = new OracleDocumentIdDeserializer();
+ }
+
+ @Test
+ void testDeserialize() throws IOException {
+ JsonParser mockJsonParser = mock(JsonParser.class);
+
+ when(mockJsonParser.getLongValue()).thenReturn(0L);
+ OracleDocumentId expected = OracleDocumentId.of(UnsignedInteger.ZERO);
+ OracleDocumentId oracleDocumentId = deserializer.deserialize(mockJsonParser, mock(DeserializationContext.class));
+ assertThat(oracleDocumentId).isEqualTo(expected);
+
+ when(mockJsonParser.getLongValue()).thenReturn(4294967295L);
+ expected = OracleDocumentId.of(UnsignedInteger.MAX_VALUE);
+ oracleDocumentId = deserializer.deserialize(mockJsonParser, mock(DeserializationContext.class));
+ assertThat(oracleDocumentId).isEqualTo(expected);
+ }
+
+}
\ No newline at end of file
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/jackson/modules/OracleDocumentIdSerializerTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/jackson/modules/OracleDocumentIdSerializerTest.java
new file mode 100644
index 000000000..c9d64a595
--- /dev/null
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/jackson/modules/OracleDocumentIdSerializerTest.java
@@ -0,0 +1,40 @@
+package org.xrpl.xrpl4j.model.jackson.modules;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.google.common.primitives.UnsignedInteger;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.xrpl.xrpl4j.model.transactions.OracleDocumentId;
+
+import java.io.IOException;
+
+/**
+ * Unit tests for {@link OracleDocumentIdSerializer}.
+ */
+class OracleDocumentIdSerializerTest {
+
+ private OracleDocumentIdSerializer serializer;
+
+ @BeforeEach
+ void setUp() {
+ serializer = new OracleDocumentIdSerializer();
+ }
+
+ @Test
+ void testSerialize() throws IOException {
+ JsonGenerator jsonGeneratorMock = mock(JsonGenerator.class);
+
+ OracleDocumentId expected = OracleDocumentId.of(UnsignedInteger.ZERO);
+ serializer.serialize(expected, jsonGeneratorMock, mock(SerializerProvider.class));
+ verify(jsonGeneratorMock).writeNumber(0L);
+
+ expected = OracleDocumentId.of(UnsignedInteger.MAX_VALUE);
+ serializer.serialize(expected, jsonGeneratorMock, mock(SerializerProvider.class));
+ verify(jsonGeneratorMock).writeNumber(4294967295L);
+ }
+
+}
\ No newline at end of file
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/jackson/modules/OracleProviderDeserializerTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/jackson/modules/OracleProviderDeserializerTest.java
new file mode 100644
index 000000000..f262b1ad6
--- /dev/null
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/jackson/modules/OracleProviderDeserializerTest.java
@@ -0,0 +1,42 @@
+package org.xrpl.xrpl4j.model.jackson.modules;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.xrpl.xrpl4j.model.transactions.OracleProvider;
+
+import java.io.IOException;
+
+/**
+ * Unit test for {@link OracleProviderDeserializer}.
+ */
+class OracleProviderDeserializerTest {
+
+ private OracleProviderDeserializer deserializer;
+
+ @BeforeEach
+ void setUp() {
+ deserializer = new OracleProviderDeserializer();
+ }
+
+ @Test
+ void testDeserialize() throws IOException {
+ JsonParser mockJsonParser = mock(JsonParser.class);
+
+ OracleProvider expected = OracleProvider.of("foo");
+ when(mockJsonParser.getText()).thenReturn("foo");
+ OracleProvider assetPrice = deserializer.deserialize(mockJsonParser, mock(DeserializationContext.class));
+ assertThat(assetPrice).isEqualTo(expected);
+
+ expected = OracleProvider.of("");
+ when(mockJsonParser.getText()).thenReturn("");
+ assetPrice = deserializer.deserialize(mockJsonParser, mock(DeserializationContext.class));
+ assertThat(assetPrice).isEqualTo(expected);
+ }
+
+}
\ No newline at end of file
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/jackson/modules/OracleUriDeserializerTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/jackson/modules/OracleUriDeserializerTest.java
new file mode 100644
index 000000000..ad2368116
--- /dev/null
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/jackson/modules/OracleUriDeserializerTest.java
@@ -0,0 +1,42 @@
+package org.xrpl.xrpl4j.model.jackson.modules;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.xrpl.xrpl4j.model.transactions.OracleUri;
+
+import java.io.IOException;
+
+/**
+ * Unit tests for {@link OracleUriDeserializer}.
+ */
+class OracleUriDeserializerTest {
+
+ private OracleUriDeserializer deserializer;
+
+ @BeforeEach
+ void setUp() {
+ deserializer = new OracleUriDeserializer();
+ }
+
+ @Test
+ void testDeserialize() throws IOException {
+ JsonParser mockJsonParser = mock(JsonParser.class);
+
+ OracleUri expected = OracleUri.of("foo");
+ when(mockJsonParser.getText()).thenReturn("foo");
+ OracleUri oracleUri = deserializer.deserialize(mockJsonParser, mock(DeserializationContext.class));
+ assertThat(oracleUri).isEqualTo(expected);
+
+ expected = OracleUri.of("");
+ when(mockJsonParser.getText()).thenReturn("");
+ oracleUri = deserializer.deserialize(mockJsonParser, mock(DeserializationContext.class));
+ assertThat(oracleUri).isEqualTo(expected);
+ }
+
+}
\ No newline at end of file
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/ledger/OracleObjectTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/ledger/OracleObjectTest.java
new file mode 100644
index 000000000..94ea51fd3
--- /dev/null
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/ledger/OracleObjectTest.java
@@ -0,0 +1,68 @@
+package org.xrpl.xrpl4j.model.ledger;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+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.flags.Flags;
+import org.xrpl.xrpl4j.model.transactions.Address;
+import org.xrpl.xrpl4j.model.transactions.AssetPrice;
+import org.xrpl.xrpl4j.model.transactions.Hash256;
+import org.xrpl.xrpl4j.model.transactions.OracleProvider;
+import org.xrpl.xrpl4j.model.transactions.PriceData;
+import org.xrpl.xrpl4j.model.transactions.PriceDataWrapper;
+
+class OracleObjectTest extends AbstractJsonTest {
+
+ @Test
+ void testJson() throws JSONException, JsonProcessingException {
+ OracleObject oracleObject = OracleObject.builder()
+ .assetClass("63757272656E6379")
+ .lastUpdateTime(UnsignedInteger.valueOf(1715797016))
+ .owner(Address.of("rMS69A6J39RmBg5yWDft5XAM8zTGbtMMZy"))
+ .ownerNode("0")
+ .previousTransactionId(Hash256.of("A5183686EF85C7D563B400C127DBEA71D1E404E419424BABB2891F4CC772E157"))
+ .previousTransactionLedgerSequence(UnsignedInteger.valueOf(722809))
+ .provider(OracleProvider.of("68747470733A2F2F74687265657872702E646576"))
+ .addPriceDataSeries(
+ PriceDataWrapper.of(
+ PriceData.builder()
+ .assetPrice(AssetPrice.of(UnsignedLong.valueOf("2030", 16)))
+ .baseAsset("XRP")
+ .quoteAsset("IDR")
+ .build()
+ )
+ )
+ .index(Hash256.of("72B5901470838D8C71E392C43999DC357BF49C1DA75583872C170AD0C28A12E8"))
+ .build();
+
+ String json = "{\n" +
+ " \"AssetClass\": \"63757272656E6379\",\n" +
+ " \"Flags\": 0,\n" +
+ " \"LastUpdateTime\": 1715797016,\n" +
+ " \"LedgerEntryType\": \"Oracle\",\n" +
+ " \"Owner\": \"rMS69A6J39RmBg5yWDft5XAM8zTGbtMMZy\",\n" +
+ " \"OwnerNode\": \"0\",\n" +
+ " \"PreviousTxnID\": \"A5183686EF85C7D563B400C127DBEA71D1E404E419424BABB2891F4CC772E157\",\n" +
+ " \"PreviousTxnLgrSeq\": 722809,\n" +
+ " \"PriceDataSeries\": [\n" +
+ " {\n" +
+ " \"PriceData\": {\n" +
+ " \"AssetPrice\": \"2030\",\n" +
+ " \"BaseAsset\": \"XRP\",\n" +
+ " \"QuoteAsset\": \"IDR\"\n" +
+ " }\n" +
+ " }\n" +
+ " ],\n" +
+ " \"Provider\": \"68747470733A2F2F74687265657872702E646576\",\n" +
+ " \"index\": \"72B5901470838D8C71E392C43999DC357BF49C1DA75583872C170AD0C28A12E8\"\n" +
+ " }";
+
+ assertCanSerializeAndDeserialize(oracleObject, json);
+ assertThat(oracleObject.flags()).isEqualTo(Flags.UNSET);
+ }
+}
\ No newline at end of file
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/AmmBidTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/AmmBidTest.java
index 91f51139b..0975e3aa0 100644
--- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/AmmBidTest.java
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/AmmBidTest.java
@@ -5,6 +5,7 @@
import org.json.JSONException;
import org.junit.jupiter.api.Test;
import org.xrpl.xrpl4j.crypto.keys.PublicKey;
+import org.xrpl.xrpl4j.crypto.signing.Signature;
import org.xrpl.xrpl4j.model.AbstractJsonTest;
import org.xrpl.xrpl4j.model.flags.TransactionFlags;
import org.xrpl.xrpl4j.model.ledger.AuthAccount;
@@ -249,4 +250,66 @@ void testJsonWithMinAndMax() throws JSONException, JsonProcessingException {
assertCanSerializeAndDeserialize(bid, json);
}
+
+ /**
+ * Test that ensures the problematic transaction found in #529 is deserializable.
+ */
+ @Test
+ void testJsonWithXrpAmountBidMinAndMax() throws JSONException, JsonProcessingException {
+ AmmBid ammBid = AmmBid.builder()
+ .account(Address.of("rammersz4CroiyvbkzeZN1sBDCK9P8DvxF"))
+ .asset(Issue.XRP)
+ .asset2(
+ Issue.builder()
+ .issuer(Address.of("rswh1fvyLqHizBS2awu1vs6QcmwTBd9qiv"))
+ .currency("XAH")
+ .build()
+ )
+ .addAuthAccounts(
+ AuthAccountWrapper.of(
+ AuthAccount.of(Address.of("rapido5rxPmP4YkMZZEeXSHqWefxHEkqv6"))
+ )
+ )
+ .bidMax(XrpCurrencyAmount.ofDrops(10))
+ .bidMin(XrpCurrencyAmount.ofDrops(10))
+ .fee(XrpCurrencyAmount.ofDrops(10))
+ .flags(TransactionFlags.FULLY_CANONICAL_SIG)
+ .sequence(UnsignedInteger.valueOf(87704195))
+ .signingPublicKey(
+ PublicKey.fromBase16EncodedPublicKey("ED2D15BC6B61D6520011E4C794C5B320E584106154D0865BB095D70DA9A2A57B57")
+ )
+ .transactionSignature(
+ Signature.fromBase16("F652BD5369F6EE9A8A1490BD37B8240CEE2B4B6EF94D22EC2DBB6912AA729B829" +
+ "FC3D7E24B30A1E6CC11F868CE229B105398719152B9BEE8992A56D654F79C0A")
+ )
+ .build();
+ String json = "{\n" +
+ " \"Account\": \"rammersz4CroiyvbkzeZN1sBDCK9P8DvxF\",\n" +
+ " \"Asset\": {\n" +
+ " \"currency\": \"XRP\"\n" +
+ " },\n" +
+ " \"Asset2\": {\n" +
+ " \"currency\": \"XAH\",\n" +
+ " \"issuer\": \"rswh1fvyLqHizBS2awu1vs6QcmwTBd9qiv\"\n" +
+ " },\n" +
+ " \"AuthAccounts\": [\n" +
+ " {\n" +
+ " \"AuthAccount\": {\n" +
+ " \"Account\": \"rapido5rxPmP4YkMZZEeXSHqWefxHEkqv6\"\n" +
+ " }\n" +
+ " }\n" +
+ " ],\n" +
+ " \"BidMax\": \"10\",\n" +
+ " \"BidMin\": \"10\",\n" +
+ " \"Fee\": \"10\",\n" +
+ " \"Flags\": 2147483648,\n" +
+ " \"Sequence\": 87704195,\n" +
+ " \"SigningPubKey\": \"ED2D15BC6B61D6520011E4C794C5B320E584106154D0865BB095D70DA9A2A57B57\",\n" +
+ " \"TransactionType\": \"AMMBid\",\n" +
+ " \"TxnSignature\": \"F652BD5369F6EE9A8A1490BD37B8240CEE2B4B6EF94D22EC2DBB6912AA729B829FC3D7E24B30A" +
+ "1E6CC11F868CE229B105398719152B9BEE8992A56D654F79C0A\"\n" +
+ "}";
+ assertCanSerializeAndDeserialize(ammBid, json);
+ }
}
\ No newline at end of file
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/AmmDepositTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/AmmDepositTest.java
index 9f4c5c1f9..004816e7d 100644
--- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/AmmDepositTest.java
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/AmmDepositTest.java
@@ -70,6 +70,47 @@ void constructLpTokenDepositAndTestJson() throws JSONException, JsonProcessingEx
assertCanSerializeAndDeserialize(deposit, json);
}
+ @Test
+ void constructLpTokenDepositWithXrpLpTokenAmountAndTestJson() throws JSONException, JsonProcessingException {
+ AmmDeposit deposit = AmmDeposit.builder()
+ .account(Address.of("rJVUeRqDFNs2xqA7ncVE6ZoAhPUoaJJSQm"))
+ .fee(XrpCurrencyAmount.ofDrops(10))
+ .signingPublicKey(
+ PublicKey.fromBase16EncodedPublicKey("02356E89059A75438887F9FEE2056A2890DB82A68353BE9C0C0C8F89C0018B37FC")
+ )
+ .flags(AmmDepositFlags.LP_TOKEN)
+ .asset(Issue.XRP)
+ .asset2(
+ Issue.builder()
+ .issuer(Address.of("rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd"))
+ .currency("TST")
+ .build()
+ )
+ .lpTokenOut(XrpCurrencyAmount.ofDrops(10))
+ .build();
+
+ assertThat(deposit.flags()).isEqualTo(AmmDepositFlags.LP_TOKEN);
+
+ String json = "{\n" +
+ " \"Account\" : \"" + deposit.account() + "\",\n" +
+ " \"LPTokenOut\" : \"10\",\n" +
+ " \"Asset2\" : {\n" +
+ " \"currency\" : \"TST\",\n" +
+ " \"issuer\" : \"rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd\"\n" +
+ " },\n" +
+ " \"Asset\" : {\n" +
+ " \"currency\" : \"XRP\"\n" +
+ " },\n" +
+ " \"Fee\" : \"10\",\n" +
+ " \"Flags\" : " + AmmDepositFlags.LP_TOKEN + ",\n" +
+ " \"Sequence\" : 0,\n" +
+ " \"SigningPubKey\" : \"02356E89059A75438887F9FEE2056A2890DB82A68353BE9C0C0C8F89C0018B37FC\",\n" +
+ " \"TransactionType\" : \"AMMDeposit\"\n" +
+ "}";
+
+ assertCanSerializeAndDeserialize(deposit, json);
+ }
+
@Test
void constructTwoAssetDepositAndTestJson() throws JSONException, JsonProcessingException {
AmmDeposit deposit = AmmDeposit.builder()
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/AmmWithdrawTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/AmmWithdrawTest.java
index 493af7fe3..8f89e64ff 100644
--- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/AmmWithdrawTest.java
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/AmmWithdrawTest.java
@@ -34,6 +34,28 @@ void constructLpTokenWithdrawAndTestJson() throws JSONException, JsonProcessingE
assertCanSerializeAndDeserialize(withdraw, json);
}
+ @Test
+ void constructLpTokenWithdrawWithXrpCurrencyAmountAndTestJson() throws JSONException, JsonProcessingException {
+ AmmWithdraw withdraw = baseBuilder()
+ .flags(AmmWithdrawFlags.LP_TOKEN)
+ .lpTokensIn(XrpCurrencyAmount.ofDrops(10))
+ .build();
+
+ String json = "{\n" +
+ " \"Account\" : \"rJVUeRqDFNs2xqA7ncVE6ZoAhPUoaJJSQm\",\n" +
+ " \"LPTokensIn\" : \"10\"," +
+ " \"Asset\" : " + objectMapper.writeValueAsString(withdraw.asset()) + "," +
+ " \"Asset2\" : " + objectMapper.writeValueAsString(withdraw.asset2()) + "," +
+ " \"Fee\" : \"10\",\n" +
+ " \"Flags\" : " + AmmWithdrawFlags.LP_TOKEN + ",\n" +
+ " \"Sequence\" : 0,\n" +
+ " \"SigningPubKey\" : \"02356E89059A75438887F9FEE2056A2890DB82A68353BE9C0C0C8F89C0018B37FC\",\n" +
+ " \"TransactionType\" : \"AMMWithdraw\"\n" +
+ "}";
+
+ assertCanSerializeAndDeserialize(withdraw, json);
+ }
+
@Test
void constructWithdrawAllAndTestJson() throws JSONException, JsonProcessingException {
AmmWithdraw withdraw = baseBuilder()
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/AssetPriceTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/AssetPriceTest.java
new file mode 100644
index 000000000..274699ecc
--- /dev/null
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/AssetPriceTest.java
@@ -0,0 +1,76 @@
+package org.xrpl.xrpl4j.model.transactions;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.google.common.primitives.UnsignedLong;
+import org.assertj.core.api.Assertions;
+import org.immutables.value.Value;
+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.jackson.ObjectMapperFactory;
+
+/**
+ * Unit tests for {@link AssetPrice}.
+ */
+public class AssetPriceTest {
+
+ ObjectMapper objectMapper = ObjectMapperFactory.create();
+
+ @Test
+ void testToString() {
+ AssetPrice count = AssetPrice.of(UnsignedLong.ZERO);
+ assertThat(count.toString()).isEqualTo("0");
+
+ AssetPrice countMax = AssetPrice.of(UnsignedLong.MAX_VALUE);
+ assertThat(countMax.toString()).isEqualTo("18446744073709551615");
+ }
+
+ @Test
+ void testJson() throws JsonProcessingException, JSONException {
+ AssetPrice count = AssetPrice.of(UnsignedLong.valueOf(1000));
+ AssetPriceWrapper wrapper = AssetPriceWrapper.of(count);
+
+ String json = "{\"value\": \"3e8\"}";
+ assertSerializesAndDeserializes(wrapper, json);
+ }
+
+ @Test
+ void testMaxJson() throws JSONException, JsonProcessingException {
+ AssetPrice count = AssetPrice.of(UnsignedLong.MAX_VALUE);
+ AssetPriceWrapper wrapper = AssetPriceWrapper.of(count);
+
+ String json = "{\"value\": \"ffffffffffffffff\"}";
+ assertSerializesAndDeserializes(wrapper, json);
+ }
+
+ private void assertSerializesAndDeserializes(
+ AssetPriceWrapper wrapper,
+ String json
+ ) throws JsonProcessingException, JSONException {
+ String serialized = objectMapper.writeValueAsString(wrapper);
+ JSONAssert.assertEquals(json, serialized, JSONCompareMode.STRICT);
+ AssetPriceWrapper deserialized = objectMapper.readValue(
+ serialized, AssetPriceWrapper.class
+ );
+ Assertions.assertThat(deserialized).isEqualTo(wrapper);
+ }
+
+ @Value.Immutable
+ @JsonSerialize(as = ImmutableAssetPriceWrapper.class)
+ @JsonDeserialize(as = ImmutableAssetPriceWrapper.class)
+ interface AssetPriceWrapper {
+
+ static AssetPriceWrapper of(AssetPrice count) {
+ return ImmutableAssetPriceWrapper.builder().value(count).build();
+ }
+
+ AssetPrice value();
+
+ }
+}
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/OracleDeleteTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/OracleDeleteTest.java
new file mode 100644
index 000000000..54e54264a
--- /dev/null
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/OracleDeleteTest.java
@@ -0,0 +1,48 @@
+package org.xrpl.xrpl4j.model.transactions;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.xrpl.xrpl4j.crypto.TestConstants.ED_PUBLIC_KEY;
+import static org.xrpl.xrpl4j.crypto.TestConstants.ED_PUBLIC_KEY_HEX;
+
+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.flags.TransactionFlags;
+
+class OracleDeleteTest extends AbstractJsonTest {
+
+ @Test
+ void testDefaultFlags() {
+ OracleDelete oracleDelete = baseBuilder().build();
+
+ assertThat(oracleDelete.flags()).isEqualTo(TransactionFlags.EMPTY);
+ }
+
+ @Test
+ void testJson() throws JSONException, JsonProcessingException {
+ OracleDelete oracleDelete = baseBuilder().build();
+ String json = "\n" +
+ "{\n" +
+ " \"TransactionType\": \"OracleDelete\",\n" +
+ " \"Account\": \"rp4pqYgrTAtdPHuZd1ZQWxrzx45jxYcZex\",\n" +
+ " \"OracleDocumentID\": 1,\n" +
+ " \"Fee\": \"12\",\n" +
+ " \"Sequence\": 391,\n" +
+ " \"SigningPubKey\": \"" + ED_PUBLIC_KEY_HEX + "\"\n" +
+ "}";
+
+ assertCanSerializeAndDeserialize(oracleDelete, json);
+ }
+
+ private static ImmutableOracleDelete.Builder baseBuilder() {
+ return OracleDelete.builder()
+ .account(Address.of("rp4pqYgrTAtdPHuZd1ZQWxrzx45jxYcZex"))
+ .fee(XrpCurrencyAmount.ofDrops(12))
+ .sequence(UnsignedInteger.valueOf(391))
+ .signingPublicKey(ED_PUBLIC_KEY)
+ .oracleDocumentId(OracleDocumentId.of(UnsignedInteger.ONE));
+ }
+}
\ No newline at end of file
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/OracleDocumentIdTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/OracleDocumentIdTest.java
new file mode 100644
index 000000000..976ef71b2
--- /dev/null
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/OracleDocumentIdTest.java
@@ -0,0 +1,65 @@
+package org.xrpl.xrpl4j.model.transactions;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.google.common.primitives.UnsignedInteger;
+import org.assertj.core.api.Assertions;
+import org.immutables.value.Value;
+import org.immutables.value.Value.Immutable;
+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.jackson.ObjectMapperFactory;
+
+/**
+ * Unit tests for {@link OracleDocumentId}.
+ */
+public class OracleDocumentIdTest {
+
+ ObjectMapper objectMapper = ObjectMapperFactory.create();
+
+ @Test
+ void testToString() {
+ OracleDocumentId count = OracleDocumentId.of(UnsignedInteger.ONE);
+ assertThat(count.toString()).isEqualTo("1");
+ }
+
+ @Test
+ void testJson() throws JsonProcessingException, JSONException {
+ OracleDocumentId count = OracleDocumentId.of(UnsignedInteger.ONE);
+ OracleDocumentIdWrapper wrapper = OracleDocumentIdWrapper.of(count);
+
+ String json = "{\"value\": 1}";
+ assertSerializesAndDeserializes(wrapper, json);
+ }
+
+ private void assertSerializesAndDeserializes(
+ OracleDocumentIdWrapper wrapper,
+ String json
+ ) throws JsonProcessingException, JSONException {
+ String serialized = objectMapper.writeValueAsString(wrapper);
+ JSONAssert.assertEquals(json, serialized, JSONCompareMode.STRICT);
+ OracleDocumentIdWrapper deserialized = objectMapper.readValue(
+ serialized, OracleDocumentIdWrapper.class
+ );
+ Assertions.assertThat(deserialized).isEqualTo(wrapper);
+ }
+
+ @Immutable
+ @JsonSerialize(as = ImmutableOracleDocumentIdWrapper.class)
+ @JsonDeserialize(as = ImmutableOracleDocumentIdWrapper.class)
+ interface OracleDocumentIdWrapper {
+
+ static OracleDocumentIdWrapper of(OracleDocumentId value) {
+ return ImmutableOracleDocumentIdWrapper.builder().value(value).build();
+ }
+
+ OracleDocumentId value();
+
+ }
+}
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/OracleProviderTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/OracleProviderTest.java
new file mode 100644
index 000000000..024c11de5
--- /dev/null
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/OracleProviderTest.java
@@ -0,0 +1,61 @@
+package org.xrpl.xrpl4j.model.transactions;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.google.common.primitives.UnsignedInteger;
+import org.assertj.core.api.Assertions;
+import org.immutables.value.Value.Immutable;
+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.jackson.ObjectMapperFactory;
+
+public class OracleProviderTest {
+
+ ObjectMapper objectMapper = ObjectMapperFactory.create();
+
+ @Test
+ void testToString() {
+ OracleProvider count = OracleProvider.of("ABCD");
+ assertThat(count.toString()).isEqualTo("ABCD");
+ }
+
+ @Test
+ void testJson() throws JsonProcessingException, JSONException {
+ OracleProvider count = OracleProvider.of("ABCD");
+ OracleProviderWrapper wrapper = OracleProviderWrapper.of(count);
+
+ String json = "{\"value\": \"ABCD\"}";
+ assertSerializesAndDeserializes(wrapper, json);
+ }
+
+ private void assertSerializesAndDeserializes(
+ OracleProviderWrapper wrapper,
+ String json
+ ) throws JsonProcessingException, JSONException {
+ String serialized = objectMapper.writeValueAsString(wrapper);
+ JSONAssert.assertEquals(json, serialized, JSONCompareMode.STRICT);
+ OracleProviderWrapper deserialized = objectMapper.readValue(
+ serialized, OracleProviderWrapper.class
+ );
+ Assertions.assertThat(deserialized).isEqualTo(wrapper);
+ }
+
+ @Immutable
+ @JsonSerialize(as = ImmutableOracleProviderWrapper.class)
+ @JsonDeserialize(as = ImmutableOracleProviderWrapper.class)
+ interface OracleProviderWrapper {
+
+ static OracleProviderWrapper of(OracleProvider value) {
+ return ImmutableOracleProviderWrapper.builder().value(value).build();
+ }
+
+ OracleProvider value();
+
+ }
+}
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/OracleSetTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/OracleSetTest.java
new file mode 100644
index 000000000..05bdb2733
--- /dev/null
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/OracleSetTest.java
@@ -0,0 +1,94 @@
+package org.xrpl.xrpl4j.model.transactions;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+import static org.xrpl.xrpl4j.crypto.TestConstants.ED_PUBLIC_KEY;
+import static org.xrpl.xrpl4j.crypto.TestConstants.ED_PUBLIC_KEY_HEX;
+
+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.flags.TransactionFlags;
+
+class OracleSetTest extends AbstractJsonTest {
+
+ @Test
+ void testDefaultFlags() {
+ OracleSet oracleSet = baseBuilder().build();
+
+ assertThat(oracleSet.flags()).isEqualTo(TransactionFlags.EMPTY);
+ }
+
+ @Test
+ void testJsonEmptyFields() throws JSONException, JsonProcessingException {
+ OracleSet oracleSet = baseBuilder().build();
+ String json = "\n" +
+ "{\n" +
+ " \"TransactionType\": \"OracleSet\",\n" +
+ " \"Account\": \"rp4pqYgrTAtdPHuZd1ZQWxrzx45jxYcZex\",\n" +
+ " \"OracleDocumentID\": 1,\n" +
+ " \"Fee\": \"12\",\n" +
+ " \"Sequence\": 391,\n" +
+ " \"SigningPubKey\": \"" + ED_PUBLIC_KEY_HEX + "\",\n" +
+ " \"LastUpdateTime\": 1\n" +
+ "}";
+
+ assertCanSerializeAndDeserialize(oracleSet, json);
+ }
+
+ @Test
+ void testFullJson() throws JSONException, JsonProcessingException {
+ OracleSet oracleSet = baseBuilder()
+ .provider(OracleProvider.of("70726F7669646572"))
+ .assetClass("63757272656E6379")
+ .uri(OracleUri.of("ABCD"))
+ .addPriceDataSeries(
+ PriceDataWrapper.of(
+ PriceData.builder()
+ .baseAsset("XRP")
+ .quoteAsset("USD")
+ .assetPrice(AssetPrice.of(UnsignedLong.ONE))
+ .scale(UnsignedInteger.valueOf(3))
+ .build()
+ )
+ )
+ .build();
+ String json = "\n" +
+ "{\n" +
+ " \"TransactionType\": \"OracleSet\",\n" +
+ " \"Account\": \"rp4pqYgrTAtdPHuZd1ZQWxrzx45jxYcZex\",\n" +
+ " \"Provider\": \"70726F7669646572\"," +
+ " \"AssetClass\": \"63757272656E6379\"," +
+ " \"URI\": \"ABCD\"," +
+ " \"OracleDocumentID\": 1,\n" +
+ " \"Fee\": \"12\",\n" +
+ " \"Sequence\": 391,\n" +
+ " \"SigningPubKey\": \"" + ED_PUBLIC_KEY_HEX + "\",\n" +
+ " \"LastUpdateTime\": 1,\n" +
+ " \"PriceDataSeries\": [\n" +
+ " {\n" +
+ " \"PriceData\": {\n" +
+ " \"BaseAsset\": \"XRP\",\n" +
+ " \"QuoteAsset\": \"USD\",\n" +
+ " \"AssetPrice\": \"1\",\n" +
+ " \"Scale\": 3\n" +
+ " }\n" +
+ " }\n" +
+ " ]" +
+ "}";
+
+ assertCanSerializeAndDeserialize(oracleSet, json);
+ }
+
+ private static ImmutableOracleSet.Builder baseBuilder() {
+ return OracleSet.builder()
+ .account(Address.of("rp4pqYgrTAtdPHuZd1ZQWxrzx45jxYcZex"))
+ .fee(XrpCurrencyAmount.ofDrops(12))
+ .sequence(UnsignedInteger.valueOf(391))
+ .signingPublicKey(ED_PUBLIC_KEY)
+ .oracleDocumentId(OracleDocumentId.of(UnsignedInteger.ONE))
+ .lastUpdateTime(UnsignedInteger.ONE);
+ }
+}
\ No newline at end of file
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/OracleUriTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/OracleUriTest.java
new file mode 100644
index 000000000..84d4acac9
--- /dev/null
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/OracleUriTest.java
@@ -0,0 +1,63 @@
+package org.xrpl.xrpl4j.model.transactions;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import org.assertj.core.api.Assertions;
+import org.immutables.value.Value.Immutable;
+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.jackson.ObjectMapperFactory;
+
+/**
+ * Unit tests for {@link OracleUri}.
+ */
+public class OracleUriTest {
+
+ ObjectMapper objectMapper = ObjectMapperFactory.create();
+
+ @Test
+ void testToString() {
+ OracleUri count = OracleUri.of("ABCD");
+ assertThat(count.toString()).isEqualTo("ABCD");
+ }
+
+ @Test
+ void testJson() throws JsonProcessingException, JSONException {
+ OracleUri count = OracleUri.of("ABCD");
+ OracleUriWrapper wrapper = OracleUriWrapper.of(count);
+
+ String json = "{\"value\": \"ABCD\"}";
+ assertSerializesAndDeserializes(wrapper, json);
+ }
+
+ private void assertSerializesAndDeserializes(
+ OracleUriWrapper wrapper,
+ String json
+ ) throws JsonProcessingException, JSONException {
+ String serialized = objectMapper.writeValueAsString(wrapper);
+ JSONAssert.assertEquals(json, serialized, JSONCompareMode.STRICT);
+ OracleUriWrapper deserialized = objectMapper.readValue(
+ serialized, OracleUriWrapper.class
+ );
+ Assertions.assertThat(deserialized).isEqualTo(wrapper);
+ }
+
+ @Immutable
+ @JsonSerialize(as = ImmutableOracleUriWrapper.class)
+ @JsonDeserialize(as = ImmutableOracleUriWrapper.class)
+ interface OracleUriWrapper {
+
+ static OracleUriWrapper of(OracleUri value) {
+ return ImmutableOracleUriWrapper.builder().value(value).build();
+ }
+
+ OracleUri value();
+
+ }
+}
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/PriceDataTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/PriceDataTest.java
new file mode 100644
index 000000000..6dec9e070
--- /dev/null
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/PriceDataTest.java
@@ -0,0 +1,69 @@
+package org.xrpl.xrpl4j.model.transactions;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.primitives.UnsignedLong;
+import org.assertj.core.api.Assertions;
+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.jackson.ObjectMapperFactory;
+
+/**
+ * Unit test for {@link PriceData}.
+ */
+class PriceDataTest {
+
+ ObjectMapper objectMapper = ObjectMapperFactory.create();
+
+ @Test
+ void builder() {
+ PriceData priceData = PriceData.builder()
+ .assetPrice(AssetPrice.of(UnsignedLong.ONE))
+ .baseAsset("baseAsset")
+ .quoteAsset("quoteAsset")
+ .build();
+
+ assertThat(priceData.assetPrice().isPresent()).isTrue();
+ priceData.assetPrice().ifPresent((assetPrice) -> assertThat(assetPrice.value()).isEqualTo(UnsignedLong.ONE));
+
+ assertThat(priceData.baseAsset()).isEqualTo("baseAsset");
+ assertThat(priceData.quoteAsset()).isEqualTo("quoteAsset");
+ }
+
+ @Test
+ void testToString() {
+ PriceData priceData = PriceData.builder()
+ .assetPrice(AssetPrice.of(UnsignedLong.ONE))
+ .baseAsset("baseAsset")
+ .quoteAsset("quoteAsset")
+ .build();
+ assertThat(priceData.toString()).isEqualTo("PriceData{baseAsset=baseAsset, quoteAsset=quoteAsset, assetPrice=1}");
+ }
+
+ @Test
+ void testJson() throws JsonProcessingException, JSONException {
+ PriceData priceData = PriceData.builder()
+ .assetPrice(AssetPrice.of(UnsignedLong.ONE))
+ .baseAsset("baseAsset")
+ .quoteAsset("quoteAsset")
+ .build();
+ PriceDataWrapper wrapper = PriceDataWrapper.of(priceData);
+
+ String json = "{\"PriceData\":{\"BaseAsset\":\"baseAsset\",\"QuoteAsset\":\"quoteAsset\",\"AssetPrice\":\"1\"}}";
+ assertSerializesAndDeserializes(wrapper, json);
+ }
+
+ private void assertSerializesAndDeserializes(
+ PriceDataWrapper wrapper,
+ String json
+ ) throws JsonProcessingException, JSONException {
+ String serialized = objectMapper.writeValueAsString(wrapper);
+ JSONAssert.assertEquals(json, serialized, JSONCompareMode.STRICT);
+ PriceDataWrapper deserialized = objectMapper.readValue(serialized, PriceDataWrapper.class);
+ Assertions.assertThat(deserialized).isEqualTo(wrapper);
+ }
+}
\ No newline at end of file
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/PriceDataWrapperTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/PriceDataWrapperTest.java
new file mode 100644
index 000000000..8fc7825d2
--- /dev/null
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/PriceDataWrapperTest.java
@@ -0,0 +1,42 @@
+package org.xrpl.xrpl4j.model.transactions;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.primitives.UnsignedLong;
+import org.assertj.core.api.Assertions;
+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.jackson.ObjectMapperFactory;
+
+/**
+ * Unit tests for {@link PriceDataWrapper}.
+ */
+class PriceDataWrapperTest {
+
+ ObjectMapper objectMapper = ObjectMapperFactory.create();
+
+ @Test
+ void testJson() throws JsonProcessingException, JSONException {
+ PriceData priceData = PriceData.builder()
+ .assetPrice(AssetPrice.of(UnsignedLong.ONE))
+ .baseAsset("baseAsset")
+ .quoteAsset("quoteAsset")
+ .build();
+ PriceDataWrapper wrapper = PriceDataWrapper.of(priceData);
+
+ String json = "{\"PriceData\":{\"BaseAsset\":\"baseAsset\",\"QuoteAsset\":\"quoteAsset\",\"AssetPrice\":\"1\"}}";
+ assertSerializesAndDeserializes(wrapper, json);
+ }
+
+ private void assertSerializesAndDeserializes(
+ PriceDataWrapper wrapper,
+ String json
+ ) throws JsonProcessingException, JSONException {
+ String serialized = objectMapper.writeValueAsString(wrapper);
+ JSONAssert.assertEquals(json, serialized, JSONCompareMode.STRICT);
+ PriceDataWrapper deserialized = objectMapper.readValue(serialized, PriceDataWrapper.class);
+ Assertions.assertThat(deserialized).isEqualTo(wrapper);
+ }
+}
\ No newline at end of file
diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/metadata/MetaLedgerEntryTypeTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/metadata/MetaLedgerEntryTypeTest.java
index 5333b30dd..20045d4e1 100644
--- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/metadata/MetaLedgerEntryTypeTest.java
+++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/metadata/MetaLedgerEntryTypeTest.java
@@ -9,6 +9,7 @@
import org.junit.jupiter.api.Test;
import org.xrpl.xrpl4j.model.AbstractJsonTest;
import org.xrpl.xrpl4j.model.ledger.AccountRootObject;
+import org.xrpl.xrpl4j.model.ledger.OracleObject;
class MetaLedgerEntryTypeTest extends AbstractJsonTest {
@@ -36,6 +37,7 @@ void testConstants() {
.isEqualTo("XChainOwnedCreateAccountClaimID");
assertThat(MetaLedgerEntryType.XCHAIN_OWNED_CLAIM_ID.value()).isEqualTo("XChainOwnedClaimID");
assertThat(MetaLedgerEntryType.DID.value()).isEqualTo("DID");
+ assertThat(MetaLedgerEntryType.ORACLE.value()).isEqualTo("Oracle");
}
@Test
@@ -64,6 +66,7 @@ void testLedgerObjectType() {
MetaXChainOwnedClaimIdObject.class
);
assertThat(MetaLedgerEntryType.DID.ledgerObjectType()).isEqualTo(MetaDidObject.class);
+ assertThat(MetaLedgerEntryType.ORACLE.ledgerObjectType()).isEqualTo(MetaOracleObject.class);
}
@Test
diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AbstractIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AbstractIT.java
index 261e938de..dcc1fdb97 100644
--- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AbstractIT.java
+++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AbstractIT.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.
@@ -68,6 +68,7 @@
import org.xrpl.xrpl4j.model.client.transactions.SubmitResult;
import org.xrpl.xrpl4j.model.client.transactions.TransactionRequestParams;
import org.xrpl.xrpl4j.model.client.transactions.TransactionResult;
+import org.xrpl.xrpl4j.model.flags.TrustSetFlags;
import org.xrpl.xrpl4j.model.ledger.LedgerObject;
import org.xrpl.xrpl4j.model.transactions.Address;
import org.xrpl.xrpl4j.model.transactions.Hash256;
@@ -77,7 +78,6 @@
import org.xrpl.xrpl4j.model.transactions.TransactionResultCodes;
import org.xrpl.xrpl4j.model.transactions.TransactionType;
import org.xrpl.xrpl4j.model.transactions.TrustSet;
-import org.xrpl.xrpl4j.model.transactions.XChainCreateBridge;
import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount;
import org.xrpl.xrpl4j.tests.environment.XrplEnvironment;
@@ -128,16 +128,13 @@ protected AbstractIT() {
protected void logInfo(TransactionType transactionType, Hash256 hash) {
String url = System.getProperty("useTestnet") != null ? "https://testnet.xrpl.org/transactions/" :
(System.getProperty("useDevnet") != null ? "https://devnet.xrpl.org/transactions/" : "");
- logger.info(transactionType.value() + " transaction successful: {}{}", url, hash);
+ logger.info("{} transaction successful: {}{}", transactionType.value(), url, hash);
}
protected KeyPair createRandomAccountEd25519() {
// Create the account
final KeyPair randomKeyPair = Seed.ed25519Seed().deriveKeyPair();
- logger.info(
- "Generated testnet wallet with ClassicAddress={})",
- randomKeyPair.publicKey().deriveAddress()
- );
+ logAccountCreation(randomKeyPair.publicKey().deriveAddress());
fundAccount(randomKeyPair.publicKey().deriveAddress());
@@ -147,10 +144,7 @@ protected KeyPair createRandomAccountEd25519() {
protected KeyPair createRandomAccountSecp256k1() {
// Create the account
final KeyPair randomKeyPair = Seed.secp256k1Seed().deriveKeyPair();
- logger.info(
- "Generated testnet wallet with ClassicAddress={})",
- randomKeyPair.publicKey().deriveAddress()
- );
+ logAccountCreation(randomKeyPair.publicKey().deriveAddress());
fundAccount(randomKeyPair.publicKey().deriveAddress());
@@ -171,7 +165,8 @@ public String keyIdentifier() {
};
PublicKey publicKey = derivedKeySignatureService.derivePublicKey(privateKeyReference);
- logger.info("Generated testnet wallet with ClassicAddress={})", publicKey.deriveAddress());
+ logAccountCreation(publicKey.deriveAddress());
+
fundAccount(publicKey.deriveAddress());
return privateKeyReference;
@@ -191,7 +186,8 @@ public String keyIdentifier() {
};
PublicKey publicKey = derivedKeySignatureService.derivePublicKey(privateKeyReference);
- logger.info("Generated testnet wallet with ClassicAddress={})", publicKey.deriveAddress());
+ logAccountCreation(publicKey.deriveAddress());
+
fundAccount(publicKey.deriveAddress());
return privateKeyReference;
@@ -425,28 +421,47 @@ protected Instant xrpTimestampToInstant(UnsignedLong xrpTimeStamp) {
}
/**
- * Create a trustline between the given issuer and counterparty accounts for the given currency code and with the
- * given limit.
+ * Create a trustline between the issuer of the specified {@param trustlineLimitAmount} and specified counterparty for
+ * the given currency code with the given limit.
*
- * @param currency The currency code of the trustline to create.
- * @param value The trustline limit of the trustline to create.
- * @param issuerKeyPair The {@link KeyPair} of the issuer account.
- * @param counterpartyKeyPair The {@link KeyPair} of the counterparty account.
- * @param fee The current network fee, as an {@link XrpCurrencyAmount}.
+ * @param counterpartyKeyPair The {@link KeyPair} of the counterparty account.
+ * @param trustlineLimitAmount A {@link IssuedCurrencyAmount} representing the trust limit for the counterparty.
+ * @param fee The current network fee, as an {@link XrpCurrencyAmount}.
*
* @return The {@link TrustLine} that gets created.
*
* @throws JsonRpcClientErrorException If anything goes wrong while communicating with rippled.
*/
public TrustLine createTrustLine(
- String currency,
- String value,
- KeyPair issuerKeyPair,
KeyPair counterpartyKeyPair,
+ IssuedCurrencyAmount trustlineLimitAmount,
XrpCurrencyAmount fee
+ ) throws JsonRpcClientErrorException, JsonProcessingException {
+ return createTrustLine(
+ counterpartyKeyPair, trustlineLimitAmount, fee, TrustSetFlags.builder().tfSetNoRipple().build()
+ );
+ }
+
+ /**
+ * Create a trustline between the issuer of the specified {@param trustlineLimitAmount} and specified counterparty for
+ * the given currency code with the given limit.
+ *
+ * @param counterpartyKeyPair The {@link KeyPair} of the counterparty account.
+ * @param trustlineLimitAmount A {@link IssuedCurrencyAmount} representing the trust limit for the counterparty.
+ * @param fee The current network fee, as an {@link XrpCurrencyAmount}.
+ * @param trustSetFlags A {@link TrustSetFlags} to use when creating the trustline.
+ *
+ * @return The {@link TrustLine} that gets created.
+ *
+ * @throws JsonRpcClientErrorException If anything goes wrong while communicating with rippled.
+ */
+ public TrustLine createTrustLine(
+ KeyPair counterpartyKeyPair,
+ IssuedCurrencyAmount trustlineLimitAmount,
+ XrpCurrencyAmount fee,
+ TrustSetFlags trustSetFlags
) throws JsonRpcClientErrorException, JsonProcessingException {
Address counterpartyAddress = counterpartyKeyPair.publicKey().deriveAddress();
- Address issuerAddress = issuerKeyPair.publicKey().deriveAddress();
AccountInfoResult counterpartyAccountInfo = this.scanForResult(
() -> this.getValidatedAccountInfo(counterpartyAddress)
@@ -456,11 +471,8 @@ public TrustLine createTrustLine(
.account(counterpartyAddress)
.fee(fee)
.sequence(counterpartyAccountInfo.accountData().sequence())
- .limitAmount(IssuedCurrencyAmount.builder()
- .currency(currency)
- .issuer(issuerAddress)
- .value(value)
- .build())
+ .limitAmount(trustlineLimitAmount)
+ .flags(trustSetFlags)
.signingPublicKey(counterpartyKeyPair.publicKey())
.build();
@@ -477,72 +489,138 @@ public TrustLine createTrustLine(
);
return scanForResult(
- () ->
- getValidatedAccountLines(issuerAddress, counterpartyAddress),
+ () -> getValidatedAccountLines(trustlineLimitAmount.issuer(), counterpartyAddress),
linesResult -> !linesResult.lines().isEmpty()
- )
- .lines().get(0);
+ ).lines().get(0);
}
/**
- * Send issued currency funds from an issuer to a counterparty.
+ * Send issued currency funds from a sender to a receiver.
*
- * @param currency The currency code to send.
- * @param value The amount of currency to send.
- * @param issuerKeyPair The {@link KeyPair} of the issuer account.
- * @param counterpartyKeyPair The {@link KeyPair} of the counterparty account.
- * @param fee The current network fee, as an {@link XrpCurrencyAmount}.
+ * @param senderKeyPair The {@link KeyPair} of the payment sender.
+ * @param receiverKeyPair The {@link KeyPair} of the payment receiver.
+ * @param amount An {@link IssuedCurrencyAmount} to send.
+ * @param fee The current network fee, as an {@link XrpCurrencyAmount}.
*
* @throws JsonRpcClientErrorException If anything goes wrong while communicating with rippled.
*/
public void sendIssuedCurrency(
- String currency,
- String value,
- KeyPair issuerKeyPair,
- KeyPair counterpartyKeyPair,
+ KeyPair senderKeyPair,
+ KeyPair receiverKeyPair,
+ IssuedCurrencyAmount amount,
XrpCurrencyAmount fee
) throws JsonRpcClientErrorException, JsonProcessingException {
- Address counterpartyAddress = counterpartyKeyPair.publicKey().deriveAddress();
- Address issuerAddress = issuerKeyPair.publicKey().deriveAddress();
+ sendIssuedCurrency(senderKeyPair, receiverKeyPair, amount, fee, TransactionResultCodes.TES_SUCCESS);
+ }
- ///////////////////////////
- // Issuer sends a payment with the issued currency to the counterparty
- AccountInfoResult issuerAccountInfo = this.scanForResult(
- () -> getValidatedAccountInfo(issuerAddress)
- );
+ /**
+ * Send issued currency funds from a sender to a receiver.
+ *
+ * @param senderKeyPair The {@link KeyPair} of the payment sender.
+ * @param receiverKeyPair The {@link KeyPair} of the payment receiver.
+ * @param amount An {@link IssuedCurrencyAmount} to send.
+ * @param fee The current network fee, as an {@link XrpCurrencyAmount}.
+ * @param expectedEngineResult The expected engine result.
+ *
+ * @throws JsonRpcClientErrorException If anything goes wrong while communicating with rippled.
+ */
+ public void sendIssuedCurrency(
+ KeyPair senderKeyPair,
+ KeyPair receiverKeyPair,
+ IssuedCurrencyAmount amount,
+ XrpCurrencyAmount fee,
+ String expectedEngineResult
+ ) throws JsonRpcClientErrorException, JsonProcessingException {
+ Objects.requireNonNull(senderKeyPair);
+ Objects.requireNonNull(receiverKeyPair);
+ Objects.requireNonNull(amount);
+ Objects.requireNonNull(fee);
+ Objects.requireNonNull(expectedEngineResult);
+
+ final Address senderAddress = senderKeyPair.publicKey().deriveAddress();
+ final Address receiverAddress = receiverKeyPair.publicKey().deriveAddress();
+
+ int loopGuard = 0;
+ String paymentEngineResult = null;
+
+ while (!expectedEngineResult.equalsIgnoreCase(paymentEngineResult)) {
+ if (loopGuard++ > 30) {
+ throw new RuntimeException(
+ String.format("engineResult should have been `%s`, but was `%s` instead",
+ expectedEngineResult, paymentEngineResult
+ )
+ );
+ }
- Payment fundCounterparty = Payment.builder()
- .account(issuerAddress)
- .fee(fee)
- .sequence(issuerAccountInfo.accountData().sequence())
- .destination(counterpartyAddress)
- .amount(IssuedCurrencyAmount.builder()
- .issuer(issuerAddress)
- .currency(currency)
- .value(value)
- .build())
- .signingPublicKey(issuerKeyPair.publicKey())
- .build();
+ ///////////////////////////
+ // Sender sends a payment with the issued currency to the counterparty
+ AccountInfoResult senderAccountInfo = this.scanForResult(
+ () -> getValidatedAccountInfo(senderAddress)
+ );
+ UnsignedInteger currentSenderSequence = senderAccountInfo.accountData().sequence();
+ logger.info("About to send a payment on Sequence={}", currentSenderSequence);
+
+ Payment fundCounterparty = Payment.builder()
+ .account(senderAddress)
+ .fee(fee)
+ .sequence(senderAccountInfo.accountData().sequence())
+ .destination(receiverAddress)
+ .amount(amount)
+ .signingPublicKey(senderKeyPair.publicKey())
+ .build();
- SingleSignedTransaction signedPayment = signatureService.sign(
- issuerKeyPair.privateKey(),
- fundCounterparty
- );
- SubmitResult paymentResult = xrplClient.submit(signedPayment);
- assertThat(paymentResult.engineResult()).isEqualTo(TransactionResultCodes.TES_SUCCESS);
- assertThat(paymentResult.transactionResult().hash()).isEqualTo(signedPayment.hash());
+ SingleSignedTransaction signedPayment = signatureService.sign(
+ senderKeyPair.privateKey(), fundCounterparty
+ );
+ SubmitResult paymentResult = xrplClient.submit(signedPayment);
+ assertThat(paymentResult.transactionResult().hash()).isEqualTo(signedPayment.hash());
+
+ paymentEngineResult = paymentResult.engineResult();
+ if (!paymentResult.engineResult().equals(expectedEngineResult)) {
+ try {
+ // If the code gets here, it means the transaction did not succeed. The most typical reason here is a latent
+ // Clio node (see description at the end of this function). This loop allows the code to retry for just a bit
+ // longer than the current 60s DNS TTL.
+ Thread.sleep(3000); // <-- Sleep for 3 seconds and try again
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ logger.error(
+ "PaymentEngineResult `{}` did not equal expectedEngineResult `{}`", paymentEngineResult, expectedEngineResult
+ );
+ continue; // <-- Try again, up to the loop guard above.
+ }
- logInfo(
- paymentResult.transactionResult().transaction().transactionType(),
- paymentResult.transactionResult().hash()
- );
+ logInfo(
+ paymentResult.transactionResult().transaction().transactionType(),
+ paymentResult.transactionResult().hash()
+ );
- this.scanForResult(
- () -> getValidatedTransaction(
- paymentResult.transactionResult().hash(),
- Payment.class)
- );
+ this.scanForResult(
+ () -> getValidatedTransaction(
+ paymentResult.transactionResult().hash(),
+ Payment.class)
+ );
+ // This extra check exists for Clio servers. Occasionally, one Clio server in the cluster will report that a TX
+ // (e.g., with sequence 5) is `VALIDATED`. Subsequent calls to `account_info` should return an account sequence
+ // number of 6, but sometimes one of the servers in the cluster will return the old sequence value of 5. This
+ // scanner simply waits until at least one of the Clio server reports the correct account_sequence number so that
+ // subsequent calls to `account_info` will typically have the correct account sequence. Note that this solution
+ // will _mostly_ work, but not always. Consider an example with three Clio nodes in a cluster. Node A is latent,
+ // but B & C are not (i.e., B & C have an up-to-date account sequence). In this instance, this scanner might
+ // receive a result from B or C, but on the next payment, this code might get a response from the (incorrect)
+ // node A. In reality, this should _almost_ never happen because the current testnet DNS configuration
+ // pegs JSON-RPC clients to a single IP address (i.e., single clio server) for 60s. Therefore, there should
+ // only be very tiny windows where this solution does not fix the issue. For that, we have the loop above as
+ // well to retry.
+ this.scanForResult(
+ () -> getValidatedAccountInfo(senderAddress),
+ result -> result.accountData().sequence().equals(
+ senderAccountInfo.accountData().sequence().plus(UnsignedInteger.ONE)
+ )
+ );
+ }
}
//////////////////
@@ -603,10 +681,7 @@ protected SignatureService constructSignatureService() {
protected KeyPair constructRandomAccount() {
// Create the account
final KeyPair randomKeyPair = Seed.ed25519Seed().deriveKeyPair();
- logger.info(
- "Generated testnet wallet with ClassicAddress={})",
- randomKeyPair.publicKey().deriveAddress()
- );
+ logAccountCreation(randomKeyPair.publicKey().deriveAddress());
fundAccount(randomKeyPair.publicKey().deriveAddress());
@@ -627,4 +702,8 @@ private KeyStore loadKeyStore() {
final char[] jksPassword = "password".toCharArray();
return JavaKeystoreLoader.loadFromClasspath(jksFileName, jksPassword);
}
+
+ private void logAccountCreation(Address address) {
+ logger.info("Generated wallet with ClassicAddress={})", address);
+ }
}
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 b908e9287..8a6799491 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
@@ -144,8 +144,8 @@ void depositAndVoteOnTradingFee() throws JsonRpcClientErrorException, JsonProces
TradingFee expectedTradingFee = TradingFee.ofPercent(
issuerLpTokenBalance.multiply(amm.amm().tradingFee().bigDecimalValue()).add(
traderLpTokenBalance.multiply(newTradingFee.bigDecimalValue())
- ).divide(issuerLpTokenBalance.add(traderLpTokenBalance), RoundingMode.FLOOR)
- .setScale(3, RoundingMode.FLOOR)
+ ).divide(issuerLpTokenBalance.add(traderLpTokenBalance), RoundingMode.HALF_UP)
+ .setScale(3, RoundingMode.HALF_UP)
);
AmmInfoResult ammAfterVote = getAmmInfo(issuerKeyPair);
@@ -255,7 +255,7 @@ void depositAndWithdraw() throws JsonRpcClientErrorException, JsonProcessingExce
.build()
)
.asset(Issue.XRP)
- .amount(XrpCurrencyAmount.ofXrp(BigDecimal.valueOf(90)))
+ .amount(XrpCurrencyAmount.ofXrp(BigDecimal.valueOf(9)))
.flags(AmmWithdrawFlags.SINGLE_ASSET)
.build();
@@ -296,7 +296,7 @@ private AccountInfoResult depositXrp(
SignatureService signatureService,
FeeResult feeResult
) throws JsonRpcClientErrorException, JsonProcessingException {
- XrpCurrencyAmount depositAmount = XrpCurrencyAmount.ofXrp(BigDecimal.valueOf(100));
+ XrpCurrencyAmount depositAmount = XrpCurrencyAmount.ofXrp(BigDecimal.valueOf(10));
AmmDeposit deposit = AmmDeposit.builder()
.account(traderAccount.accountData().account())
.asset2(
@@ -373,10 +373,10 @@ private AmmInfoResult createAmm(
IssuedCurrencyAmount.builder()
.issuer(issuerKeyPair.publicKey().deriveAddress())
.currency(xrpl4jCoin)
- .value("25")
+ .value("2.5")
.build()
)
- .amount2(XrpCurrencyAmount.ofXrp(BigDecimal.valueOf(100)))
+ .amount2(XrpCurrencyAmount.ofXrp(BigDecimal.valueOf(10)))
.tradingFee(TradingFee.ofPercent(BigDecimal.ONE))
.lastLedgerSequence(issuerAccount.ledgerIndexSafe().plus(UnsignedInteger.valueOf(4)).unsignedIntegerValue())
.signingPublicKey(issuerKeyPair.publicKey())
diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/ClawbackIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/ClawbackIT.java
index ace2e32f1..fc1ae9abc 100644
--- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/ClawbackIT.java
+++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/ClawbackIT.java
@@ -45,24 +45,28 @@ void issueBalanceAndClawback() throws JsonRpcClientErrorException, JsonProcessin
setAllowClawback(issuerKeyPair, issuerAccount, fee);
createTrustLine(
- "USD",
- "10000",
- issuerKeyPair,
holderKeyPair,
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .currency("USD")
+ .value("10000")
+ .build(),
fee
);
sendIssuedCurrency(
- "USD",
- "100",
issuerKeyPair,
holderKeyPair,
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .currency("USD")
+ .value("100")
+ .build(),
fee
);
issuerAccount = this.getValidatedAccountInfo(issuerAccount.accountData().account());
clawback(
- "USD",
"10",
holderKeyPair.publicKey().deriveAddress(),
issuerKeyPair,
@@ -82,7 +86,6 @@ void issueBalanceAndClawback() throws JsonRpcClientErrorException, JsonProcessin
issuerAccount = this.getValidatedAccountInfo(issuerAccount.accountData().account());
clawback(
- "USD",
"90",
holderKeyPair.publicKey().deriveAddress(),
issuerKeyPair,
@@ -101,7 +104,6 @@ void issueBalanceAndClawback() throws JsonRpcClientErrorException, JsonProcessin
}
private void clawback(
- String currencyCode,
String amount,
Address holderAddress,
KeyPair issuerKeyPair,
@@ -115,7 +117,7 @@ private void clawback(
.signingPublicKey(issuerKeyPair.publicKey())
.amount(
IssuedCurrencyAmount.builder()
- .currency(currencyCode)
+ .currency("USD")
.value(amount)
.issuer(holderAddress)
.build()
@@ -130,7 +132,7 @@ private void clawback(
scanForFinality(
signedClawback.hash(),
issuerAccountInfo.ledgerIndexSafe(),
- clawback.lastLedgerSequence().get(),
+ clawback.lastLedgerSequence().orElseThrow(() -> new RuntimeException("Clawback lacked lastLedgerSequence")),
clawback.sequence(),
clawback.account()
);
@@ -159,7 +161,7 @@ private void setAllowClawback(
scanForFinality(
signedAccountSet.hash(),
issuerAccount.ledgerIndexSafe(),
- accountSet.lastLedgerSequence().get(),
+ accountSet.lastLedgerSequence().orElseThrow(() -> new RuntimeException("AccountSet lacked lastLedgerSequence")),
accountSet.sequence(),
accountSet.account()
);
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 5d34919b7..c112b6009 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
@@ -504,18 +504,25 @@ 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);
+ EscrowObject escrowObject = (EscrowObject) this.scanForResult(
+ () -> {
+ try {
+ return xrplClient.accountObjects(AccountObjectsRequestParams.builder()
+ .type(AccountObjectType.ESCROW)
+ .account(escrowOwner)
+ .ledgerSpecifier(LedgerSpecifier.VALIDATED)
+ .build()
+ ).accountObjects();
+ } catch (JsonRpcClientErrorException e) {
+ throw new RuntimeException(e);
+ }
+ },
+ result -> result.size() == 1
+ ).get(0);
LedgerEntryResult escrowEntry = xrplClient.ledgerEntry(
LedgerEntryRequestParams.escrow(
diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/FreezeIssuedCurrencyIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/FreezeIssuedCurrencyIT.java
index 613e0fbd5..920dbb619 100644
--- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/FreezeIssuedCurrencyIT.java
+++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/FreezeIssuedCurrencyIT.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.
@@ -21,6 +21,8 @@
*/
import static org.assertj.core.api.Assertions.assertThat;
+import static org.xrpl.xrpl4j.model.transactions.TransactionResultCodes.TEC_PATH_DRY;
+import static org.xrpl.xrpl4j.model.transactions.TransactionResultCodes.TES_SUCCESS;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.Strings;
@@ -41,14 +43,12 @@
import org.xrpl.xrpl4j.model.transactions.AccountSet.AccountSetFlag;
import org.xrpl.xrpl4j.model.transactions.ImmutableAccountSet;
import org.xrpl.xrpl4j.model.transactions.IssuedCurrencyAmount;
-import org.xrpl.xrpl4j.model.transactions.Payment;
import org.xrpl.xrpl4j.model.transactions.TrustSet;
import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount;
/**
* An Integration Test to validate that an amount of issued currency held by a bad actor can be "frozen."
*/
-@SuppressWarnings("deprecation")
public class FreezeIssuedCurrencyIT extends AbstractIT {
private static final String TEN_THOUSAND = "10000";
@@ -85,9 +85,13 @@ public void issueAndFreezeFundsIndividual() throws JsonRpcClientErrorException,
FeeResult feeResult = xrplClient.fee();
// Create a Trust Line between issuer and the bad actor.
- TrustLine badActorTrustLine = this.createTrustLine(
- issuerKeyPair,
+ TrustLine badActorTrustLine = createTrustLine(
badActorKeyPair,
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .currency(ISSUED_CURRENCY_CODE)
+ .value(TEN_THOUSAND)
+ .build(),
FeeUtils.computeNetworkFees(feeResult).recommendedFee()
);
assertThat(badActorTrustLine.freeze()).isFalse();
@@ -98,8 +102,12 @@ public void issueAndFreezeFundsIndividual() throws JsonRpcClientErrorException,
///////////////////////////
// Create a Trust Line between issuer and the good actor.
TrustLine goodActorTrustLine = this.createTrustLine(
- issuerKeyPair,
goodActorKeyPair,
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .currency(ISSUED_CURRENCY_CODE)
+ .value(TEN_THOUSAND)
+ .build(),
FeeUtils.computeNetworkFees(feeResult).recommendedFee()
);
assertThat(goodActorTrustLine.freeze()).isFalse();
@@ -111,9 +119,23 @@ public void issueAndFreezeFundsIndividual() throws JsonRpcClientErrorException,
// Send Funds
/////////////
- // Send funds from issuer to the badActor.
- sendFunds(
- TEN_THOUSAND, issuerKeyPair, badActorKeyPair, FeeUtils.computeNetworkFees(feeResult).recommendedFee()
+ logger.info("Send ${} from issuer ({}) to the badActor ({}) and expect {}",
+ TEN_THOUSAND,
+ issuerKeyPair.publicKey().deriveAddress(),
+ goodActorKeyPair.publicKey().deriveAddress(),
+ TES_SUCCESS
+ );
+
+ sendIssuedCurrency(
+ issuerKeyPair, // <-- From
+ badActorKeyPair, // <-- To
+ IssuedCurrencyAmount.builder()
+ .currency(FreezeIssuedCurrencyIT.ISSUED_CURRENCY_CODE)
+ .value(TEN_THOUSAND)
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .build(),
+ FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
+ TES_SUCCESS
);
///////////////////////////
@@ -125,9 +147,22 @@ public void issueAndFreezeFundsIndividual() throws JsonRpcClientErrorException,
.anyMatch(line -> line.balance().equals("-" + TEN_THOUSAND))
);
- // Send funds from badActor to the goodActor.
- sendFunds(
- FIVE_THOUSAND, badActorKeyPair, goodActorKeyPair, FeeUtils.computeNetworkFees(feeResult).recommendedFee()
+ logger.info("Send ${} from badActor ({}) to the goodActor ({}) and expect {}",
+ FIVE_THOUSAND,
+ badActorKeyPair.publicKey().deriveAddress(),
+ goodActorKeyPair.publicKey().deriveAddress(),
+ TES_SUCCESS
+ );
+
+ sendIssuedCurrency(
+ badActorKeyPair, // <-- From
+ goodActorKeyPair, // <-- To
+ IssuedCurrencyAmount.builder()
+ .currency(FreezeIssuedCurrencyIT.ISSUED_CURRENCY_CODE)
+ .value(FIVE_THOUSAND)
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .build(),
+ FeeUtils.computeNetworkFees(feeResult).recommendedFee()
);
///////////////////////////
@@ -159,20 +194,62 @@ public void issueAndFreezeFundsIndividual() throws JsonRpcClientErrorException,
// 2) The counterparty can only send the frozen currencies directly to the issuer (no where else)
// 3) The counterparty can still receive payments from others on the frozen trust line.
- // Try to send funds from badActor to goodActor should not work because the badActor is frozen.
- sendFunds(
- FIVE_THOUSAND, badActorKeyPair, goodActorKeyPair, FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
- "tecPATH_DRY"
+ logger.info("Send ${} from badActor ({}) to the goodActor ({}) and expect {}",
+ FIVE_THOUSAND,
+ badActorKeyPair.publicKey().deriveAddress(),
+ goodActorKeyPair.publicKey().deriveAddress(),
+ TEC_PATH_DRY
+ );
+
+ sendIssuedCurrency(
+ badActorKeyPair, // <-- From
+ goodActorKeyPair, // <-- To
+ IssuedCurrencyAmount.builder()
+ .currency(FreezeIssuedCurrencyIT.ISSUED_CURRENCY_CODE)
+ .value(FIVE_THOUSAND)
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .build(),
+ FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
+ TEC_PATH_DRY
);
// Sending from the badActor to the issuer should still work
- sendFunds(
- FIVE_THOUSAND, badActorKeyPair, issuerKeyPair, FeeUtils.computeNetworkFees(feeResult).recommendedFee()
+ logger.info("Send ${} from badActor ({}) to the issuer ({}) and expect {}",
+ FIVE_THOUSAND,
+ badActorKeyPair.publicKey().deriveAddress(),
+ issuerKeyPair.publicKey().deriveAddress(),
+ TEC_PATH_DRY
+ );
+
+ sendIssuedCurrency(
+ badActorKeyPair, // <-- From
+ issuerKeyPair, // <-- To
+ IssuedCurrencyAmount.builder()
+ .currency(FreezeIssuedCurrencyIT.ISSUED_CURRENCY_CODE)
+ .value(FIVE_THOUSAND)
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .build(),
+ FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
+ TEC_PATH_DRY
);
// Sending from the goodActor to the badActor should still work
- sendFunds(
- FIVE_THOUSAND, goodActorKeyPair, badActorKeyPair, FeeUtils.computeNetworkFees(feeResult).recommendedFee()
+ logger.info("Send ${} from goodActor ({}) to the badActor ({}) and expect {}",
+ FIVE_THOUSAND,
+ goodActorKeyPair.publicKey().deriveAddress(),
+ badActorKeyPair.publicKey().deriveAddress(),
+ TEC_PATH_DRY
+ );
+ sendIssuedCurrency(
+ goodActorKeyPair, // <-- From
+ badActorKeyPair, // <-- To
+ IssuedCurrencyAmount.builder()
+ .currency(FreezeIssuedCurrencyIT.ISSUED_CURRENCY_CODE)
+ .value(FIVE_THOUSAND)
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .build(),
+ FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
+ TEC_PATH_DRY
);
// Unfreeze the bad actor.
@@ -201,8 +278,12 @@ public void issueAndFreezeFundsGlobal() throws JsonRpcClientErrorException, Json
// Create a Trust Line between issuer and the bad actor.
TrustLine badActorTrustLine = this.createTrustLine(
- issuerKeyPair,
badActorKeyPair,
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .currency(ISSUED_CURRENCY_CODE)
+ .value(TEN_THOUSAND)
+ .build(),
FeeUtils.computeNetworkFees(feeResult).recommendedFee()
);
assertThat(badActorTrustLine.freeze()).isFalse();
@@ -213,8 +294,12 @@ public void issueAndFreezeFundsGlobal() throws JsonRpcClientErrorException, Json
///////////////////////////
// Create a Trust Line between issuer and the good actor.
TrustLine goodActorTrustLine = this.createTrustLine(
- issuerKeyPair,
goodActorKeyPair,
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .currency(ISSUED_CURRENCY_CODE)
+ .value(TEN_THOUSAND)
+ .build(),
FeeUtils.computeNetworkFees(feeResult).recommendedFee()
);
assertThat(goodActorTrustLine.freeze()).isFalse();
@@ -226,9 +311,22 @@ public void issueAndFreezeFundsGlobal() throws JsonRpcClientErrorException, Json
// Send Funds
/////////////
- // Send funds from issuer to the badActor.
- sendFunds(
- TEN_THOUSAND, issuerKeyPair, badActorKeyPair, FeeUtils.computeNetworkFees(feeResult).recommendedFee()
+ logger.info("Send ${} from issuer ({}) to the badActor ({}) and expect {}",
+ TEN_THOUSAND,
+ issuerKeyPair.publicKey().deriveAddress(),
+ badActorKeyPair.publicKey().deriveAddress(),
+ TES_SUCCESS
+ );
+ sendIssuedCurrency(
+ issuerKeyPair, // <-- From
+ badActorKeyPair, // <-- To
+ IssuedCurrencyAmount.builder()
+ .currency(FreezeIssuedCurrencyIT.ISSUED_CURRENCY_CODE)
+ .value(TEN_THOUSAND)
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .build(),
+ FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
+ TES_SUCCESS
);
///////////////////////////
@@ -240,20 +338,36 @@ public void issueAndFreezeFundsGlobal() throws JsonRpcClientErrorException, Json
.anyMatch(line -> line.balance().equals("-" + TEN_THOUSAND))
);
- // Send funds from badActor to the goodActor.
- sendFunds(
- FIVE_THOUSAND, badActorKeyPair, goodActorKeyPair, FeeUtils.computeNetworkFees(feeResult).recommendedFee()
+ logger.info("Send ${} from badActor ({}) to the goodActor ({}) and expect {}",
+ FIVE_THOUSAND,
+ badActorKeyPair.publicKey().deriveAddress(),
+ goodActorKeyPair.publicKey().deriveAddress(),
+ TES_SUCCESS
+ );
+ sendIssuedCurrency(
+ badActorKeyPair, // <-- From
+ goodActorKeyPair, // <-- To
+ IssuedCurrencyAmount.builder()
+ .currency(FreezeIssuedCurrencyIT.ISSUED_CURRENCY_CODE)
+ .value(FIVE_THOUSAND)
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .build(),
+ FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
+ TES_SUCCESS
);
///////////////////////////
// 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(),
- goodActorKeyPair.publicKey().deriveAddress()),
+ this.scanForResult(() -> getValidatedAccountLines(
+ issuerKeyPair.publicKey().deriveAddress(), goodActorKeyPair.publicKey().deriveAddress()
+ ),
linesResult -> linesResult.lines().stream()
.anyMatch(line -> line.balance().equals("-" + FIVE_THOUSAND))
);
+ logger.info("Globally freeze trustline for issuer ({})", issuerKeyPair.publicKey().deriveAddress());
+
// Global-Freeze the trustline for the issuer.
AccountInfoResult issuerAccountInfo = this.adjustGlobalTrustlineFreeze(
issuerKeyPair,
@@ -269,170 +383,125 @@ public void issueAndFreezeFundsGlobal() throws JsonRpcClientErrorException, Json
// 1) The counterparty can only send the frozen currencies directly to the issuer (no where else)
// 2) The counterparty can still receive payments from others on the frozen trust line.
- // Try to send funds from badActor to goodActor should not work because the badActor is frozen.
- sendFunds(
- "500", badActorKeyPair, goodActorKeyPair, FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
- "tecPATH_DRY"
+ logger.info("Send ${} from badActor ({}) to the goodActor ({}) and expect {}",
+ "500",
+ badActorKeyPair.publicKey().deriveAddress(),
+ goodActorKeyPair.publicKey().deriveAddress(),
+ TEC_PATH_DRY
);
- // Sending from the goodActor to the badActor should not work
- sendFunds(
- "500", goodActorKeyPair, badActorKeyPair, FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
- "tecPATH_DRY"
+ sendIssuedCurrency(
+ badActorKeyPair, // <-- From
+ goodActorKeyPair, // <-- To
+ IssuedCurrencyAmount.builder()
+ .currency(FreezeIssuedCurrencyIT.ISSUED_CURRENCY_CODE)
+ .value("500")
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .build(),
+ FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
+ TEC_PATH_DRY
);
- // Try to send funds from issuer to goodActor should work per
- // https://xrpl.org/enact-global-freeze.html#intermission-while-frozen
- sendFunds(
- "100", issuerKeyPair, goodActorKeyPair, FeeUtils.computeNetworkFees(feeResult).recommendedFee()
+ logger.info("Send ${} from goodActor ({}) to the badActor ({}) and expect {}",
+ "500",
+ goodActorKeyPair.publicKey().deriveAddress(),
+ badActorKeyPair.publicKey().deriveAddress(),
+ TEC_PATH_DRY
);
-
- // Try to send funds from issuer to badActor should work per
- // https://xrpl.org/enact-global-freeze.html#intermission-while-frozen
- sendFunds(
- "100", issuerKeyPair, badActorKeyPair, FeeUtils.computeNetworkFees(feeResult).recommendedFee()
+ sendIssuedCurrency(
+ goodActorKeyPair, // <-- From
+ badActorKeyPair, // <-- To
+ IssuedCurrencyAmount.builder()
+ .currency(FreezeIssuedCurrencyIT.ISSUED_CURRENCY_CODE)
+ .value("500")
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .build(),
+ FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
+ TEC_PATH_DRY
);
- // Try to send funds from issuer to goodActor should work per
- // https://xrpl.org/enact-global-freeze.html#intermission-while-frozen
- sendFunds(
- FIVE_THOUSAND, badActorKeyPair, issuerKeyPair, FeeUtils.computeNetworkFees(feeResult).recommendedFee()
+ // Note: The following should work per https://xrpl.org/enact-global-freeze.html#intermission-while-frozen).
+ logger.info("Send ${} from issuer ({}) to the goodActor ({}) and expect {}",
+ "100",
+ issuerKeyPair.publicKey().deriveAddress(),
+ goodActorKeyPair.publicKey().deriveAddress(),
+ TES_SUCCESS
);
-
- // Try to send funds from issuer to goodActor should work per
- // https://xrpl.org/enact-global-freeze.html#intermission-while-frozen
- sendFunds(
- FIVE_THOUSAND, goodActorKeyPair, issuerKeyPair, FeeUtils.computeNetworkFees(feeResult).recommendedFee()
+ sendIssuedCurrency(
+ issuerKeyPair, // <-- From
+ goodActorKeyPair, // <-- To
+ IssuedCurrencyAmount.builder()
+ .currency(FreezeIssuedCurrencyIT.ISSUED_CURRENCY_CODE)
+ .value("100")
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .build(),
+ FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
+ TES_SUCCESS
);
- // Unfreeze the bad actor.
- issuerAccountInfo = this.adjustGlobalTrustlineFreeze(
- issuerKeyPair,
+ // Note: The following should work per https://xrpl.org/enact-global-freeze.html#intermission-while-frozen).
+ logger.info("Send ${} from issuer ({}) to the badActor ({}) and expect {}",
+ "100",
+ issuerKeyPair.publicKey().deriveAddress(),
+ badActorKeyPair.publicKey().deriveAddress(),
+ TES_SUCCESS
+ );
+ sendIssuedCurrency(
+ issuerKeyPair, // <-- From
+ badActorKeyPair, // <-- To
+ IssuedCurrencyAmount.builder()
+ .currency(FreezeIssuedCurrencyIT.ISSUED_CURRENCY_CODE)
+ .value("100")
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .build(),
FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
- UN_FREEZE
+ TES_SUCCESS
);
- assertThat(issuerAccountInfo.accountData().flags().lsfGlobalFreeze()).isFalse();
- }
- /**
- * Send issued currency funds from an issuer to a badActor.
- *
- * @param value The amount of currency to send.
- * @param sender The {@link KeyPair} of the sender.
- * @param recipient The {@link KeyPair} of the recipient.
- * @param fee The current network fee, as an {@link XrpCurrencyAmount}.
- *
- * @throws JsonRpcClientErrorException If anything goes wrong while communicating with rippled.
- * @throws JsonProcessingException If there are any problems parsing JSON.
- */
- private void sendFunds(
- String value,
- KeyPair sender,
- KeyPair recipient,
- XrpCurrencyAmount fee
- ) throws JsonRpcClientErrorException, JsonProcessingException {
- this.sendFunds(value, sender, recipient, fee, "tesSUCCESS");
- }
-
- /**
- * Send issued currency funds from an issuer to a badActor.
- *
- * @param valueToSend The amount of currency to send.
- * @param sender The {@link KeyPair} of the sender.
- * @param recipient The {@link KeyPair} of the recipient.
- * @param fee The current network fee, as an {@link XrpCurrencyAmount}.
- * @param expectedResultCode The expected result code after submitting a payment transaction.
- *
- * @throws JsonRpcClientErrorException If anything goes wrong while communicating with rippled.
- * @throws JsonProcessingException If there are any problems parsing JSON.
- */
- private void sendFunds(
- String valueToSend,
- KeyPair sender,
- KeyPair recipient,
- XrpCurrencyAmount fee,
- String expectedResultCode
- ) throws JsonRpcClientErrorException, JsonProcessingException {
- AccountInfoResult senderAccountInfo = this.scanForResult(
- () -> getValidatedAccountInfo(sender.publicKey().deriveAddress())
+ // Note: The following should work per https://xrpl.org/enact-global-freeze.html#intermission-while-frozen).
+ logger.info("Send ${} from issuer ({}) to the badActor ({}) and expect {}",
+ FIVE_THOUSAND,
+ badActorKeyPair.publicKey().deriveAddress(),
+ issuerKeyPair.publicKey().deriveAddress(),
+ TES_SUCCESS
);
-
- Payment payment = Payment.builder()
- .account(sender.publicKey().deriveAddress())
- .fee(fee)
- .sequence(senderAccountInfo.accountData().sequence())
- .destination(recipient.publicKey().deriveAddress())
- .amount(IssuedCurrencyAmount.builder()
- .issuer(issuerKeyPair.publicKey().deriveAddress())
+ sendIssuedCurrency(
+ badActorKeyPair, // <-- From
+ issuerKeyPair, // <-- To
+ IssuedCurrencyAmount.builder()
.currency(FreezeIssuedCurrencyIT.ISSUED_CURRENCY_CODE)
- .value(valueToSend)
- .build())
- .signingPublicKey(sender.publicKey())
- .build();
-
- SingleSignedTransaction signedPayment = signatureService.sign(sender.privateKey(), payment);
- SubmitResult paymentResult = xrplClient.submit(signedPayment);
- assertThat(paymentResult.engineResult()).isEqualTo(expectedResultCode);
-
- if (expectedResultCode.equals("tesSUCCESS")) {
- logger.info(
- "Payment transaction: https://testnet.xrpl.org/transactions/{}", paymentResult.transactionResult().hash()
- );
- }
- this.scanForResult(() -> getValidatedTransaction(paymentResult.transactionResult().hash(), Payment.class));
- }
-
- /**
- * Create a trustline between the given issuer and badActor accounts for the given currency code and with the given
- * limit.
- *
- * @param issuerKeyPair The {@link KeyPair} of the issuer account.
- * @param counterpartKeyPair The {@link KeyPair} of the badActor account.
- * @param fee The current network fee, as an {@link XrpCurrencyAmount}.
- *
- * @return The {@link TrustLine} that gets created.
- *
- * @throws JsonRpcClientErrorException If anything goes wrong while communicating with rippled.
- * @throws JsonProcessingException If there are any problems parsing JSON.
- */
- private TrustLine createTrustLine(
- KeyPair issuerKeyPair,
- KeyPair counterpartKeyPair,
- XrpCurrencyAmount fee
- ) throws JsonRpcClientErrorException, JsonProcessingException {
- AccountInfoResult badActorAccountInfo = this.scanForResult(
- () -> this.getValidatedAccountInfo(counterpartKeyPair.publicKey().deriveAddress())
+ .value(FIVE_THOUSAND)
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .build(),
+ FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
+ TES_SUCCESS
);
- TrustSet trustSet = TrustSet.builder()
- .account(counterpartKeyPair.publicKey().deriveAddress())
- .fee(fee)
- .sequence(badActorAccountInfo.accountData().sequence())
- .limitAmount(IssuedCurrencyAmount.builder()
+ // Note: The following should work per https://xrpl.org/enact-global-freeze.html#intermission-while-frozen).
+ logger.info("Send ${} from issuer ({}) to the goodActor ({}) and expect {}",
+ "FIVE_THOUSAND",
+ goodActorKeyPair.publicKey().deriveAddress(),
+ issuerKeyPair.publicKey().deriveAddress(),
+ TES_SUCCESS
+ );
+ sendIssuedCurrency(
+ goodActorKeyPair, // <-- From
+ issuerKeyPair, // <-- To
+ IssuedCurrencyAmount.builder()
.currency(FreezeIssuedCurrencyIT.ISSUED_CURRENCY_CODE)
+ .value(FIVE_THOUSAND)
.issuer(issuerKeyPair.publicKey().deriveAddress())
- .value(FreezeIssuedCurrencyIT.TEN_THOUSAND)
- .build())
- .flags(TrustSetFlags.builder()
- .tfSetNoRipple()
- .build())
- .signingPublicKey(counterpartKeyPair.publicKey())
- .build();
-
- SingleSignedTransaction signedTrustSet = signatureService.sign(counterpartKeyPair.privateKey(), trustSet);
- SubmitResult trustSetSubmitResult = xrplClient.submit(signedTrustSet);
- assertThat(trustSetSubmitResult.engineResult()).isEqualTo("tesSUCCESS");
- logger.info(
- "TrustSet transaction successful: https://testnet.xrpl.org/transactions/{}",
- trustSetSubmitResult.transactionResult().hash()
+ .build(),
+ FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
+ TES_SUCCESS
);
- return scanForResult(
- () ->
- getValidatedAccountLines(issuerKeyPair.publicKey().deriveAddress(),
- counterpartKeyPair.publicKey().deriveAddress()),
- linesResult -> !linesResult.lines().isEmpty()
- )
- .lines().get(0);
+ // Unfreeze the bad actor.
+ issuerAccountInfo = this.adjustGlobalTrustlineFreeze(
+ issuerKeyPair,
+ FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
+ UN_FREEZE
+ );
+ assertThat(issuerAccountInfo.accountData().flags().lsfGlobalFreeze()).isFalse();
}
/**
diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/GatewayBalancesIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/GatewayBalancesIT.java
index 8fc278922..a685bdf10 100644
--- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/GatewayBalancesIT.java
+++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/GatewayBalancesIT.java
@@ -36,6 +36,7 @@
import org.xrpl.xrpl4j.model.client.accounts.TrustLine;
import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier;
import org.xrpl.xrpl4j.model.transactions.Address;
+import org.xrpl.xrpl4j.model.transactions.IssuedCurrencyAmount;
import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount;
import java.math.BigDecimal;
@@ -55,20 +56,25 @@ public void testGatewayBalances() throws JsonRpcClientErrorException, JsonProces
String xrpl4jCoin = Strings.padEnd(BaseEncoding.base16().encode("xrpl4jCoin".getBytes()), 40, '0');
TrustLine trustLine = createTrustLine(
- xrpl4jCoin,
- "10000",
- issuerKeyPair,
counterpartyKeyPair,
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .currency(xrpl4jCoin)
+ .value("10000")
+ .build(),
XrpCurrencyAmount.ofXrp(BigDecimal.valueOf(1))
);
///////////////////////////
// Send some xrpl4jCoin to the counterparty account.
sendIssuedCurrency(
- xrpl4jCoin,
- trustLine.limitPeer(),
- issuerKeyPair,
- counterpartyKeyPair,
+ issuerKeyPair, // <-- From
+ counterpartyKeyPair, // <-- To
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .currency(xrpl4jCoin)
+ .value(trustLine.limitPeer())
+ .build(),
XrpCurrencyAmount.ofXrp(BigDecimal.valueOf(1))
);
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 670135a9a..2334220cd 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,7 +25,6 @@
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;
@@ -43,7 +42,7 @@
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.flags.TrustSetFlags;
import org.xrpl.xrpl4j.model.ledger.LedgerObject;
import org.xrpl.xrpl4j.model.ledger.RippleStateObject;
import org.xrpl.xrpl4j.model.transactions.AccountSet;
@@ -51,8 +50,6 @@
import org.xrpl.xrpl4j.model.transactions.IssuedCurrencyAmount;
import org.xrpl.xrpl4j.model.transactions.PathStep;
import org.xrpl.xrpl4j.model.transactions.Payment;
-import org.xrpl.xrpl4j.model.transactions.TrustSet;
-import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount;
import org.xrpl.xrpl4j.tests.environment.DevnetEnvironment;
import org.xrpl.xrpl4j.tests.environment.TestnetEnvironment;
@@ -80,10 +77,12 @@ void createTrustlineWithMaxLimit() throws JsonRpcClientErrorException, JsonProce
String xrpl4jCoin = Strings.padEnd(BaseEncoding.base16().encode("xrpl4jCoin".getBytes()), 40, '0');
TrustLine trustLine = createTrustLine(
- xrpl4jCoin,
- IssuedCurrencyAmount.MAX_VALUE,
- issuerKeyPair,
counterpartyKeyPair,
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .currency(xrpl4jCoin)
+ .value(IssuedCurrencyAmount.MAX_VALUE)
+ .build(),
FeeUtils.computeNetworkFees(feeResult).recommendedFee()
);
@@ -111,10 +110,12 @@ void createTrustlineWithMaxLimitMinusOneExponent() throws JsonRpcClientErrorExce
String xrpl4jCoin = Strings.padEnd(BaseEncoding.base16().encode("xrpl4jCoin".getBytes()), 40, '0');
TrustLine trustLine = createTrustLine(
- xrpl4jCoin,
- new BigDecimal(IssuedCurrencyAmount.MAX_VALUE).scaleByPowerOfTen(-1).toEngineeringString(),
- issuerKeyPair,
counterpartyKeyPair,
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .currency(xrpl4jCoin)
+ .value(new BigDecimal(IssuedCurrencyAmount.MAX_VALUE).scaleByPowerOfTen(-1).toEngineeringString())
+ .build(),
FeeUtils.computeNetworkFees(feeResult).recommendedFee()
);
@@ -142,10 +143,12 @@ void createTrustlineWithSmallestPositiveLimit() throws JsonRpcClientErrorExcepti
String xrpl4jCoin = Strings.padEnd(BaseEncoding.base16().encode("xrpl4jCoin".getBytes()), 40, '0');
TrustLine trustLine = createTrustLine(
- xrpl4jCoin,
- IssuedCurrencyAmount.MIN_POSITIVE_VALUE,
- issuerKeyPair,
counterpartyKeyPair,
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .currency(xrpl4jCoin)
+ .value(IssuedCurrencyAmount.MIN_POSITIVE_VALUE)
+ .build(),
FeeUtils.computeNetworkFees(feeResult).recommendedFee()
);
@@ -175,10 +178,12 @@ void createTrustlineWithSmalletPositiveLimitPlusOne() throws JsonRpcClientErrorE
BigDecimal limitValue = new BigDecimal(IssuedCurrencyAmount.MIN_POSITIVE_VALUE)
.add(new BigDecimal(IssuedCurrencyAmount.MIN_POSITIVE_VALUE).scaleByPowerOfTen(-1));
TrustLine trustLine = createTrustLine(
- xrpl4jCoin,
- limitValue.toString(),
- issuerKeyPair,
counterpartyKeyPair,
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .currency(xrpl4jCoin)
+ .value(limitValue.toString())
+ .build(),
FeeUtils.computeNetworkFees(feeResult).recommendedFee()
);
@@ -206,10 +211,12 @@ public void issueIssuedCurrencyBalance() throws JsonRpcClientErrorException, Jso
String xrpl4jCoin = Strings.padEnd(BaseEncoding.base16().encode("xrpl4jCoin".getBytes()), 40, '0');
TrustLine trustLine = createTrustLine(
- xrpl4jCoin,
- "10000",
- issuerKeyPair,
counterpartyKeyPair,
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .currency(xrpl4jCoin)
+ .value("10000")
+ .build(),
FeeUtils.computeNetworkFees(feeResult).recommendedFee()
);
@@ -222,7 +229,12 @@ public void issueIssuedCurrencyBalance() throws JsonRpcClientErrorException, Jso
///////////////////////////
// Send some xrpl4jCoin to the counterparty account.
sendIssuedCurrency(
- xrpl4jCoin, trustLine.limitPeer(), issuerKeyPair, counterpartyKeyPair,
+ issuerKeyPair, counterpartyKeyPair,
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .currency(xrpl4jCoin)
+ .value(trustLine.limitPeer())
+ .build(),
FeeUtils.computeNetworkFees(feeResult).recommendedFee()
);
@@ -230,13 +242,17 @@ 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.
TrustLine trustLineAfterPayment = this.scanForResult(
- () -> getValidatedAccountLines(issuerKeyPair.publicKey().deriveAddress(),
- counterpartyKeyPair.publicKey().deriveAddress()),
- linesResult -> linesResult.lines().stream().anyMatch(line -> line.balance().equals("-" + trustLine.limitPeer()))
+ () -> 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();
+ .orElseThrow(() -> new RuntimeException(
+ String.format("Expected a RippleStateObject for account %s, but none existed.", trustLine.account().value())
+ ));
assertThatEntryEqualsObjectFromAccountObjects(
trustLineAfterPayment,
@@ -255,8 +271,7 @@ public void issueIssuedCurrencyBalance() throws JsonRpcClientErrorException, Jso
);
assertThat(counterpartyCurrencies.sendCurrencies()).asList().containsOnly(xrpl4jCoin);
- assertThat(counterpartyCurrencies.ledgerCurrentIndex()).isNotEmpty().get()
- .isEqualTo(counterpartyCurrencies.ledgerCurrentIndexSafe());
+ assertThat(counterpartyCurrencies.ledgerCurrentIndex()).isNotEmpty();
}
@Test
@@ -276,40 +291,48 @@ public void sendSimpleRipplingIssuedCurrencyPayment() throws JsonRpcClientErrorE
///////////////////////////
// Create a TrustLine between alice and the issuer
createTrustLine(
- "USD",
- "10000",
- issuerKeyPair,
aliceKeyPair,
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .currency("USD")
+ .value("10000")
+ .build(),
FeeUtils.computeNetworkFees(feeResult).recommendedFee()
);
///////////////////////////
// Create a TrustLine between bob and the issuer
TrustLine bobTrustLine = createTrustLine(
- "USD",
- "10000",
- issuerKeyPair,
bobKeyPair,
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .currency("USD")
+ .value("10000")
+ .build(),
FeeUtils.computeNetworkFees(feeResult).recommendedFee()
);
///////////////////////////
// Issuer issues 50 USD to alice
sendIssuedCurrency(
- "USD",
- "50",
- issuerKeyPair,
- aliceKeyPair,
+ issuerKeyPair, aliceKeyPair,
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .currency("USD")
+ .value("50")
+ .build(),
FeeUtils.computeNetworkFees(feeResult).recommendedFee()
);
///////////////////////////
// Issuer issues 50 USD to bob
sendIssuedCurrency(
- "USD",
- "50",
- issuerKeyPair,
- bobKeyPair,
+ issuerKeyPair, bobKeyPair,
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerKeyPair.publicKey().deriveAddress())
+ .currency("USD")
+ .value("50")
+ .build(),
FeeUtils.computeNetworkFees(feeResult).recommendedFee()
);
@@ -326,7 +349,7 @@ public void sendSimpleRipplingIssuedCurrencyPayment() throws JsonRpcClientErrorE
// Note that because this path consists of all implied paths, rippled will not return any path steps
scanForResult(
() -> getValidatedRipplePath(aliceKeyPair, bobKeyPair, pathDestinationAmount),
- path -> path.alternatives().size() > 0 // rippled will return a PathAlternative without any path steps
+ path -> !path.alternatives().isEmpty() // rippled will return a PathAlternative without any path steps
);
///////////////////////////
@@ -382,7 +405,7 @@ public void sendSimpleRipplingIssuedCurrencyPayment() throws JsonRpcClientErrorE
*/
@Test
public void sendMultiHopSameCurrencyPayment() throws JsonRpcClientErrorException, JsonProcessingException {
- // NOTE: Only run this on non-testnet and non-devnet evironmens.
+ // NOTE: Only run this on non-testnet and non-devnet environments.
if (TestnetEnvironment.class.isAssignableFrom(xrplEnvironment.getClass()) ||
DevnetEnvironment.class.isAssignableFrom(xrplEnvironment.getClass())) {
return;
@@ -393,8 +416,8 @@ public void sendMultiHopSameCurrencyPayment() throws JsonRpcClientErrorException
final KeyPair issuerAKeyPair = createRandomAccountEd25519();
final KeyPair issuerBKeyPair = createRandomAccountEd25519();
final KeyPair charlieKeyPair = createRandomAccountEd25519();
- final KeyPair danielKeyPair = createRandomAccountEd25519();
final KeyPair emilyKeyPair = createRandomAccountEd25519();
+ final KeyPair danielKeyPair = createRandomAccountEd25519();
///////////////////////////
// Set the lsfDefaultRipple AccountRoot flag so that all trustlines in this topography allow rippling
@@ -402,57 +425,72 @@ public void sendMultiHopSameCurrencyPayment() throws JsonRpcClientErrorException
setDefaultRipple(issuerAKeyPair, feeResult);
setDefaultRipple(issuerBKeyPair, feeResult);
setDefaultRipple(charlieKeyPair, feeResult);
- setDefaultRipple(danielKeyPair, feeResult);
setDefaultRipple(emilyKeyPair, feeResult);
+ setDefaultRipple(danielKeyPair, feeResult);
///////////////////////////
// Create a Trustline between charlie and issuerA
final TrustLine charlieTrustLineWithIssuerA = createTrustLine(
- "USD",
- "10000",
- issuerAKeyPair,
charlieKeyPair,
- FeeUtils.computeNetworkFees(feeResult).recommendedFee()
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerAKeyPair.publicKey().deriveAddress())
+ .currency("USD")
+ .value("10000")
+ .build(),
+ FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
+ TrustSetFlags.empty()
);
///////////////////////////
// Create a Trustline between emily and issuerA
createTrustLine(
- "USD",
- "10000",
- issuerAKeyPair,
emilyKeyPair,
- FeeUtils.computeNetworkFees(feeResult).recommendedFee()
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerAKeyPair.publicKey().deriveAddress())
+ .currency("USD")
+ .value("10000")
+ .build(),
+ FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
+ TrustSetFlags.empty()
);
///////////////////////////
// Create a Trustline between emily and issuerB
createTrustLine(
- "USD",
- "10000",
- issuerBKeyPair,
emilyKeyPair,
- FeeUtils.computeNetworkFees(feeResult).recommendedFee()
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerBKeyPair.publicKey().deriveAddress())
+ .currency("USD")
+ .value("10000")
+ .build(),
+ FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
+ TrustSetFlags.empty()
);
///////////////////////////
// Create a Trustline between daniel and issuerB
final TrustLine danielTrustLineWithIssuerB = createTrustLine(
- "USD",
- "10000",
- issuerBKeyPair,
danielKeyPair,
- FeeUtils.computeNetworkFees(feeResult).recommendedFee()
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerBKeyPair.publicKey().deriveAddress())
+ .currency("USD")
+ .value("10000")
+ .build(),
+ FeeUtils.computeNetworkFees(feeResult).recommendedFee(),
+ TrustSetFlags.empty()
);
///////////////////////////
// Issue 10 USD from issuerA to charlie.
// IssuerA now owes Charlie 10 USD.
sendIssuedCurrency(
- "USD",
- "10",
- issuerAKeyPair,
- charlieKeyPair,
+ issuerAKeyPair, // <-- From
+ charlieKeyPair, // <-- To
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerAKeyPair.publicKey().deriveAddress())
+ .currency("USD")
+ .value("10")
+ .build(),
FeeUtils.computeNetworkFees(feeResult).recommendedFee()
);
@@ -460,10 +498,13 @@ public void sendMultiHopSameCurrencyPayment() throws JsonRpcClientErrorException
// Issue 1 USD from issuerA to emily.
// IssuerA now owes Emily 1 USD
sendIssuedCurrency(
- "USD",
- "1",
- issuerAKeyPair,
- emilyKeyPair,
+ issuerAKeyPair, // <-- From
+ emilyKeyPair, // <-- To
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerAKeyPair.publicKey().deriveAddress())
+ .currency("USD")
+ .value("1")
+ .build(),
FeeUtils.computeNetworkFees(feeResult).recommendedFee()
);
@@ -471,10 +512,13 @@ public void sendMultiHopSameCurrencyPayment() throws JsonRpcClientErrorException
// Issue 100 USD from issuerB to emily.
// IssuerB now owes Emily 100 USD
sendIssuedCurrency(
- "USD",
- "100",
- issuerBKeyPair,
- emilyKeyPair,
+ issuerBKeyPair, // <-- From
+ emilyKeyPair, // <-- To
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerBKeyPair.publicKey().deriveAddress())
+ .currency("USD")
+ .value("100")
+ .build(),
FeeUtils.computeNetworkFees(feeResult).recommendedFee()
);
@@ -482,22 +526,26 @@ public void sendMultiHopSameCurrencyPayment() throws JsonRpcClientErrorException
// Issue 2 USD from issuerB to daniel.
// IssuerB now owes Daniel 2 USD
sendIssuedCurrency(
- "USD",
- "2",
- issuerBKeyPair,
- danielKeyPair,
+ issuerBKeyPair, // <-- From
+ danielKeyPair, // <-- To
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerBKeyPair.publicKey().deriveAddress())
+ .currency("USD")
+ .value("2")
+ .build(),
FeeUtils.computeNetworkFees(feeResult).recommendedFee()
);
///////////////////////////
// Look for a payment path from charlie to daniel.
List> pathSteps = scanForResult(
- () -> getValidatedRipplePath(charlieKeyPair, danielKeyPair, IssuedCurrencyAmount.builder()
- .issuer(issuerBKeyPair.publicKey().deriveAddress())
- .currency(charlieTrustLineWithIssuerA.currency())
- .value("10")
- .build()),
- path -> path.alternatives().size() > 0
+ () -> getValidatedRipplePath(charlieKeyPair, danielKeyPair,
+ IssuedCurrencyAmount.builder()
+ .issuer(issuerBKeyPair.publicKey().deriveAddress())
+ .currency(charlieTrustLineWithIssuerA.currency())
+ .value("10")
+ .build()),
+ path -> !path.alternatives().isEmpty()
)
.alternatives().stream()
.filter(alt ->
@@ -577,6 +625,7 @@ public void sendMultiHopSameCurrencyPayment() throws JsonRpcClientErrorException
*
* @param issuerKeyPair The {@link KeyPair} containing the address of the issuer account.
* @param feeResult The current {@link FeeResult}.
+ *
* @throws JsonRpcClientErrorException If anything goes wrong while communicating with rippled.
*/
public void setDefaultRipple(KeyPair issuerKeyPair, FeeResult feeResult)
@@ -618,10 +667,11 @@ private void assertThatEntryEqualsObjectFromAccountObjects(
RippleStateObject rippleStateObject = (RippleStateObject) xrplClient.accountObjects(
AccountObjectsRequestParams.of(trustLine.account())
).accountObjects().stream()
- .filter(object -> RippleStateObject.class.isAssignableFrom(object.getClass()) /*&&*/
- /*((RippleStateObject) object).previousTransactionLedgerSequence().equals(lastSequence)*/)
+ .filter(object -> RippleStateObject.class.isAssignableFrom(object.getClass()))
.findFirst()
- .get();
+ .orElseThrow(() -> new RuntimeException(
+ String.format("Expected a RippleStateObject for account %s, but none existed.", trustLine.account().value())
+ ));
LedgerEntryResult entry = xrplClient.ledgerEntry(
LedgerEntryRequestParams.rippleState(
diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/LedgerResultIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/LedgerResultIT.java
index de2809483..cf135b60a 100644
--- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/LedgerResultIT.java
+++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/LedgerResultIT.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.
@@ -25,9 +25,17 @@
import org.junit.jupiter.api.Test;
import org.xrpl.xrpl4j.client.JsonRpcClientErrorException;
+import org.xrpl.xrpl4j.client.XrplClient;
import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier;
import org.xrpl.xrpl4j.model.client.ledger.LedgerRequestParams;
import org.xrpl.xrpl4j.model.client.ledger.LedgerResult;
+import org.xrpl.xrpl4j.model.client.transactions.TransactionResult;
+import org.xrpl.xrpl4j.model.transactions.AmmBid;
+import org.xrpl.xrpl4j.model.transactions.Hash256;
+import org.xrpl.xrpl4j.model.transactions.Transaction;
+import org.xrpl.xrpl4j.tests.environment.XrplEnvironment;
+
+import java.util.Optional;
/**
* These tests ensure {@link LedgerResult}s can be constructed from any JSON responses rippled sends back.
@@ -88,4 +96,28 @@ void getClosedLedgerResult() throws JsonRpcClientErrorException {
assertThat(ledgerResult.ledger().closeTimeHuman()).isNotEmpty();
assertThat(ledgerResult.ledger().parentCloseTime()).isNotEmpty();
}
+
+ /**
+ * Pulls down ledger BAA4AF508E9A9CB7685D9E61B82486681E52E9B920904B0650499497F050D8FA, which had an
+ * {@link org.xrpl.xrpl4j.model.transactions.AmmBid} transaction with an
+ * {@link org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount} in the {@link AmmBid#bidMax()} field.
+ */
+ @Test
+ void canDeserializeLedger_Issue529() throws JsonRpcClientErrorException {
+ XrplClient mainnetClient = XrplEnvironment.getConfiguredMainnetEnvironment().getXrplClient();
+ LedgerResult ledger = mainnetClient.ledger(
+ LedgerRequestParams.builder()
+ .transactions(true)
+ .ledgerSpecifier(
+ LedgerSpecifier.of(Hash256.of("BAA4AF508E9A9CB7685D9E61B82486681E52E9B920904B0650499497F050D8FA")))
+ .build()
+ );
+
+ Optional> problematicTransaction = ledger.ledger().transactions().stream()
+ .filter(transaction -> transaction.hash()
+ .equals(Hash256.of("6A8BC948E1309219EA8E14905D0EA9BB94E24429DE5B15CDE8916CDBCE42034B")))
+ .findFirst();
+
+ assertThat(problematicTransaction).isNotEmpty();
+ }
}
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 5204f08f8..5a3c91f4e 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
@@ -491,8 +491,27 @@ void mintAndCreateThenAcceptOffer() throws JsonRpcClientErrorException, JsonProc
);
logger.info("NFT Accept Offer transaction was validated successfully.");
- assertThat(xrplClient.accountNfts(keypair.publicKey().deriveAddress()).accountNfts().size()).isEqualTo(0);
- assertThat(xrplClient.accountNfts(wallet2.publicKey().deriveAddress()).accountNfts().size()).isEqualTo(1);
+ this.scanForResult(
+ () -> {
+ try {
+ return xrplClient.accountNfts(keypair.publicKey().deriveAddress()).accountNfts().size();
+ } catch (JsonRpcClientErrorException e) {
+ throw new RuntimeException(e);
+ }
+ },
+ size -> size == 0
+ );
+
+ this.scanForResult(
+ () -> {
+ try {
+ return xrplClient.accountNfts(wallet2.publicKey().deriveAddress()).accountNfts().size();
+ } catch (JsonRpcClientErrorException e) {
+ throw new RuntimeException(e);
+ }
+ },
+ size -> size == 1
+ );
logger.info("The NFT ownership was transferred.");
}
diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/NftInfoIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/NftInfoIT.java
index 259a7eae0..8ffeefb61 100644
--- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/NftInfoIT.java
+++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/NftInfoIT.java
@@ -50,7 +50,7 @@ void getNftInfo() throws JsonRpcClientErrorException {
}
@Test
- void getNftInfoFromReportingModeThrows() throws JsonRpcClientErrorException {
+ void getNftInfoFromReportingModeThrows() {
XrplClient client = new ReportingMainnetEnvironment().getXrplClient();
NftInfoRequestParams params = NftInfoRequestParams.builder()
.nfTokenId(NfTokenId.of("0008138808C4E53F4F6EF5D5B2AF64F96B457F42E0ED9530FE9B131300001178"))
@@ -59,7 +59,7 @@ void getNftInfoFromReportingModeThrows() throws JsonRpcClientErrorException {
assertThatThrownBy(
() -> client.nftInfo(params)
).isInstanceOf(JsonRpcClientErrorException.class)
- .hasMessage("Unknown method.");
+ .hasMessage("unknownCmd (Unknown method.)");
}
}
diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/PriceOracleIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/PriceOracleIT.java
new file mode 100644
index 000000000..5987f8470
--- /dev/null
+++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/PriceOracleIT.java
@@ -0,0 +1,336 @@
+package org.xrpl.xrpl4j.tests;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.io.BaseEncoding;
+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.junit.jupiter.api.condition.DisabledIf;
+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.Finality;
+import org.xrpl.xrpl4j.model.client.FinalityStatus;
+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.ledger.OracleLedgerEntryParams;
+import org.xrpl.xrpl4j.model.client.oracle.GetAggregatePriceRequestParams;
+import org.xrpl.xrpl4j.model.client.oracle.GetAggregatePriceResult;
+import org.xrpl.xrpl4j.model.client.transactions.SubmitResult;
+import org.xrpl.xrpl4j.model.ledger.OracleObject;
+import org.xrpl.xrpl4j.model.transactions.AssetPrice;
+import org.xrpl.xrpl4j.model.transactions.OracleDelete;
+import org.xrpl.xrpl4j.model.transactions.OracleDocumentId;
+import org.xrpl.xrpl4j.model.transactions.OracleProvider;
+import org.xrpl.xrpl4j.model.transactions.OracleSet;
+import org.xrpl.xrpl4j.model.transactions.OracleUri;
+import org.xrpl.xrpl4j.model.transactions.PriceData;
+import org.xrpl.xrpl4j.model.transactions.PriceDataWrapper;
+
+import java.math.BigDecimal;
+import java.time.Instant;
+import java.util.Optional;
+
+@DisabledIf(value = "shouldNotRun", disabledReason = "PriceOracleIT only runs on local rippled node or devnet.")
+public class PriceOracleIT extends AbstractIT {
+
+ static boolean shouldNotRun() {
+ return System.getProperty("useTestnet") != null ||
+ System.getProperty("useClioTestnet") != null;
+ }
+
+ String xrpl4jCoin = Strings.padEnd(BaseEncoding.base16().encode("xrpl4jCoin".getBytes()), 40, '0');
+
+ @Test
+ void createAndUpdateAndDeleteOracle() throws JsonRpcClientErrorException, JsonProcessingException {
+ KeyPair sourceKeyPair = createRandomAccountEd25519();
+
+ AccountInfoResult accountInfo = this.scanForResult(
+ () -> this.getValidatedAccountInfo(sourceKeyPair.publicKey().deriveAddress())
+ );
+
+ FeeResult feeResult = xrplClient.fee();
+ OracleProvider provider = OracleProvider.of(BaseEncoding.base16().encode("DIA".getBytes()));
+ OracleUri uri = OracleUri.of(BaseEncoding.base16().encode("http://example.com".getBytes()));
+ UnsignedInteger lastUpdateTime = unixTimestamp();
+ String assetClass = BaseEncoding.base16().encode("currency".getBytes());
+ PriceDataWrapper priceData1 = PriceDataWrapper.of(
+ PriceData.builder()
+ .baseAsset("XRP")
+ .quoteAsset(xrpl4jCoin)
+ .assetPrice(AssetPrice.of(UnsignedLong.ONE))
+ .scale(UnsignedInteger.valueOf(10))
+ .build()
+ );
+ PriceDataWrapper priceData2 = PriceDataWrapper.of(
+ PriceData.builder()
+ .baseAsset("XRP")
+ .quoteAsset("EUR")
+ .assetPrice(AssetPrice.of(UnsignedLong.ONE))
+ .scale(UnsignedInteger.valueOf(10))
+ .build()
+ );
+ OracleSet oracleSet = OracleSet.builder()
+ .account(sourceKeyPair.publicKey().deriveAddress())
+ .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee())
+ .sequence(accountInfo.accountData().sequence())
+ .signingPublicKey(sourceKeyPair.publicKey())
+ .lastLedgerSequence(accountInfo.ledgerIndexSafe().plus(UnsignedInteger.valueOf(4)).unsignedIntegerValue())
+ .oracleDocumentId(OracleDocumentId.of(UnsignedInteger.ONE))
+ .provider(provider)
+ .uri(uri)
+ .lastUpdateTime(lastUpdateTime)
+ .assetClass(assetClass)
+ .addPriceDataSeries(priceData1, priceData2)
+ .build();
+
+ SingleSignedTransaction signedOracleSet = signatureService.sign(sourceKeyPair.privateKey(), oracleSet);
+ SubmitResult oracleSetSubmitResult = xrplClient.submit(signedOracleSet);
+ assertThat(oracleSetSubmitResult.engineResult()).isEqualTo("tesSUCCESS");
+
+ Finality finality = scanForFinality(
+ signedOracleSet.hash(),
+ accountInfo.ledgerIndexSafe(),
+ oracleSet.lastLedgerSequence().get(),
+ oracleSet.sequence(),
+ sourceKeyPair.publicKey().deriveAddress()
+ );
+ assertThat(finality.finalityStatus()).isEqualTo(FinalityStatus.VALIDATED_SUCCESS);
+
+ LedgerEntryResult ledgerEntry = xrplClient.ledgerEntry(
+ LedgerEntryRequestParams.oracle(
+ OracleLedgerEntryParams.builder()
+ .oracleDocumentId(oracleSet.oracleDocumentId())
+ .account(sourceKeyPair.publicKey().deriveAddress())
+ .build(),
+ LedgerSpecifier.VALIDATED
+ )
+ );
+ OracleObject oracleObject = ledgerEntry.node();
+ assertThat(oracleObject.owner()).isEqualTo(sourceKeyPair.publicKey().deriveAddress());
+ assertThat(oracleObject.provider()).isEqualTo(provider);
+ assertThat(oracleObject.assetClass()).isEqualTo(assetClass);
+ assertThat(oracleObject.lastUpdateTime()).isEqualTo(lastUpdateTime);
+ assertThat(oracleObject.uri()).isNotEmpty().get().isEqualTo(uri);
+ assertThat(oracleObject.priceDataSeries()).containsExactlyInAnyOrder(priceData1, priceData2);
+
+ UnsignedInteger lastUpdateTime2 = unixTimestamp();
+ PriceDataWrapper newPriceData = PriceDataWrapper.of(
+ PriceData.builder()
+ .baseAsset("XRP")
+ .quoteAsset("USD")
+ .assetPrice(AssetPrice.of(UnsignedLong.ONE))
+ .scale(UnsignedInteger.valueOf(10))
+ .build()
+ );
+ PriceDataWrapper updatedPriceData = PriceDataWrapper.of(
+ PriceData.builder().from(priceData2.priceData())
+ .assetPrice(AssetPrice.of(UnsignedLong.valueOf(1000)))
+ .build()
+ );
+ OracleSet oracleUpdate = OracleSet.builder().from(oracleSet)
+ .lastLedgerSequence(ledgerEntry.ledgerIndexSafe().plus(UnsignedInteger.valueOf(4)).unsignedIntegerValue())
+ .lastUpdateTime(lastUpdateTime2)
+ .sequence(oracleSet.sequence().plus(UnsignedInteger.ONE))
+ .priceDataSeries(Lists.newArrayList(
+ // New asset pair should get added
+ newPriceData,
+ // Same asset pair without assetPrice should delete
+ PriceDataWrapper.of(
+ PriceData.builder().from(priceData1.priceData())
+ .scale(Optional.empty())
+ .assetPrice(Optional.empty())
+ .build()
+ ),
+ // Updating assetPrice should update an existing price data entry.
+ updatedPriceData
+ ))
+ .build();
+
+ SingleSignedTransaction signedOracleUpdate = signatureService.sign(
+ sourceKeyPair.privateKey(), oracleUpdate
+ );
+ SubmitResult oracleUpdateSubmitResult = xrplClient.submit(signedOracleUpdate);
+ assertThat(oracleUpdateSubmitResult.engineResult()).isEqualTo("tesSUCCESS");
+
+ Finality updateFinality = scanForFinality(
+ signedOracleUpdate.hash(),
+ accountInfo.ledgerIndexSafe(),
+ oracleUpdate.lastLedgerSequence().get(),
+ oracleUpdate.sequence(),
+ sourceKeyPair.publicKey().deriveAddress()
+ );
+ assertThat(updateFinality.finalityStatus()).isEqualTo(FinalityStatus.VALIDATED_SUCCESS);
+
+ ledgerEntry = xrplClient.ledgerEntry(
+ LedgerEntryRequestParams.oracle(
+ OracleLedgerEntryParams.builder()
+ .oracleDocumentId(oracleSet.oracleDocumentId())
+ .account(sourceKeyPair.publicKey().deriveAddress())
+ .build(),
+ LedgerSpecifier.VALIDATED
+ )
+ );
+ oracleObject = ledgerEntry.node();
+ assertThat(oracleObject.owner()).isEqualTo(sourceKeyPair.publicKey().deriveAddress());
+ assertThat(oracleObject.provider()).isEqualTo(provider);
+ assertThat(oracleObject.assetClass()).isEqualTo(assetClass);
+ assertThat(oracleObject.lastUpdateTime()).isEqualTo(lastUpdateTime2);
+ assertThat(oracleObject.uri()).isNotEmpty().get().isEqualTo(uri);
+ assertThat(oracleObject.priceDataSeries()).containsExactlyInAnyOrder(newPriceData, updatedPriceData);
+
+ OracleDelete oracleDelete = OracleDelete.builder()
+ .account(sourceKeyPair.publicKey().deriveAddress())
+ .fee(oracleSet.fee())
+ .sequence(oracleUpdate.sequence().plus(UnsignedInteger.ONE))
+ .lastLedgerSequence(ledgerEntry.ledgerIndexSafe().plus(UnsignedInteger.valueOf(4)).unsignedIntegerValue())
+ .signingPublicKey(sourceKeyPair.publicKey())
+ .oracleDocumentId(oracleSet.oracleDocumentId())
+ .build();
+ SingleSignedTransaction signedOracleDelete = signatureService.sign(
+ sourceKeyPair.privateKey(), oracleDelete
+ );
+ SubmitResult oracleDeleteSubmitResult = xrplClient.submit(signedOracleDelete);
+ assertThat(oracleDeleteSubmitResult.engineResult()).isEqualTo("tesSUCCESS");
+
+ Finality deleteFinality = scanForFinality(
+ signedOracleDelete.hash(),
+ accountInfo.ledgerIndexSafe(),
+ oracleDelete.lastLedgerSequence().get(),
+ oracleDelete.sequence(),
+ sourceKeyPair.publicKey().deriveAddress()
+ );
+ assertThat(deleteFinality.finalityStatus()).isEqualTo(FinalityStatus.VALIDATED_SUCCESS);
+
+ assertThatThrownBy(() -> xrplClient.ledgerEntry(
+ LedgerEntryRequestParams.oracle(
+ OracleLedgerEntryParams.builder()
+ .oracleDocumentId(oracleSet.oracleDocumentId())
+ .account(sourceKeyPair.publicKey().deriveAddress())
+ .build(),
+ LedgerSpecifier.VALIDATED
+ )
+ )).isInstanceOf(JsonRpcClientErrorException.class)
+ .hasMessage("entryNotFound (n/a)");
+ }
+
+ @Test
+ void createTwoOraclesAndGetAggregatePrice() throws JsonRpcClientErrorException, JsonProcessingException {
+ KeyPair sourceKeyPair = createRandomAccountEd25519();
+
+ AccountInfoResult accountInfo = this.scanForResult(
+ () -> this.getValidatedAccountInfo(sourceKeyPair.publicKey().deriveAddress())
+ );
+
+ FeeResult feeResult = xrplClient.fee();
+ OracleProvider provider = OracleProvider.of(BaseEncoding.base16().encode("DIA".getBytes()));
+ OracleUri uri = OracleUri.of(BaseEncoding.base16().encode("http://example.com".getBytes()));
+ UnsignedInteger lastUpdateTime = unixTimestamp();
+ String assetClass = BaseEncoding.base16().encode("currency".getBytes());
+ PriceDataWrapper priceData1 = PriceDataWrapper.of(
+ PriceData.builder()
+ .baseAsset("XRP")
+ .quoteAsset("EUR")
+ .assetPrice(AssetPrice.of(UnsignedLong.ONE))
+ .build()
+ );
+ OracleSet oracleSet = OracleSet.builder()
+ .account(sourceKeyPair.publicKey().deriveAddress())
+ .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee())
+ .sequence(accountInfo.accountData().sequence())
+ .signingPublicKey(sourceKeyPair.publicKey())
+ .lastLedgerSequence(accountInfo.ledgerIndexSafe().plus(UnsignedInteger.valueOf(4)).unsignedIntegerValue())
+ .oracleDocumentId(OracleDocumentId.of(UnsignedInteger.ONE))
+ .provider(provider)
+ .uri(uri)
+ .lastUpdateTime(lastUpdateTime)
+ .assetClass(assetClass)
+ .addPriceDataSeries(priceData1)
+ .build();
+
+ SingleSignedTransaction signedOracleSet = signatureService.sign(sourceKeyPair.privateKey(), oracleSet);
+ SubmitResult oracleSetSubmitResult = xrplClient.submit(signedOracleSet);
+ assertThat(oracleSetSubmitResult.engineResult()).isEqualTo("tesSUCCESS");
+
+ Finality finality = scanForFinality(
+ signedOracleSet.hash(),
+ accountInfo.ledgerIndexSafe(),
+ oracleSet.lastLedgerSequence().get(),
+ oracleSet.sequence(),
+ sourceKeyPair.publicKey().deriveAddress()
+ );
+ assertThat(finality.finalityStatus()).isEqualTo(FinalityStatus.VALIDATED_SUCCESS);
+
+ PriceDataWrapper priceData2 = PriceDataWrapper.of(
+ PriceData.builder()
+ .baseAsset("XRP")
+ .quoteAsset("EUR")
+ .assetPrice(AssetPrice.of(UnsignedLong.valueOf(2)))
+ .build()
+ );
+ OracleSet oracleSet2 = OracleSet.builder()
+ .account(sourceKeyPair.publicKey().deriveAddress())
+ .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee())
+ .sequence(accountInfo.accountData().sequence().plus(UnsignedInteger.ONE))
+ .signingPublicKey(sourceKeyPair.publicKey())
+ .lastLedgerSequence(accountInfo.ledgerIndexSafe().plus(UnsignedInteger.valueOf(8)).unsignedIntegerValue())
+ .oracleDocumentId(OracleDocumentId.of(UnsignedInteger.valueOf(2)))
+ .provider(provider)
+ .uri(uri)
+ .lastUpdateTime(lastUpdateTime)
+ .assetClass(assetClass)
+ .addPriceDataSeries(priceData2)
+ .build();
+
+ SingleSignedTransaction signedOracleSet2 = signatureService.sign(sourceKeyPair.privateKey(), oracleSet2);
+ SubmitResult oracleSetSubmitResult2 = xrplClient.submit(signedOracleSet2);
+ assertThat(oracleSetSubmitResult2.engineResult()).isEqualTo("tesSUCCESS");
+
+ Finality finality2 = scanForFinality(
+ signedOracleSet2.hash(),
+ accountInfo.ledgerIndexSafe(),
+ oracleSet2.lastLedgerSequence().get(),
+ oracleSet2.sequence(),
+ sourceKeyPair.publicKey().deriveAddress()
+ );
+ assertThat(finality2.finalityStatus()).isEqualTo(FinalityStatus.VALIDATED_SUCCESS);
+
+ GetAggregatePriceResult aggregatePrice = xrplClient.getAggregatePrice(
+ GetAggregatePriceRequestParams.builder()
+ .ledgerSpecifier(LedgerSpecifier.VALIDATED)
+ .baseAsset("XRP")
+ .quoteAsset("EUR")
+ .trim(UnsignedInteger.ONE)
+ .addOracles(
+ OracleLedgerEntryParams.builder()
+ .account(sourceKeyPair.publicKey().deriveAddress())
+ .oracleDocumentId(oracleSet.oracleDocumentId())
+ .build(),
+ OracleLedgerEntryParams.builder()
+ .account(sourceKeyPair.publicKey().deriveAddress())
+ .oracleDocumentId(oracleSet2.oracleDocumentId())
+ .build()
+ )
+ .build()
+ );
+ assertThat(aggregatePrice.median()).isEqualTo(BigDecimal.valueOf(1.5));
+ assertThat(aggregatePrice.status()).isNotEmpty().get().isEqualTo("success");
+ assertThat(aggregatePrice.entireSet().mean()).isEqualTo(BigDecimal.valueOf(1.5));
+ assertThat(aggregatePrice.entireSet().size()).isEqualTo(UnsignedLong.valueOf(2));
+ assertThat(aggregatePrice.trimmedSet()).isNotEmpty().get().isEqualTo(aggregatePrice.entireSet());
+ }
+
+ private static UnsignedInteger unixTimestamp() {
+ return UnsignedInteger.valueOf(System.currentTimeMillis() / 1000L);
+ }
+}
diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/ServerInfoIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/ServerInfoIT.java
index 5c4a9d0ad..fe068612a 100644
--- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/ServerInfoIT.java
+++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/ServerInfoIT.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.
@@ -20,53 +20,83 @@
* =========================LICENSE_END==================================
*/
+import static org.assertj.core.api.Fail.fail;
+
+import com.google.common.primitives.UnsignedInteger;
import okhttp3.HttpUrl;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.Timeout;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.xrpl.xrpl4j.client.JsonRpcClientErrorException;
import org.xrpl.xrpl4j.client.XrplClient;
import org.xrpl.xrpl4j.model.client.serverinfo.ServerInfo;
-
-import java.util.concurrent.TimeUnit;
+import org.xrpl.xrpl4j.model.transactions.NetworkId;
/**
* Integration test for different types of ServerInfo values.
*/
public class ServerInfoIT {
- Logger logger = LoggerFactory.getLogger(this.getClass());
-
/**
- * This test was written to run a long-running test on different Server Info models. Server Info is an implementation
- * detail and different servers could respond differently. This test essentially verifies the correctness of these
- * models. In the future, if the response structure changes, this test will alarm us to take action but has to be run
- * manually everytime since it is disabled for CI.
+ * This test validates actual Server Info models. {@link ServerInfo} is an implementation detail and different servers
+ * could respond differently, so this test essentially verifies the correctness of these models.
*
* @throws JsonRpcClientErrorException If {@code jsonRpcClient} throws an error.
* @throws InterruptedException If {@link Thread} is interrupted.
+ * @see "https://xrpl.org/docs/tutorials/public-servers/#mainnet"
*/
- @Timeout(value = 3, unit = TimeUnit.HOURS)
@Test
- @Disabled
public void testServerInfoAcrossAllTypes() throws JsonRpcClientErrorException, InterruptedException {
- XrplClient rippledClient = getXrplClient(HttpUrl.parse("https://s1.cbdc-sandbox.rippletest.net:51234"));
- XrplClient reportingClient = getXrplClient(HttpUrl.parse("https://s2-reporting.ripple.com:51234"));
- XrplClient clioClient = getXrplClient(HttpUrl.parse("https://s2-clio.ripple.com:51234"));
-
- ServerInfo info;
-
- while (true) {
- info = rippledClient.serverInformation().info();
- logger.info("Rippled info was mapped correctly. " + getType(info));
- info = reportingClient.serverInformation().info();
- logger.info("Reporting mode info was mapped correctly. " + getType(info));
- info = clioClient.serverInformation().info();
- logger.info("Clio info was mapped correctly. " + getType(info));
- Thread.sleep(2000);
- }
+
+ // XRPL Cluster
+ getXrplClient(HttpUrl.parse("https://xrplcluster.com/")).serverInformation().info().handle(
+ this::assertValidNetworkId,
+ clioServerInfo -> fail("Shouldn't be a Clio server"),
+ reportingModeServerInfo -> fail("Shouldn't be a Reporting server")
+ );
+
+ // XRPL Cluster (testnet)
+ // Not executed due to rate limiting.
+
+ // Ripple Mainnet (s1)
+ getXrplClient(HttpUrl.parse("https://s1.ripple.com:51234")).serverInformation().info().handle(
+ rippledServerInfo -> fail("Shouldn't be a rippled server"),
+ this::assertValidNetworkId,
+ reportingModeServerInfo -> fail("Shouldn't be a Reporting server")
+ );
+
+ // Ripple Mainnet (s2)
+ getXrplClient(HttpUrl.parse("https://s2.ripple.com:51234")).serverInformation().info().handle(
+ rippledServerInfo -> fail("Shouldn't be a rippled server"),
+ this::assertValidNetworkId,
+ reportingModeServerInfo -> fail("Shouldn't be a Reporting server")
+ );
+
+ // Ripple Testnet
+ getXrplClient(HttpUrl.parse("https://s.altnet.rippletest.net:51234/")).serverInformation().info().handle(
+ this::assertValidNetworkId,
+ clioServerInfo -> fail("Shouldn't be a Clio server"),
+ reportingModeServerInfo -> fail("Shouldn't be a Reporting server")
+ );
+
+ // Ripple Testnet (Clio)
+ getXrplClient(HttpUrl.parse("https://clio.altnet.rippletest.net:51234/")).serverInformation().info().handle(
+ rippledServerInfo -> fail("Shouldn't be a rippled server"),
+ this::assertValidNetworkId,
+ reportingModeServerInfo -> fail("Shouldn't be a Reporting server")
+ );
+
+ // Ripple Devnet
+ getXrplClient(HttpUrl.parse("https://s.devnet.rippletest.net:51234/")).serverInformation().info().handle(
+ this::assertValidNetworkId,
+ clioServerInfo -> fail("Shouldn't be a Clio server"),
+ reportingModeServerInfo -> fail("Shouldn't be a Reporting server")
+ );
+
+ // Ripple Devnet (Clio)
+ getXrplClient(HttpUrl.parse("https://clio.devnet.rippletest.net:51234/")).serverInformation().info().handle(
+ rippledServerInfo -> fail("Shouldn't be a rippled server"),
+ this::assertValidNetworkId,
+ reportingModeServerInfo -> fail("Shouldn't be a Reporting server")
+ );
}
private String getType(ServerInfo info) {
@@ -80,4 +110,11 @@ private String getType(ServerInfo info) {
private XrplClient getXrplClient(HttpUrl serverUrl) {
return new XrplClient(serverUrl);
}
+
+ private void assertValidNetworkId(ServerInfo serverInfo) {
+ serverInfo.networkId()
+ .map(NetworkId::value)
+ .map($ -> $.equals(UnsignedInteger.ZERO))
+ .orElseThrow(() -> new RuntimeException("networkId should have existed"));
+ }
}
diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SubmitPaymentIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SubmitPaymentIT.java
index 5ee2a97bf..893e54dae 100644
--- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SubmitPaymentIT.java
+++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SubmitPaymentIT.java
@@ -114,10 +114,19 @@ public void sendPaymentFromSecp256k1KeyPair() throws JsonRpcClientErrorException
private void assertPaymentCloseTimeMatchesLedgerCloseTime(TransactionResult validatedPayment)
throws JsonRpcClientErrorException {
- LedgerResult ledger = xrplClient.ledger(
- LedgerRequestParams.builder()
- .ledgerSpecifier(LedgerSpecifier.of(validatedPayment.ledgerIndex().get()))
- .build()
+
+ LedgerResult ledger = this.scanForResult(
+ () -> {
+ try {
+ return xrplClient.ledger(
+ LedgerRequestParams.builder()
+ .ledgerSpecifier(LedgerSpecifier.of(validatedPayment.ledgerIndex().get()))
+ .build()
+ );
+ } catch (JsonRpcClientErrorException e) {
+ throw new RuntimeException(e);
+ }
+ }
);
assertThat(validatedPayment.closeDateHuman()).isNotEmpty();
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 d4531f336..f156c841c 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
@@ -45,6 +45,7 @@
import org.xrpl.xrpl4j.model.transactions.metadata.MetaLedgerEntryType;
import java.util.List;
+import java.util.Optional;
public class TicketIT extends AbstractIT {
@@ -91,8 +92,11 @@ void createTicketAndUseSequenceNumber() throws JsonRpcClientErrorException, Json
.map(AffectedNode::ledgerIndex)
.get();
- AccountInfoResult accountInfoAfterTicketCreate = getValidatedAccountInfo(sourceKeyPair.publicKey().deriveAddress());
- assertThat(accountInfoAfterTicketCreate.accountData().ticketCount()).isNotEmpty().get()
+ Optional ticketCount = this.scanForResult(
+ () -> getValidatedAccountInfo(sourceKeyPair.publicKey().deriveAddress()),
+ result -> result.accountData().ticketCount().isPresent()
+ ).accountData().ticketCount();
+ assertThat(ticketCount).isNotEmpty().get()
.isEqualTo(ticketCreate.ticketCount());
List tickets = getValidatedAccountObjects(
diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/XChainIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/XChainIT.java
index f98919765..e5012517c 100644
--- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/XChainIT.java
+++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/XChainIT.java
@@ -196,17 +196,17 @@ void testXChainAddAccountCreateAttestation() throws JsonProcessingException, Jso
assertThat(newFields.xChainAccountCreateCount()).isNotEmpty().get().isEqualTo(XChainCount.of(UnsignedLong.ONE));
assertThat(newFields.xChainCreateAccountAttestations()).containsExactly(
ImmutableMetaXChainCreateAccountAttestation.builder()
- .xChainCreateAccountProofSig(
- ImmutableMetaXChainCreateAccountProofSig.builder()
- .amount(transaction.amount())
- .signatureReward(transaction.signatureReward())
- .attestationRewardAccount(transaction.attestationRewardAccount())
- .attestationSignerAccount(transaction.attestationSignerAccount())
- .destination(transaction.destination())
- .publicKey(testBridge.witnessKeyPair().publicKey())
- .wasLockingChainSend(false)
- .build()
- )
+ .xChainCreateAccountProofSig(
+ ImmutableMetaXChainCreateAccountProofSig.builder()
+ .amount(transaction.amount())
+ .signatureReward(transaction.signatureReward())
+ .attestationRewardAccount(transaction.attestationRewardAccount())
+ .attestationSignerAccount(transaction.attestationSignerAccount())
+ .destination(transaction.destination())
+ .publicKey(testBridge.witnessKeyPair().publicKey())
+ .wasLockingChainSend(false)
+ .build()
+ )
.build()
);
@@ -221,16 +221,16 @@ void testXChainAddAccountCreateAttestation() throws JsonProcessingException, Jso
assertThat(claimIdObject.xChainBridge()).isEqualTo(testBridge.bridge());
assertThat(claimIdObject.xChainCreateAccountAttestations()).containsExactly(
XChainCreateAccountAttestation.of(
- XChainCreateAccountProofSig.builder()
- .amount(transaction.amount())
- .signatureReward(transaction.signatureReward().get())
- .attestationRewardAccount(transaction.attestationRewardAccount())
- .attestationSignerAccount(transaction.attestationSignerAccount())
- .destination(transaction.destination())
- .publicKey(testBridge.witnessKeyPair().publicKey())
- .wasLockingChainSend(false)
- .build()
- )
+ XChainCreateAccountProofSig.builder()
+ .amount(transaction.amount())
+ .signatureReward(transaction.signatureReward().get())
+ .attestationRewardAccount(transaction.attestationRewardAccount())
+ .attestationSignerAccount(transaction.attestationSignerAccount())
+ .destination(transaction.destination())
+ .publicKey(testBridge.witnessKeyPair().publicKey())
+ .wasLockingChainSend(false)
+ .build()
+ )
);
// Create an attestation for witness 2 to sign
@@ -362,36 +362,38 @@ void testXChainAddClaimAttestationXrpToXrpBridge() throws JsonRpcClientErrorExce
@Test
void testAddClaimAttestationIouToIouBridge() throws JsonRpcClientErrorException, JsonProcessingException {
KeyPair lockingDoor = Seed.ed25519Seed().deriveKeyPair();
- KeyPair issuer = Seed.ed25519Seed().deriveKeyPair();
+ KeyPair lockingChainIssuer = Seed.ed25519Seed().deriveKeyPair();
- KeyPair source = this.createRandomAccountEd25519();
+ KeyPair lockingChainSource = this.createRandomAccountEd25519();
XrpCurrencyAmount fee = FeeUtils.computeNetworkFees(xrplClient.fee()).recommendedFee();
- enableRippling(source, fee);
+ enableRippling(lockingChainSource, fee);
KeyPair destination = this.createRandomAccountEd25519();
this.createTrustLine(
- "USD",
- "1000000000",
- source,
destination,
+ IssuedCurrencyAmount.builder()
+ .issuer(lockingChainSource.publicKey().deriveAddress())
+ .currency("USD")
+ .value("1000000000")
+ .build(),
fee
);
TestBridge iouBridge = setupBridge(
- source,
+ lockingChainSource,
XChainBridge.builder()
.lockingChainDoor(lockingDoor.publicKey().deriveAddress())
.lockingChainIssue(
Issue.builder()
- .issuer(issuer.publicKey().deriveAddress())
+ .issuer(lockingChainIssuer.publicKey().deriveAddress())
.currency("USD")
.build()
)
- .issuingChainDoor(source.publicKey().deriveAddress())
+ .issuingChainDoor(lockingChainSource.publicKey().deriveAddress())
.issuingChainIssue(
Issue.builder()
- .issuer(source.publicKey().deriveAddress())
+ .issuer(lockingChainSource.publicKey().deriveAddress())
.currency("USD")
.build()
)
@@ -401,15 +403,17 @@ void testAddClaimAttestationIouToIouBridge() throws JsonRpcClientErrorException,
KeyPair otherChainSource = this.createRandomAccountEd25519();
IssuedCurrencyAmount amount = IssuedCurrencyAmount.builder()
+ .issuer(lockingChainIssuer.publicKey().deriveAddress())
.currency("USD")
- .issuer(issuer.publicKey().deriveAddress())
.value("10")
.build();
AccountInfoResult sourceAccountInfo = this.scanForResult(
- () -> this.getValidatedAccountInfo(source.publicKey().deriveAddress()));
+ () -> this.getValidatedAccountInfo(lockingChainSource.publicKey().deriveAddress()));
- XChainOwnedClaimIdObject claimIdObject = createClaimId(sourceAccountInfo, fee, source, iouBridge, otherChainSource);
+ XChainOwnedClaimIdObject claimIdObject = createClaimId(
+ sourceAccountInfo, fee, lockingChainSource, iouBridge, otherChainSource
+ );
XChainAddClaimAttestation addAttestation = addClaimAttestation(
otherChainSource,
diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/environment/RippledContainer.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/environment/RippledContainer.java
index 8ed24e93a..feebbac3d 100644
--- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/environment/RippledContainer.java
+++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/environment/RippledContainer.java
@@ -61,7 +61,7 @@ public class RippledContainer {
LOGGER.warn("Ledger accept failed", e);
}
};
- private final GenericContainer rippledContainer;
+ private final GenericContainer> rippledContainer;
private final ScheduledExecutorService ledgerAcceptor;
private boolean started;
@@ -69,7 +69,7 @@ public class RippledContainer {
* No-args constructor.
*/
public RippledContainer() {
- rippledContainer = new GenericContainer("rippleci/rippled:2.0.0-b4")
+ rippledContainer = new GenericContainer<>("rippleci/rippled:2.2.0-rc3")
.withCreateContainerCmdModifier((Consumer) (cmd) ->
cmd.withEntrypoint("/opt/ripple/bin/rippled"))
.withCommand("-a --start --conf /config/rippled.cfg")
@@ -92,8 +92,6 @@ public static KeyPair getMasterKeyPair() {
/**
* Starts container with default interval (1s) for closing ledgers.
- *
- * @return
*/
public RippledContainer start() {
return this.start(1000);
@@ -102,9 +100,9 @@ public RippledContainer start() {
/**
* Start contain with given interval for closing ledgers.
*
- * @param acceptIntervalMillis
+ * @param acceptIntervalMillis The number of milliseconds before each accept call to close the ledger.
*
- * @return
+ * @return A {@link RippledContainer}.
*/
public RippledContainer start(int acceptIntervalMillis) {
if (started) {
@@ -166,7 +164,7 @@ private void assertContainerStarted() {
/**
* Provides an instance of an {@link XrplAdminClient} that will connect to the rippled container.
*
- * @return
+ * @return A {@link XrplAdminClient}.
*/
public XrplAdminClient getXrplAdminClient() {
return new XrplAdminClient(this.getBaseUri());
@@ -175,13 +173,13 @@ public XrplAdminClient getXrplAdminClient() {
/**
* Provides an instance of an {@link XrplClient} that will connect to the rippled container.
*
- * @return
+ * @return A {@link XrplAdminClient}.
*/
public XrplClient getXrplClient() {
return new XrplClient(this.getBaseUri());
}
- private static HttpUrl getBaseUri(GenericContainer rippledContainer) {
+ private static HttpUrl getBaseUri(GenericContainer> rippledContainer) {
return HttpUrl.parse("http://" + rippledContainer.getHost() + ":" + rippledContainer.getMappedPort(5005) + "/");
}
diff --git a/xrpl4j-integration-tests/src/test/resources/rippled/rippled.cfg b/xrpl4j-integration-tests/src/test/resources/rippled/rippled.cfg
index 8f909bae9..43c99131c 100644
--- a/xrpl4j-integration-tests/src/test/resources/rippled/rippled.cfg
+++ b/xrpl4j-integration-tests/src/test/resources/rippled/rippled.cfg
@@ -203,4 +203,17 @@ fixReducedOffersV1
fixNFTokenRemint
# 2.0.0 Amendments
XChainBridge
-DID
\ No newline at end of file
+DID
+fixFillOrKill
+fixDisallowIncomingV1
+# 2.1.0 Amendments
+fixNFTokenReserve
+fixInnerObjTemplate
+# 2.1.1 Amendments
+fixAMMOverflowOffer
+# TBD Version
+fixPreviousTxnID
+fixAMMv1_1
+fixEmptyDID
+fixXChainRewardRounding
+PriceOracle
\ No newline at end of file