From 501302088dda16d573725a6d05dbf955c95cee88 Mon Sep 17 00:00:00 2001 From: David Fuelling Date: Wed, 18 Dec 2024 11:42:10 -0500 Subject: [PATCH 1/2] Fix fee calculations in its Escrow and NFT ITs (#585) * Fix Escrow Fee calculations * Fix NFTokeIT fees * Handle invalid fulfillments --- .../model/transactions/EscrowFinish.java | 53 ++-- .../model/transactions/EscrowFinishTest.java | 232 +++++++++--------- .../java/org/xrpl/xrpl4j/tests/NfTokenIT.java | 32 +-- 3 files changed, 168 insertions(+), 149 deletions(-) diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/EscrowFinish.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/EscrowFinish.java index a1a7e7a5b..ad0ff7d33 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/EscrowFinish.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/EscrowFinish.java @@ -32,6 +32,8 @@ import com.ripple.cryptoconditions.CryptoConditionReader; import com.ripple.cryptoconditions.CryptoConditionWriter; import com.ripple.cryptoconditions.Fulfillment; +import com.ripple.cryptoconditions.PreimageSha256Fulfillment; +import com.ripple.cryptoconditions.PreimageSha256Fulfillment.AbstractPreimageSha256Fulfillment; import com.ripple.cryptoconditions.der.DerEncodingException; import org.immutables.value.Value; import org.slf4j.Logger; @@ -41,6 +43,7 @@ import org.xrpl.xrpl4j.model.transactions.AccountSet.AccountSetFlag; import java.util.Arrays; +import java.util.Base64; import java.util.Locale; import java.util.Objects; import java.util.Optional; @@ -69,25 +72,39 @@ static ImmutableEscrowFinish.Builder builder() { * transaction increases if it contains a fulfillment. If the transaction contains a fulfillment, the transaction cost * is 330 drops of XRP plus another 10 drops for every 16 bytes in size of the preimage. * - * @param currentLedgerFeeDrops The number of drops that the ledger demands at present. - * @param fulfillment The {@link Fulfillment} that is being presented to the ledger for computation - * purposes. + * @param currentLedgerBaseFeeDrops The number of drops that the ledger demands at present. + * @param fulfillment The {@link Fulfillment} that is being presented to the ledger for computation + * purposes. * * @return An {@link XrpCurrencyAmount} representing the computed fee. * * @see "https://xrpl.org/escrowfinish.html" */ - static XrpCurrencyAmount computeFee(final XrpCurrencyAmount currentLedgerFeeDrops, final Fulfillment fulfillment) { - Objects.requireNonNull(currentLedgerFeeDrops); + static XrpCurrencyAmount computeFee( + final XrpCurrencyAmount currentLedgerBaseFeeDrops, + final Fulfillment fulfillment + ) { + Objects.requireNonNull(currentLedgerBaseFeeDrops); Objects.requireNonNull(fulfillment); - UnsignedLong newFee = - currentLedgerFeeDrops.value() // <-- usually 10 drops, per the docs. - // <-- https://github.com/ripple/rippled/blob/develop/src/ripple/app/tx/impl/Escrow.cpp#L362 - .plus(UnsignedLong.valueOf(320)) - // <-- 10 drops for each additional 16 bytes. - .plus(UnsignedLong.valueOf(10 * (fulfillment.getDerivedCondition().getCost() / 16))); - return XrpCurrencyAmount.of(newFee); + if (PreimageSha256Fulfillment.class.isAssignableFrom(fulfillment.getClass())) { + + final long fulfillmentByteSize = Base64.getUrlDecoder().decode( + ((PreimageSha256Fulfillment) fulfillment).getEncodedPreimage() + ).length; + // See https://xrpl.org/docs/references/protocol/transactions/types/escrowfinish#escrowfinish-fields for + // computing the additional fee for Escrows. + // In particular: `extraFee = view.fees().base * (32 + (fb->size() / 16))` + // See https://github.com/XRPLF/rippled/blob/master/src/xrpld/app/tx/detail/Escrow.cpp#L368 + final long baseFee = currentLedgerBaseFeeDrops.value().longValue(); + final long extraFeeDrops = baseFee * (32 + (fulfillmentByteSize / 16)); + final long totalFeeDrops = baseFee + extraFeeDrops; // <-- Add an extra base fee + return XrpCurrencyAmount.of( + UnsignedLong.valueOf(totalFeeDrops) + ); + } else { + throw new RuntimeException("Only PreimageSha256Fulfillment is supported."); + } } /** @@ -144,11 +161,11 @@ default TransactionFlags flags() { * *

Note that a similar field does not exist on {@link EscrowCreate}, * {@link org.xrpl.xrpl4j.model.ledger.EscrowObject}, or - * {@link org.xrpl.xrpl4j.model.transactions.metadata.MetaEscrowObject} because {@link EscrowCreate}s with - * malformed conditions will never be included in a ledger by the XRPL. Because of this fact, an + * {@link org.xrpl.xrpl4j.model.transactions.metadata.MetaEscrowObject} because {@link EscrowCreate}s with malformed + * conditions will never be included in a ledger by the XRPL. Because of this fact, an * {@link org.xrpl.xrpl4j.model.ledger.EscrowObject} and - * {@link org.xrpl.xrpl4j.model.transactions.metadata.MetaEscrowObject} will also never contain a malformed - * crypto condition.

+ * {@link org.xrpl.xrpl4j.model.transactions.metadata.MetaEscrowObject} will also never contain a malformed crypto + * condition.

* * @return An {@link Optional} {@link String} containing the hex-encoded PREIMAGE-SHA-256 condition. */ @@ -191,8 +208,8 @@ default TransactionFlags flags() { *

If {@link #condition()} is present but {@link #conditionRawValue()} is empty, we set * {@link #conditionRawValue()} to the underlying value of {@link #condition()}.

*

If {@link #condition()} is empty and {@link #conditionRawValue()} is present, we will set - * {@link #condition()} to the {@link Condition} representing the raw condition value, or leave - * {@link #condition()} empty if {@link #conditionRawValue()} is a malformed {@link Condition}.

+ * {@link #condition()} to the {@link Condition} representing the raw condition value, or leave {@link #condition()} + * empty if {@link #conditionRawValue()} is a malformed {@link Condition}.

* * @return A normalized {@link EscrowFinish}. */ diff --git a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/EscrowFinishTest.java b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/EscrowFinishTest.java index 15358b4c6..42a00da31 100644 --- a/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/EscrowFinishTest.java +++ b/xrpl4j-core/src/test/java/org/xrpl/xrpl4j/model/transactions/EscrowFinishTest.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. @@ -22,6 +22,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertThrows; import com.fasterxml.jackson.core.JsonProcessingException; import com.google.common.io.BaseEncoding; @@ -30,12 +31,16 @@ import com.ripple.cryptoconditions.CryptoConditionReader; import com.ripple.cryptoconditions.CryptoConditionWriter; import com.ripple.cryptoconditions.Fulfillment; +import com.ripple.cryptoconditions.PrefixSha256Fulfillment; import com.ripple.cryptoconditions.PreimageSha256Condition; import com.ripple.cryptoconditions.PreimageSha256Fulfillment; import com.ripple.cryptoconditions.der.DerEncodingException; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; import org.xrpl.xrpl4j.model.jackson.ObjectMapperFactory; import org.xrpl.xrpl4j.model.transactions.ImmutableEscrowFinish.Builder; @@ -86,7 +91,8 @@ public void constructWithNoFulfillmentNoCondition() { //////////////////////////////// // normalizeCondition tests - //////////////////////////////// + + /// ///////////////////////////// @Test void normalizeWithNoConditionNoRawValue() { @@ -131,7 +137,7 @@ void normalizeWithConditionAndRawValueNonMatching() { .conditionRawValue("A0258020E3B0C44298FC1C149ABCD4C8996FB92427AE41E4649B934CA495991B7852B855810100") .build() ).isInstanceOf(IllegalStateException.class) - .hasMessage("condition and conditionRawValue should be equivalent if both are present."); + .hasMessage("condition and conditionRawValue should be equivalent if both are present."); } @Test @@ -178,10 +184,9 @@ void normalizeWithNoConditionAndRawValueForMalformedCondition() { } /** - * This tests the case where conditionRawValue is present and is parseable to a Condition but when the - * parsed Condition is written to a byte array, the value differs from the conditionRawValue bytes. This - * can occur if the condition raw value contains a valid condition in the first 32 bytes, but also includes - * extra bytes afterward. + * This tests the case where conditionRawValue is present and is parseable to a Condition but when the parsed + * Condition is written to a byte array, the value differs from the conditionRawValue bytes. This can occur if the + * condition raw value contains a valid condition in the first 32 bytes, but also includes extra bytes afterward. */ @Test void normalizeConditionWithRawValueThatIsParseableButNotValid() { @@ -217,7 +222,8 @@ void normalizeWithNoConditionAndRawValueForBadHexLengthCondition() { //////////////////////////////// // normalizeFulfillment tests - //////////////////////////////// + + /// ///////////////////////////// @Test void normalizeWithNoFulfillmentNoRawValue() { @@ -309,10 +315,10 @@ void normalizeWithNoFulfillmentAndRawValueForMalformedFulfillment() { } /** - * This tests the case where fulfillmentRawValue is present and is parseable to a Fulfillment but when the - * parsed Fulfillment is written to a byte array, the value differs from the fulfillmentRawValue bytes. This - * can occur if the fulfillment raw value contains a valid fulfillment in the first 32 bytes, but also includes - * extra bytes afterward, such as in transaction 138543329687544CDAFCD3AB0DCBFE9C4F8E710397747BA7155F19426F493C8D. + * This tests the case where fulfillmentRawValue is present and is parseable to a Fulfillment but when the parsed + * Fulfillment is written to a byte array, the value differs from the fulfillmentRawValue bytes. This can occur if the + * fulfillment raw value contains a valid fulfillment in the first 32 bytes, but also includes extra bytes afterward, + * such as in transaction 138543329687544CDAFCD3AB0DCBFE9C4F8E710397747BA7155F19426F493C8D. */ @Test void normalizeFulfillmentWithRawValueThatIsParseableButNotValid() { @@ -346,8 +352,54 @@ void normalizeWithNoFulfillmentAndRawValueForBadHexLengthFulfillment() { assertThat(actual.fulfillmentRawValue()).isNotEmpty().get().isEqualTo("123"); } - @Test - public void testNormalizeWithVariousFulfillmentSizes() { + @ParameterizedTest + @CsvSource( { + // Simulate 10 drop base fee + "0,10,330", // Standard 10 drop fee, plus 320 drops + "1,10,330", // Standard 10 drop fee, plus 320 drops + "2,10,330", // Standard 10 drop fee, plus 320 drops + "15,10,330", // Standard 10 drop fee, plus 320 drops + "16,10,340", // Standard 10 drop fee, plus 320 drops + 10 drops for 1 chunk of 16 bytes + "17,10,340", // Standard 10 drop fee, plus 320 drops + 10 drops for 1 chunk of 16 bytes + "31,10,340", // Standard 10 drop fee, plus 320 drops + 10 drops for 1 chunk of 16 bytes + "32,10,350", // Standard 10 drop fee, plus 320 drops + 20 drops for 2 chunks of 16 bytes + "33,10,350", // Standard 10 drop fee, plus 320 drops + 20 drops for 2 chunks of 16 bytes + // Simulate 100 drop base fee + "0,100,3300", // Standard 100 drop fee, plus 3200 drops + "1,100,3300", // Standard 100 drop fee, plus 3200 drops + "2,100,3300", // Standard 100 drop fee, plus 3200 drops + "15,100,3300", // Standard 100 drop fee, plus 3200 drops + "16,100,3400", // Standard 100 drop fee, plus 3200 drops + 100 drops for 1 chunk of 16 bytes + "17,100,3400", // Standard 100 drop fee, plus 3200 drops + 100 drops for 1 chunk of 16 bytes + "31,100,3400", // Standard 100 drop fee, plus 3200 drops + 100 drops for 1 chunk of 16 bytes + "32,100,3500", // Standard 100 drop fee, plus 3200 drops + 200 drops for 2 chunks of 16 bytes + "33,100,3500", // Standard 100 drop fee, plus 3200 drops + 200 drops for 2 chunks of 16 bytes + // Simulate 200 drop base fee + "0,200,6600", // Standard 200 drop fee, plus 6400 drops + "1,200,6600", // Standard 200 drop fee, plus 6400 drops + "2,200,6600", // Standard 200 drop fee, plus 6400 drops + "15,200,6600",// Standard 200 drop fee, plus 6400 drops + "16,200,6800",// Standard 200 drop fee, plus 6400 drops + 200 drops for 1 chunk of 16 bytes + "17,200,6800",// Standard 200 drop fee, plus 6400 drops + 200 drops for 1 chunk of 16 bytes + "31,200,6800",// Standard 200 drop fee, plus 6400 drops + 200 drops for 1 chunk of 16 bytes + "32,200,7000",// Standard 200 drop fee, plus 6400 drops + 400 drops for 2 chunks of 16 bytes + "33,200,7000",// Standard 200 drop fee, plus 6400 drops + 400 drops for 2 chunks of 16 bytes + // Simulate 5000 drop base fee + "0,5000,165000", // Standard 5000 drop fee, plus 160000 drops + "1,5000,165000", // Standard 5000 drop fee, plus 160000 drops + "2,5000,165000", // Standard 5000 drop fee, plus 160000 drops + "15,5000,165000",// Standard 5000 drop fee, plus 160000 drops + "16,5000,170000",// Standard 5000 drop fee, plus 160000 drops + 5000 drops for 1 chunk of 16 bytes + "17,5000,170000",// Standard 5000 drop fee, plus 160000 drops + 5000 drops for 1 chunk of 16 bytes + "31,5000,170000",// Standard 5000 drop fee, plus 160000 drops + 5000 drops for 1 chunk of 16 bytes + "32,5000,175000",// Standard 5000 drop fee, plus 160000 drops + 10000 drops for 2 chunks of 16 bytes + "33,5000,175000",// Standard 5000 drop fee, plus 160000 drops + 10000 drops for 2 chunks of 16 bytes + }) + public void testNormalizeWithVariousFulfillmentSizes(int numBytes, int baseFee, int expectedDrops) { + ///////////////// + // Test Normalize + ///////////////// + Builder builder = EscrowFinish.builder() .fee(XrpCurrencyAmount.ofDrops(1)) .account(Address.of("rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59Ba")) @@ -355,113 +407,63 @@ public void testNormalizeWithVariousFulfillmentSizes() { .owner(Address.of("rN7n7otQDd6FczFgLdSqtcsAUxDkw6fzRH")) .offerSequence(UnsignedInteger.ZERO); - // 0 bytes - Fulfillment fulfillment = PreimageSha256Fulfillment.from(new byte[0]); - builder.fulfillment(fulfillment); - builder.fee(EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), fulfillment)); - builder.condition(fulfillment.getDerivedCondition()); - // Standard 10 drop fee, plus 320 drops - Assertions.assertThat(builder.build().fee()).isEqualTo(XrpCurrencyAmount.ofDrops(330)); - - // 1 byte - fulfillment = PreimageSha256Fulfillment.from(new byte[1]); - builder.fulfillment(fulfillment); - builder.fee(EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), fulfillment)); - builder.condition(fulfillment.getDerivedCondition()); - // Standard 10 drop fee, plus 320 drops - Assertions.assertThat(builder.build().fee()).isEqualTo(XrpCurrencyAmount.ofDrops(330)); - - // 2 byte2 - fulfillment = PreimageSha256Fulfillment.from(new byte[2]); - builder.fulfillment(fulfillment); - builder.fee(EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), fulfillment)); - builder.condition(fulfillment.getDerivedCondition()); - // Standard 10 drop fee, plus 320 drops - Assertions.assertThat(builder.build().fee()).isEqualTo(XrpCurrencyAmount.ofDrops(330)); - - // 15 bytes - fulfillment = PreimageSha256Fulfillment.from(new byte[15]); - builder.fulfillment(fulfillment); - builder.fee(EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), fulfillment)); - builder.condition(fulfillment.getDerivedCondition()); - // Standard 10 drop fee, plus 320 drops - Assertions.assertThat(builder.build().fee()).isEqualTo(XrpCurrencyAmount.ofDrops(330)); - - // 16 bytes - fulfillment = PreimageSha256Fulfillment.from(new byte[16]); - builder.fulfillment(fulfillment); - builder.fee(EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), fulfillment)); - builder.condition(fulfillment.getDerivedCondition()); - // Standard 10 drop fee, plus 320 drops + 10 drops for 16 bytes - Assertions.assertThat(builder.build().fee()).isEqualTo(XrpCurrencyAmount.ofDrops(340)); - - // 17 bytes - fulfillment = PreimageSha256Fulfillment.from(new byte[17]); - builder.fulfillment(fulfillment); - builder.fee(EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), fulfillment)); - builder.condition(fulfillment.getDerivedCondition()); - // Standard 10 drop fee, plus 320 drops + 10 drops for 16 bytes - Assertions.assertThat(builder.build().fee()).isEqualTo(XrpCurrencyAmount.ofDrops(340)); - - // 31 bytes - fulfillment = PreimageSha256Fulfillment.from(new byte[31]); + Fulfillment fulfillment = PreimageSha256Fulfillment.from(new byte[numBytes]); builder.fulfillment(fulfillment); - builder.fee(EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), fulfillment)); + builder.fee(EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(baseFee), fulfillment)); builder.condition(fulfillment.getDerivedCondition()); - // Standard 10 drop fee, plus 320 drops + 10 drops for 16 bytes - Assertions.assertThat(builder.build().fee()).isEqualTo(XrpCurrencyAmount.ofDrops(340)); - - // 32 bytes - fulfillment = PreimageSha256Fulfillment.from(new byte[32]); - builder.fulfillment(fulfillment); - builder.fee(EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), fulfillment)); - builder.condition(fulfillment.getDerivedCondition()); - // Standard 10 drop fee, plus 320 drops + 20 drops for 32 bytes + // Standard fee, plus (32 * base-fee) drops, plus one base-fee for every 16 fulfillment bytes. // (see https://xrpl.org/transaction-cost.html#fee-levels) - Assertions.assertThat(builder.build().fee()).isEqualTo(XrpCurrencyAmount.ofDrops(350)); + Assertions.assertThat(builder.build().fee()).isEqualTo(XrpCurrencyAmount.ofDrops(expectedDrops)); - // 33 bytes - fulfillment = PreimageSha256Fulfillment.from(new byte[33]); - builder.fulfillment(fulfillment); - builder.fee(EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), fulfillment)); - builder.condition(fulfillment.getDerivedCondition()); - // Standard 10 drop fee, plus 320 drops + 20 drops for 32 bytes - Assertions.assertThat(builder.build().fee()).isEqualTo(XrpCurrencyAmount.ofDrops(350)); + /////////////////// + // Test Compute Fee + /////////////////// + final XrpCurrencyAmount computedFee = EscrowFinish.computeFee( + XrpCurrencyAmount.ofDrops(baseFee), PreimageSha256Fulfillment.from(new byte[numBytes]) + ); + assertThat(computedFee).isEqualTo(XrpCurrencyAmount.ofDrops(expectedDrops)); } @Test - public void testComputeFee() { - // 0 bytes - assertThat( - EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), PreimageSha256Fulfillment.from(new byte[0]))) - .isEqualTo(XrpCurrencyAmount.ofDrops(330)); - assertThat( - EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), PreimageSha256Fulfillment.from(new byte[1]))) - .isEqualTo(XrpCurrencyAmount.ofDrops(330)); - assertThat( - EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), PreimageSha256Fulfillment.from(new byte[2]))) - .isEqualTo(XrpCurrencyAmount.ofDrops(330)); - assertThat( - EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), PreimageSha256Fulfillment.from(new byte[15]))) - .isEqualTo(XrpCurrencyAmount.ofDrops(330)); - assertThat( - EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), PreimageSha256Fulfillment.from(new byte[16]))) - .isEqualTo(XrpCurrencyAmount.ofDrops(340)); - assertThat( - EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), PreimageSha256Fulfillment.from(new byte[17]))) - .isEqualTo(XrpCurrencyAmount.ofDrops(340)); - assertThat( - EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), PreimageSha256Fulfillment.from(new byte[31]))) - .isEqualTo(XrpCurrencyAmount.ofDrops(340)); - assertThat( - EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), PreimageSha256Fulfillment.from(new byte[32]))) - .isEqualTo(XrpCurrencyAmount.ofDrops(350)); - assertThat( - EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), PreimageSha256Fulfillment.from(new byte[33]))) - .isEqualTo(XrpCurrencyAmount.ofDrops(350)); - assertThat( - EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), PreimageSha256Fulfillment.from(new byte[64]))) - .isEqualTo(XrpCurrencyAmount.ofDrops(370)); + public void testComputeFeeWithWrongFulfillment() { + ////////////////////// + // Invalid Fulfillment + ////////////////////// + + PreimageSha256Fulfillment preimageSha256Fulfillment = PreimageSha256Fulfillment.from(new byte[10]); + PrefixSha256Fulfillment prefixFulfillment = PrefixSha256Fulfillment.from( + new byte[1], 50, preimageSha256Fulfillment + ); + Exception thrownException = assertThrows( + Exception.class, () -> EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), prefixFulfillment) + ); + assertThat(thrownException instanceof RuntimeException).isTrue(); + assertThat(thrownException.getMessage()).isEqualTo("Only PreimageSha256Fulfillment is supported."); } + @Test + public void testComputeFeeWithNullParams() { + PreimageSha256Fulfillment preimageSha256Fulfillment = PreimageSha256Fulfillment.from(new byte[10]); + PrefixSha256Fulfillment prefixFulfillment = PrefixSha256Fulfillment.from( + new byte[1], 50, preimageSha256Fulfillment + ); + + ////////////////////// + // Null Base Fee + ////////////////////// + Exception thrownException = assertThrows( + Exception.class, () -> EscrowFinish.computeFee(null, prefixFulfillment) + ); + assertThat(thrownException instanceof NullPointerException).isTrue(); + assertThat(thrownException.getMessage()).isNull(); + + ////////////////////// + // Null Fulfillment + ////////////////////// + thrownException = assertThrows( + Exception.class, () -> EscrowFinish.computeFee(XrpCurrencyAmount.ofDrops(10), null) + ); + assertThat(thrownException instanceof NullPointerException).isTrue(); + assertThat(thrownException.getMessage()).isNull(); + } } 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 5a3c91f4e..e2036c8ab 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 @@ -87,7 +87,7 @@ void mint() throws JsonRpcClientErrorException, JsonProcessingException { NfTokenMint nfTokenMint = NfTokenMint.builder() .tokenTaxon(UnsignedLong.ONE) .account(keyPair.publicKey().deriveAddress()) - .fee(XrpCurrencyAmount.ofDrops(50)) + .fee(FeeUtils.computeNetworkFees(xrplClient.fee()).recommendedFee()) .signingPublicKey(keyPair.publicKey()) .sequence(accountInfoResult.accountData().sequence()) .uri(uri) @@ -174,7 +174,7 @@ void mintFromOtherMinterAccount() throws JsonRpcClientErrorException, JsonProces NfTokenMint nfTokenMint = NfTokenMint.builder() .tokenTaxon(UnsignedLong.ONE) .account(minterKeyPair.publicKey().deriveAddress()) - .fee(XrpCurrencyAmount.ofDrops(50)) + .fee(FeeUtils.computeNetworkFees(xrplClient.fee()).recommendedFee()) .signingPublicKey(minterKeyPair.publicKey()) .sequence(minterAccountInfo.accountData().sequence()) .issuer(keyPair.publicKey().deriveAddress()) @@ -215,7 +215,7 @@ void mintAndBurn() throws JsonRpcClientErrorException, JsonProcessingException { NfTokenMint nfTokenMint = NfTokenMint.builder() .tokenTaxon(UnsignedLong.ONE) .account(keyPair.publicKey().deriveAddress()) - .fee(XrpCurrencyAmount.ofDrops(50)) + .fee(FeeUtils.computeNetworkFees(xrplClient.fee()).recommendedFee()) .signingPublicKey(keyPair.publicKey()) .sequence(accountInfoResult.accountData().sequence()) .uri(uri) @@ -239,7 +239,7 @@ void mintAndBurn() throws JsonRpcClientErrorException, JsonProcessingException { NfTokenBurn nfTokenBurn = NfTokenBurn.builder() .nfTokenId(tokenId) .account(keyPair.publicKey().deriveAddress()) - .fee(XrpCurrencyAmount.ofDrops(50)) + .fee(FeeUtils.computeNetworkFees(xrplClient.fee()).recommendedFee()) .signingPublicKey(keyPair.publicKey()) .sequence(accountInfoResult.accountData().sequence().plus(UnsignedInteger.ONE)) .build(); @@ -291,7 +291,7 @@ void mintAndCreateOffer() throws JsonRpcClientErrorException, JsonProcessingExce NfTokenMint nfTokenMint = NfTokenMint.builder() .tokenTaxon(UnsignedLong.ONE) .account(keyPair.publicKey().deriveAddress()) - .fee(XrpCurrencyAmount.ofDrops(50)) + .fee(FeeUtils.computeNetworkFees(xrplClient.fee()).recommendedFee()) .signingPublicKey(keyPair.publicKey()) .sequence(accountInfoResult.accountData().sequence()) .uri(uri) @@ -317,7 +317,7 @@ void mintAndCreateOffer() throws JsonRpcClientErrorException, JsonProcessingExce NfTokenCreateOffer nfTokenCreateOffer = NfTokenCreateOffer.builder() .account(keyPair.publicKey().deriveAddress()) .nfTokenId(tokenId) - .fee(XrpCurrencyAmount.ofDrops(50)) + .fee(FeeUtils.computeNetworkFees(xrplClient.fee()).recommendedFee()) .sequence(accountInfoResult.accountData().sequence().plus(UnsignedInteger.ONE)) .amount(XrpCurrencyAmount.ofDrops(1000)) .flags(NfTokenCreateOfferFlags.builder() @@ -382,7 +382,7 @@ void mintAndCreateThenAcceptOffer() throws JsonRpcClientErrorException, JsonProc NfTokenMint nfTokenMint = NfTokenMint.builder() .tokenTaxon(UnsignedLong.ONE) .account(keypair.publicKey().deriveAddress()) - .fee(XrpCurrencyAmount.ofDrops(50)) + .fee(FeeUtils.computeNetworkFees(xrplClient.fee()).recommendedFee()) .signingPublicKey(keypair.publicKey()) .sequence(accountInfoResult.accountData().sequence()) .flags(NfTokenMintFlags.builder() @@ -416,7 +416,7 @@ void mintAndCreateThenAcceptOffer() throws JsonRpcClientErrorException, JsonProc .account(wallet2.publicKey().deriveAddress()) .owner(keypair.publicKey().deriveAddress()) .nfTokenId(tokenId) - .fee(XrpCurrencyAmount.ofDrops(50)) + .fee(FeeUtils.computeNetworkFees(xrplClient.fee()).recommendedFee()) .sequence(accountInfoResult2.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(1000)) .signingPublicKey(wallet2.publicKey()) @@ -470,7 +470,7 @@ void mintAndCreateThenAcceptOffer() throws JsonRpcClientErrorException, JsonProc NfTokenAcceptOffer nfTokenAcceptOffer = NfTokenAcceptOffer.builder() .account(keypair.publicKey().deriveAddress()) .buyOffer(nftBuyOffersResult.offers().get(0).nftOfferIndex()) - .fee(XrpCurrencyAmount.ofDrops(50)) + .fee(FeeUtils.computeNetworkFees(xrplClient.fee()).recommendedFee()) .sequence(accountInfoResult.accountData().sequence().plus(UnsignedInteger.ONE)) .signingPublicKey(keypair.publicKey()) .build(); @@ -530,7 +530,7 @@ void mintAndCreateOfferThenCancelOffer() throws JsonRpcClientErrorException, Jso NfTokenMint nfTokenMint = NfTokenMint.builder() .tokenTaxon(UnsignedLong.ONE) .account(keypair.publicKey().deriveAddress()) - .fee(XrpCurrencyAmount.ofDrops(50)) + .fee(FeeUtils.computeNetworkFees(xrplClient.fee()).recommendedFee()) .signingPublicKey(keypair.publicKey()) .sequence(accountInfoResult.accountData().sequence()) .uri(uri) @@ -552,7 +552,7 @@ void mintAndCreateOfferThenCancelOffer() throws JsonRpcClientErrorException, Jso NfTokenCreateOffer nfTokenCreateOffer = NfTokenCreateOffer.builder() .account(keypair.publicKey().deriveAddress()) .nfTokenId(tokenId) - .fee(XrpCurrencyAmount.ofDrops(50)) + .fee(FeeUtils.computeNetworkFees(xrplClient.fee()).recommendedFee()) .sequence(accountInfoResult.accountData().sequence().plus(UnsignedInteger.ONE)) .amount(XrpCurrencyAmount.ofDrops(1000)) .flags(NfTokenCreateOfferFlags.SELL_NFTOKEN) @@ -594,7 +594,7 @@ void mintAndCreateOfferThenCancelOffer() throws JsonRpcClientErrorException, Jso NfTokenCancelOffer nfTokenCancelOffer = NfTokenCancelOffer.builder() .addTokenOffers(nftSellOffersResult.offers().get(0).nftOfferIndex()) .account(keypair.publicKey().deriveAddress()) - .fee(XrpCurrencyAmount.ofDrops(50)) + .fee(FeeUtils.computeNetworkFees(xrplClient.fee()).recommendedFee()) .sequence(accountInfoResult.accountData().sequence().plus(UnsignedInteger.valueOf(2))) .signingPublicKey(keypair.publicKey()) .build(); @@ -643,7 +643,7 @@ void acceptOfferDirectModeWithBrokerFee() throws JsonRpcClientErrorException, Js NfTokenMint nfTokenMint = NfTokenMint.builder() .tokenTaxon(UnsignedLong.ONE) .account(keypair.publicKey().deriveAddress()) - .fee(XrpCurrencyAmount.ofDrops(50)) + .fee(FeeUtils.computeNetworkFees(xrplClient.fee()).recommendedFee()) .signingPublicKey(keypair.publicKey()) .sequence(accountInfoResult.accountData().sequence()) .flags(NfTokenMintFlags.builder() @@ -669,7 +669,7 @@ void acceptOfferDirectModeWithBrokerFee() throws JsonRpcClientErrorException, Js NfTokenCreateOffer nfTokenCreateSellOffer = NfTokenCreateOffer.builder() .account(keypair.publicKey().deriveAddress()) .nfTokenId(tokenId) - .fee(XrpCurrencyAmount.ofDrops(50)) + .fee(FeeUtils.computeNetworkFees(xrplClient.fee()).recommendedFee()) .sequence(accountInfoResult.accountData().sequence().plus(UnsignedInteger.ONE)) .amount(XrpCurrencyAmount.ofDrops(1000)) .signingPublicKey(keypair.publicKey()) @@ -719,7 +719,7 @@ void acceptOfferDirectModeWithBrokerFee() throws JsonRpcClientErrorException, Js .account(keypair2.publicKey().deriveAddress()) .owner(keypair.publicKey().deriveAddress()) .nfTokenId(tokenId) - .fee(XrpCurrencyAmount.ofDrops(50)) + .fee(FeeUtils.computeNetworkFees(xrplClient.fee()).recommendedFee()) .sequence(accountInfoResult2.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(1000)) .signingPublicKey(keypair2.publicKey()) @@ -767,7 +767,7 @@ void acceptOfferDirectModeWithBrokerFee() throws JsonRpcClientErrorException, Js .account(wallet3.publicKey().deriveAddress()) .buyOffer(nftBuyOffersResult.offers().get(0).nftOfferIndex()) .sellOffer(nftSellOffersResult.offers().get(0).nftOfferIndex()) - .fee(XrpCurrencyAmount.ofDrops(50)) + .fee(FeeUtils.computeNetworkFees(xrplClient.fee()).recommendedFee()) .sequence(accountInfoResult3.accountData().sequence()) .signingPublicKey(wallet3.publicKey()) .build(); From 613339d9f3f6f5401fc59c73f7036da6bf77a8be Mon Sep 17 00:00:00 2001 From: nkramer44 Date: Wed, 18 Dec 2024 17:01:55 -0500 Subject: [PATCH 2/2] Fix AmmWithdraw LPTokenIn field name (#587) * rename LPTokensIn to LPTokenIn in AmmWithdraw * checkstyle and test --- .../model/transactions/AmmWithdraw.java | 2 +- .../binary/BinarySerializationTests.java | 279 ++++++++++++++++++ .../model/transactions/AmmWithdrawTest.java | 6 +- 3 files changed, 283 insertions(+), 4 deletions(-) diff --git a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/AmmWithdraw.java b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/AmmWithdraw.java index 89ad06bec..93f1bd968 100644 --- a/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/AmmWithdraw.java +++ b/xrpl4j-core/src/main/java/org/xrpl/xrpl4j/model/transactions/AmmWithdraw.java @@ -96,7 +96,7 @@ static ImmutableAmmWithdraw.Builder builder() { * * @return An optionally present {@link IssuedCurrencyAmount}. */ - @JsonProperty("LPTokensIn") + @JsonProperty("LPTokenIn") Optional lpTokensIn(); } 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 298e019a6..11df2d770 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 @@ -37,6 +37,8 @@ import org.xrpl.xrpl4j.crypto.keys.PublicKey; import org.xrpl.xrpl4j.crypto.signing.Signature; import org.xrpl.xrpl4j.model.flags.AccountSetTransactionFlags; +import org.xrpl.xrpl4j.model.flags.AmmDepositFlags; +import org.xrpl.xrpl4j.model.flags.AmmWithdrawFlags; import org.xrpl.xrpl4j.model.flags.OfferCreateFlags; import org.xrpl.xrpl4j.model.flags.PaymentChannelClaimFlags; import org.xrpl.xrpl4j.model.flags.PaymentFlags; @@ -45,6 +47,8 @@ import org.xrpl.xrpl4j.model.flags.TrustSetFlags; import org.xrpl.xrpl4j.model.flags.XChainModifyBridgeFlags; import org.xrpl.xrpl4j.model.jackson.ObjectMapperFactory; +import org.xrpl.xrpl4j.model.ledger.AuthAccount; +import org.xrpl.xrpl4j.model.ledger.AuthAccountWrapper; import org.xrpl.xrpl4j.model.ledger.Issue; import org.xrpl.xrpl4j.model.ledger.RippleStateObject; import org.xrpl.xrpl4j.model.ledger.SignerEntry; @@ -52,6 +56,12 @@ 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.AmmBid; +import org.xrpl.xrpl4j.model.transactions.AmmCreate; +import org.xrpl.xrpl4j.model.transactions.AmmDelete; +import org.xrpl.xrpl4j.model.transactions.AmmDeposit; +import org.xrpl.xrpl4j.model.transactions.AmmVote; +import org.xrpl.xrpl4j.model.transactions.AmmWithdraw; import org.xrpl.xrpl4j.model.transactions.AssetPrice; import org.xrpl.xrpl4j.model.transactions.CheckCancel; import org.xrpl.xrpl4j.model.transactions.CheckCash; @@ -67,6 +77,10 @@ import org.xrpl.xrpl4j.model.transactions.EscrowCreate; import org.xrpl.xrpl4j.model.transactions.EscrowFinish; import org.xrpl.xrpl4j.model.transactions.Hash256; +import org.xrpl.xrpl4j.model.transactions.ImmutableAmmBid; +import org.xrpl.xrpl4j.model.transactions.ImmutableAmmCreate; +import org.xrpl.xrpl4j.model.transactions.ImmutableAmmDelete; +import org.xrpl.xrpl4j.model.transactions.ImmutableAmmDeposit; import org.xrpl.xrpl4j.model.transactions.ImmutableDidDelete; import org.xrpl.xrpl4j.model.transactions.ImmutableDidSet; import org.xrpl.xrpl4j.model.transactions.ImmutableOracleDelete; @@ -92,6 +106,7 @@ 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.TradingFee; import org.xrpl.xrpl4j.model.transactions.Transaction; import org.xrpl.xrpl4j.model.transactions.TrustSet; import org.xrpl.xrpl4j.model.transactions.XChainAccountCreateCommit; @@ -2256,6 +2271,270 @@ void serializeOracleDelete() throws JsonProcessingException { assertSerializesAndDeserializes(oracleDelete, expectedBinary); } + @Test + void serializeAmmWithdraw() throws JsonProcessingException { + AmmWithdraw ammWithdraw = AmmWithdraw.builder() + .account(Address.of("rpgAg9CgNV9MYUECHTx26h1PEARhreuap7")) + .fee(XrpCurrencyAmount.ofDrops(10)) + .sequence(UnsignedInteger.ONE) + .signingPublicKey( + PublicKey.fromBase16EncodedPublicKey("02B4A8F64B97151FA303F86417751B7EA5AF1D0014FCC110C234D04AF15F3F654A") + ) + .flags(AmmWithdrawFlags.LP_TOKEN) + .asset(Issue.builder() + .currency("4755534400000000000000000000000000000000") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .asset2(Issue.builder() + .currency("524C555344000000000000000000000000000000") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .lpTokensIn( + IssuedCurrencyAmount.builder() + .value("200") + .currency("LPT") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .amount( + IssuedCurrencyAmount.builder() + .value("200") + .currency("LPT") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .amount2( + IssuedCurrencyAmount.builder() + .value("200") + .currency("LPT") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .effectivePrice( + IssuedCurrencyAmount.builder() + .value("200") + .currency("LPT") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .build(); + + String expectedBinary = "1200252200010000240000000161D5071AFD498D00000000000000000000000000004C50540000" + + "000000F78AD4FDA66653106AE45EE5FE683C457E6BA9C568400000000000000A6BD5071AFD498D0000000000000000000000" + + "0000004C50540000000000F78AD4FDA66653106AE45EE5FE683C457E6BA9C5601AD5071AFD498D0000000000000000000000" + + "0000004C50540000000000F78AD4FDA66653106AE45EE5FE683C457E6BA9C5601BD5071AFD498D0000000000000000000000" + + "0000004C50540000000000F78AD4FDA66653106AE45EE5FE683C457E6BA9C5732102B4A8F64B97151FA303F86417751B7EA5" + + "AF1D0014FCC110C234D04AF15F3F654A81141285FD19EDD3FB7146B87251AFB0E17742B37271031847555344000000000000" + + "00000000000000000000F78AD4FDA66653106AE45EE5FE683C457E6BA9C50418524C55534400000000000000000000000000" + + "0000F78AD4FDA66653106AE45EE5FE683C457E6BA9C5"; + assertSerializesAndDeserializes(ammWithdraw, expectedBinary); + } + + @Test + void serializeAmmBid() throws JsonProcessingException { + AmmBid ammBid = AmmBid.builder() + .account(Address.of("rpgAg9CgNV9MYUECHTx26h1PEARhreuap7")) + .fee(XrpCurrencyAmount.ofDrops(10)) + .sequence(UnsignedInteger.ONE) + .signingPublicKey( + PublicKey.fromBase16EncodedPublicKey("02B4A8F64B97151FA303F86417751B7EA5AF1D0014FCC110C234D04AF15F3F654A")) + .asset(Issue.builder() + .currency("4755534400000000000000000000000000000000") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .asset2(Issue.builder() + .currency("524C555344000000000000000000000000000000") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .bidMin( + IssuedCurrencyAmount.builder() + .value("200") + .currency("LPT") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .bidMax( + IssuedCurrencyAmount.builder() + .value("200") + .currency("LPT") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .addAuthAccounts( + AuthAccountWrapper.of(AuthAccount.of(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy"))) + ) + .build(); + + String expectedBinary = "120027240000000168400000000000000A6CD5071AFD498D00000000000000000000000000004C5" + + "0540000000000F78AD4FDA66653106AE45EE5FE683C457E6BA9C56DD5071AFD498D00000000000000000000000000004C5054" + + "0000000000F78AD4FDA66653106AE45EE5FE683C457E6BA9C5732102B4A8F64B97151FA303F86417751B7EA5AF1D0014FCC11" + + "0C234D04AF15F3F654A81141285FD19EDD3FB7146B87251AFB0E17742B37271F019E01B8114F78AD4FDA66653106AE45EE5FE" + + "683C457E6BA9C5E1F103184755534400000000000000000000000000000000F78AD4FDA66653106AE45EE5FE683C457E6BA9" + + "C50418524C555344000000000000000000000000000000F78AD4FDA66653106AE45EE5FE683C457E6BA9C5"; + + assertSerializesAndDeserializes(ammBid, expectedBinary); + } + + @Test + void serializeAmmCreate() throws JsonProcessingException { + AmmCreate ammCreate = AmmCreate.builder() + .account(Address.of("rpgAg9CgNV9MYUECHTx26h1PEARhreuap7")) + .fee(XrpCurrencyAmount.ofDrops(10)) + .sequence(UnsignedInteger.ONE) + .signingPublicKey( + PublicKey.fromBase16EncodedPublicKey("02B4A8F64B97151FA303F86417751B7EA5AF1D0014FCC110C234D04AF15F3F654A") + ) + .amount( + IssuedCurrencyAmount.builder() + .value("200") + .currency("LPT") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .amount2( + IssuedCurrencyAmount.builder() + .value("200") + .currency("LPT") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .tradingFee(TradingFee.of(UnsignedInteger.ONE)) + .build(); + + String expectedBinary = "120023150001240000000161D5071AFD498D00000000000000000000000000004C505400000000" + + "00F78AD4FDA66653106AE45EE5FE683C457E6BA9C568400000000000000A6BD5071AFD498D00000000000000000000000000" + + "004C50540000000000F78AD4FDA66653106AE45EE5FE683C457E6BA9C5732102B4A8F64B97151FA303F86417751B7EA5AF1D" + + "0014FCC110C234D04AF15F3F654A81141285FD19EDD3FB7146B87251AFB0E17742B37271"; + + assertSerializesAndDeserializes(ammCreate, expectedBinary); + } + + @Test + void serializeAmmDelete() throws JsonProcessingException { + AmmDelete ammDelete = AmmDelete.builder() + .account(Address.of("rpgAg9CgNV9MYUECHTx26h1PEARhreuap7")) + .fee(XrpCurrencyAmount.ofDrops(10)) + .sequence(UnsignedInteger.ONE) + .signingPublicKey( + PublicKey.fromBase16EncodedPublicKey("02B4A8F64B97151FA303F86417751B7EA5AF1D0014FCC110C234D04AF15F3F654A") + ) + .asset(Issue.builder() + .currency("4755534400000000000000000000000000000000") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .asset2(Issue.builder() + .currency("524C555344000000000000000000000000000000") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .build(); + + String expectedBinary = "120028240000000168400000000000000A732102B4A8F64B97151FA303F86417751B7EA5AF1" + + "D0014FCC110C234D04AF15F3F654A81141285FD19EDD3FB7146B87251AFB0E17742B37271031847555344000000000000" + + "00000000000000000000F78AD4FDA66653106AE45EE5FE683C457E6BA9C50418524C5553440000000000000000000000" + + "00000000F78AD4FDA66653106AE45EE5FE683C457E6BA9C5"; + + assertSerializesAndDeserializes(ammDelete, expectedBinary); + } + + @Test + void serializeAmmDeposit() throws JsonProcessingException { + AmmDeposit ammDeposit = AmmDeposit.builder() + .account(Address.of("rpgAg9CgNV9MYUECHTx26h1PEARhreuap7")) + .fee(XrpCurrencyAmount.ofDrops(10)) + .sequence(UnsignedInteger.ONE) + .signingPublicKey( + PublicKey.fromBase16EncodedPublicKey("02B4A8F64B97151FA303F86417751B7EA5AF1D0014FCC110C234D04AF15F3F654A") + ) + .flags(AmmDepositFlags.SINGLE_ASSET) + .asset(Issue.builder() + .currency("4755534400000000000000000000000000000000") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .asset2(Issue.builder() + .currency("524C555344000000000000000000000000000000") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .amount( + IssuedCurrencyAmount.builder() + .value("200") + .currency("LPT") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .amount2( + IssuedCurrencyAmount.builder() + .value("200") + .currency("LPT") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .effectivePrice( + IssuedCurrencyAmount.builder() + .value("200") + .currency("LPT") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .lpTokenOut( + IssuedCurrencyAmount.builder() + .value("200") + .currency("LPT") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .tradingFee(TradingFee.ofPercent(BigDecimal.ONE)) + .build(); + + String expectedBinary = "1200241503E82200080000240000000161D5071AFD498D00000000000000000000000000004C50" + + "540000000000F78AD4FDA66653106AE45EE5FE683C457E6BA9C568400000000000000A6BD5071AFD498D0000000000000000" + + "0000000000004C50540000000000F78AD4FDA66653106AE45EE5FE683C457E6BA9C56019D5071AFD498D0000000000000000" + + "0000000000004C50540000000000F78AD4FDA66653106AE45EE5FE683C457E6BA9C5601BD5071AFD498D0000000000000000" + + "0000000000004C50540000000000F78AD4FDA66653106AE45EE5FE683C457E6BA9C5732102B4A8F64B97151FA303F8641775" + + "1B7EA5AF1D0014FCC110C234D04AF15F3F654A81141285FD19EDD3FB7146B87251AFB0E17742B37271031847555344000000" + + "00000000000000000000000000F78AD4FDA66653106AE45EE5FE683C457E6BA9C50418524C55534400000000000000000000" + + "0000000000F78AD4FDA66653106AE45EE5FE683C457E6BA9C5"; + + assertSerializesAndDeserializes(ammDeposit, expectedBinary); + } + + @Test + void serializeAmmVote() throws JsonProcessingException { + AmmVote ammVote = AmmVote.builder() + .account(Address.of("rpgAg9CgNV9MYUECHTx26h1PEARhreuap7")) + .fee(XrpCurrencyAmount.ofDrops(10)) + .sequence(UnsignedInteger.ONE) + .signingPublicKey( + PublicKey.fromBase16EncodedPublicKey("02B4A8F64B97151FA303F86417751B7EA5AF1D0014FCC110C234D04AF15F3F654A") + ) + .asset(Issue.builder() + .currency("4755534400000000000000000000000000000000") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .asset2(Issue.builder() + .currency("524C555344000000000000000000000000000000") + .issuer(Address.of("rPZtDb6ZHtfYzMmzyUGvehoi2vYhrNAPhy")) + .build() + ) + .tradingFee(TradingFee.ofPercent(BigDecimal.ONE)) + .build(); + + String expectedBinary = "1200261503E8240000000168400000000000000A732102B4A8F64B97151FA303F86417751B7" + + "EA5AF1D0014FCC110C234D04AF15F3F654A81141285FD19EDD3FB7146B87251AFB0E17742B37271031847555344000000" + + "00000000000000000000000000F78AD4FDA66653106AE45EE5FE683C457E6BA9C50418524C55534400000000000000000" + + "0000000000000F78AD4FDA66653106AE45EE5FE683C457E6BA9C5"; + + assertSerializesAndDeserializes(ammVote, expectedBinary); + } + private void assertSerializesAndDeserializes( T transaction, String expectedBinary 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 8f89e64ff..d394a00e0 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 @@ -21,7 +21,7 @@ void constructLpTokenWithdrawAndTestJson() throws JSONException, JsonProcessingE String json = "{\n" + " \"Account\" : \"rJVUeRqDFNs2xqA7ncVE6ZoAhPUoaJJSQm\",\n" + - " \"LPTokensIn\" : " + objectMapper.writeValueAsString(withdraw.lpTokensIn()) + "," + + " \"LPTokenIn\" : " + objectMapper.writeValueAsString(withdraw.lpTokensIn()) + "," + " \"Asset\" : " + objectMapper.writeValueAsString(withdraw.asset()) + "," + " \"Asset2\" : " + objectMapper.writeValueAsString(withdraw.asset2()) + "," + " \"Fee\" : \"10\",\n" + @@ -43,7 +43,7 @@ void constructLpTokenWithdrawWithXrpCurrencyAmountAndTestJson() throws JSONExcep String json = "{\n" + " \"Account\" : \"rJVUeRqDFNs2xqA7ncVE6ZoAhPUoaJJSQm\",\n" + - " \"LPTokensIn\" : \"10\"," + + " \"LPTokenIn\" : \"10\"," + " \"Asset\" : " + objectMapper.writeValueAsString(withdraw.asset()) + "," + " \"Asset2\" : " + objectMapper.writeValueAsString(withdraw.asset2()) + "," + " \"Fee\" : \"10\",\n" + @@ -171,7 +171,7 @@ void constructOneAssetLpTokenAndTestJson() throws JSONException, JsonProcessingE " \"issuer\" : \"rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd\",\n" + " \"value\" : \"5\"\n" + " },\n" + - " \"LPTokensIn\" : " + objectMapper.writeValueAsString(withdraw.lpTokensIn()) + "," + + " \"LPTokenIn\" : " + objectMapper.writeValueAsString(withdraw.lpTokensIn()) + "," + " \"Asset\" : " + objectMapper.writeValueAsString(withdraw.asset()) + "," + " \"Asset2\" : " + objectMapper.writeValueAsString(withdraw.asset2()) + "," + " \"Fee\" : \"10\",\n" +