From e625024306a495a0f0a3fab62c0b1b8b3d30c2c5 Mon Sep 17 00:00:00 2001 From: Mukul Jangid <49061120+mukulljangid@users.noreply.github.com> Date: Thu, 2 Jun 2022 09:46:09 -0400 Subject: [PATCH 01/12] use bom to import feign modules (#268) --- pom.xml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 8403481e5..df94fcc7c 100644 --- a/pom.xml +++ b/pom.xml @@ -185,13 +185,10 @@ io.github.openfeign - feign-core - ${feign.version} - - - io.github.openfeign - feign-jackson + feign-bom ${feign.version} + pom + import org.bouncycastle From 632fb0f8a2610468c06f04f1071e41ddf8ecfaaa Mon Sep 17 00:00:00 2001 From: Mukul Jangid <49061120+mukulljangid@users.noreply.github.com> Date: Tue, 7 Jun 2022 12:58:50 -0400 Subject: [PATCH 02/12] remove ledger value checks. (#265) --- .../test/java/org/xrpl/xrpl4j/tests/AccountTransactionsIT.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountTransactionsIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountTransactionsIT.java index c5c8895ad..0e92bcc39 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountTransactionsIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountTransactionsIT.java @@ -104,8 +104,6 @@ public void listTransactionWithIndexRange() { .build() ); - assertThat(results.ledgerIndexMinimum()).isEqualTo(minLedger); - assertThat(results.ledgerIndexMaximum()).isEqualTo(maxLedger); assertThat(results.transactions()).hasSize(7); // results are returned in descending sorted order by ledger index assertThat(results.transactions().get(0).transaction().ledgerIndex()).isNotEmpty().get() @@ -154,7 +152,6 @@ void listTransactionsWithLedgerSpecifiers() throws JsonRpcClientErrorException { .build() ); - assertThat(resultByShortcut.ledgerIndexMinimum()).isEqualTo(resultByShortcut.ledgerIndexMaximum()); assertThat(resultByShortcut.ledgerIndexMinimum().value()) .isEqualTo(validatedLedgerIndex.unsignedIntegerValue().longValue()); From 5aa942afadc570c6dc52f56d088d01e9543e6f6e Mon Sep 17 00:00:00 2001 From: Mukul Jangid <49061120+mukulljangid@users.noreply.github.com> Date: Wed, 8 Jun 2022 17:08:56 -0400 Subject: [PATCH 03/12] remove ledger index asserts from v3 ITs (#274) remove asserts similar to https://github.com/XRPLF/xrpl4j/pull/265/ --- .../java/org/xrpl/xrpl4j/tests/v3/AccountTransactionsIT.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/AccountTransactionsIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/AccountTransactionsIT.java index e21333d43..a93eab75e 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/AccountTransactionsIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/AccountTransactionsIT.java @@ -89,8 +89,6 @@ public void listTransactionWithIndexRange() { .build() ); - assertThat(results.ledgerIndexMinimum()).isEqualTo(minLedger); - assertThat(results.ledgerIndexMaximum()).isEqualTo(maxLedger); assertThat(results.transactions()).hasSize(16); // results are returned in descending sorted order by ledger index assertThat(results.transactions().get(0).resultTransaction().ledgerIndex()) @@ -140,7 +138,6 @@ void listTransactionsWithLedgerSpecifiers() throws JsonRpcClientErrorException { .build() ); - assertThat(resultByShortcut.ledgerIndexMinimum()).isEqualTo(resultByShortcut.ledgerIndexMaximum()); assertThat(resultByShortcut.ledgerIndexMinimum().value()) .isEqualTo(validatedLedgerIndex.unsignedIntegerValue().longValue()); From 2aa203a44738f417ae0495668394202e2c7253bd Mon Sep 17 00:00:00 2001 From: Mukul Jangid <49061120+mukulljangid@users.noreply.github.com> Date: Wed, 8 Jun 2022 17:10:43 -0400 Subject: [PATCH 04/12] add Base58EncodedSecret Model (#272) * add Base58EncodedSecret Model * can be accessed directly using value() hence deleting toString() * create an interface to hold the encoded and decoded value for Base58 Encoded Secret * fix checkstyle * Cleanup unused imports Co-authored-by: David Fuelling --- .../bc/keys/Ed25519KeyPairServiceTest.java | 3 +- .../bc/keys/Secp256k1KeyPairServiceTest.java | 3 +- .../crypto/core/keys/Base58EncodedSecret.java | 55 +++++++++++++++++++ .../xrpl/xrpl4j/crypto/core/keys/Seed.java | 5 +- .../xrpl4j/crypto/core/keys/SeedTest.java | 8 +-- .../xrpl/xrpl4j/tests/v3/SubmitPaymentIT.java | 5 +- 6 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/Base58EncodedSecret.java diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/test/java/org/xrpl/xrpl4j/crypto/bc/keys/Ed25519KeyPairServiceTest.java b/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/test/java/org/xrpl/xrpl4j/crypto/bc/keys/Ed25519KeyPairServiceTest.java index ab6d69687..7e3b35265 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/test/java/org/xrpl/xrpl4j/crypto/bc/keys/Ed25519KeyPairServiceTest.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/test/java/org/xrpl/xrpl4j/crypto/bc/keys/Ed25519KeyPairServiceTest.java @@ -8,6 +8,7 @@ import org.junit.jupiter.api.Test; import org.xrpl.xrpl4j.codec.addresses.UnsignedByteArray; import org.xrpl.xrpl4j.crypto.bc.BcAddressUtils; +import org.xrpl.xrpl4j.crypto.core.keys.Base58EncodedSecret; import org.xrpl.xrpl4j.crypto.core.keys.Entropy; import org.xrpl.xrpl4j.crypto.core.keys.KeyPair; import org.xrpl.xrpl4j.crypto.core.keys.PrivateKey; @@ -50,7 +51,7 @@ void deriveKeyPairFromNull() { @Test public void deriveKeyPair() { - Seed seed = Seed.fromBase58EncodedSecret("sEdSvUyszZFDFkkxQLm18ry3yeZ2FDM"); + Seed seed = Seed.fromBase58EncodedSecret(Base58EncodedSecret.of("sEdSvUyszZFDFkkxQLm18ry3yeZ2FDM")); KeyPair keyPair = keyPairService.deriveKeyPair(seed); KeyPair expectedKeyPair = KeyPair.builder() .privateKey(PrivateKey.of(UnsignedByteArray.of( diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/test/java/org/xrpl/xrpl4j/crypto/bc/keys/Secp256k1KeyPairServiceTest.java b/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/test/java/org/xrpl/xrpl4j/crypto/bc/keys/Secp256k1KeyPairServiceTest.java index 26e4f42d5..b37efea19 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/test/java/org/xrpl/xrpl4j/crypto/bc/keys/Secp256k1KeyPairServiceTest.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/test/java/org/xrpl/xrpl4j/crypto/bc/keys/Secp256k1KeyPairServiceTest.java @@ -8,6 +8,7 @@ import org.junit.jupiter.api.Test; import org.xrpl.xrpl4j.codec.addresses.UnsignedByteArray; import org.xrpl.xrpl4j.crypto.bc.BcAddressUtils; +import org.xrpl.xrpl4j.crypto.core.keys.Base58EncodedSecret; import org.xrpl.xrpl4j.crypto.core.keys.Entropy; import org.xrpl.xrpl4j.crypto.core.keys.KeyPair; import org.xrpl.xrpl4j.crypto.core.keys.PrivateKey; @@ -50,7 +51,7 @@ void deriveKeyPairFromNull() { @Test public void deriveSecp256k1KeyPair() { - Seed seed = Seed.fromBase58EncodedSecret("sp5fghtJtpUorTwvof1NpDXAzNwf5"); + Seed seed = Seed.fromBase58EncodedSecret(Base58EncodedSecret.of("sp5fghtJtpUorTwvof1NpDXAzNwf5")); KeyPair keyPair = keyPairService.deriveKeyPair(seed); KeyPair expectedKeyPair = KeyPair.builder() .privateKey(PrivateKey.of(UnsignedByteArray.of( diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/Base58EncodedSecret.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/Base58EncodedSecret.java new file mode 100644 index 000000000..b0ace1df5 --- /dev/null +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/Base58EncodedSecret.java @@ -0,0 +1,55 @@ +package org.xrpl.xrpl4j.crypto.core.keys; + +import org.immutables.value.Value; +import org.xrpl.xrpl4j.codec.addresses.Base58; +import org.xrpl.xrpl4j.codec.addresses.UnsignedByteArray; + +import java.util.Objects; + +/** + * A typed instance of an XRPL Base58 Encoded Secret, which can be used to generate {@link Seed}. + */ +@Value.Immutable +public interface Base58EncodedSecret { + + /** + * Instantiates a new builder. + * + * @return A {@link ImmutableBase58EncodedSecret.Builder}. + */ + static ImmutableBase58EncodedSecret.Builder builder() { + return ImmutableBase58EncodedSecret.builder(); + } + + /** + * Construct a {@link Base58EncodedSecret} from a base58-encoded {@link String}. + * + * @param base58EncodedSecret A base58-encoded {@link String}. + * + * @return A {@link Base58EncodedSecret}. + */ + static Base58EncodedSecret of(final String base58EncodedSecret) { + Objects.requireNonNull(base58EncodedSecret); + return Base58EncodedSecret.builder() + .value(base58EncodedSecret) + .build(); + } + + /** + * The value of Base58 Encoded Secret. + * + * @return An instance of {@link String}. + */ + String value(); + + /** + * The decoded value of Base58 Encoded Secret. + * + * @return An instance of {@link UnsignedByteArray}. + */ + @Value.Derived + default UnsignedByteArray decodedValueBytes() { + return UnsignedByteArray.of(Base58.decode(value())); + } + +} diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/Seed.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/Seed.java index 01eb97d43..85a949111 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/Seed.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/Seed.java @@ -114,11 +114,10 @@ public static Seed secp256k1SeedFromEntropy(final Entropy entropy) { * * @see "https://xrpl.org/xrp-testnet-faucet.html" */ - public static Seed fromBase58EncodedSecret(final String base58EncodedSecret) { + public static Seed fromBase58EncodedSecret(final Base58EncodedSecret base58EncodedSecret) { Objects.requireNonNull(base58EncodedSecret); - final byte[] base58EncodedSeed = AddressBase58.decode(base58EncodedSecret); - return new Seed(UnsignedByteArray.of(base58EncodedSeed)); + return new Seed(base58EncodedSecret.decodedValueBytes()); } /** diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/test/java/org/xrpl/xrpl4j/crypto/core/keys/SeedTest.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/test/java/org/xrpl/xrpl4j/crypto/core/keys/SeedTest.java index 5d0f7cf47..f6e86c551 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/test/java/org/xrpl/xrpl4j/crypto/core/keys/SeedTest.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/test/java/org/xrpl/xrpl4j/crypto/core/keys/SeedTest.java @@ -35,14 +35,14 @@ void constructorWithNullUnsignedByteArray() { @Test void constructorWithUnsignedByteArray() { - Seed originalSeed = Seed.fromBase58EncodedSecret("sEdSvUyszZFDFkkxQLm18ry3yeZ2FDM"); + Seed originalSeed = Seed.fromBase58EncodedSecret(Base58EncodedSecret.of("sEdSvUyszZFDFkkxQLm18ry3yeZ2FDM")); byte[] originalSeedBytes = Base58.decode("sEdSvUyszZFDFkkxQLm18ry3yeZ2FDM"); assertThat(new Seed(UnsignedByteArray.of(originalSeedBytes))).isEqualTo(originalSeed); } @Test void constructorWithSeed() { - final Seed originalSeed = Seed.fromBase58EncodedSecret("sEdSvUyszZFDFkkxQLm18ry3yeZ2FDM"); + final Seed originalSeed = Seed.fromBase58EncodedSecret(Base58EncodedSecret.of("sEdSvUyszZFDFkkxQLm18ry3yeZ2FDM")); final Seed copiedSeed = new Seed(originalSeed); assertThat(originalSeed).isEqualTo(copiedSeed); assertThat(copiedSeed).isEqualTo(originalSeed); @@ -106,13 +106,13 @@ void seedFromBase58EncodedSecretWithNull() { @Test void seedFromBase58EncodedSecretEd25519() { - Seed seed = Seed.fromBase58EncodedSecret("sEdSvUyszZFDFkkxQLm18ry3yeZ2FDM"); + Seed seed = Seed.fromBase58EncodedSecret(Base58EncodedSecret.of("sEdSvUyszZFDFkkxQLm18ry3yeZ2FDM")); assertThat(seed.decodedSeed().bytes().hexValue()).isEqualTo("2C74FD17EDAFD80E8447B0D46741EE24"); } @Test void seedFromBase58EncodedSecretSecp256k1() { - Seed seed = Seed.fromBase58EncodedSecret("sp5fghtJtpUorTwvof1NpDXAzNwf5"); + Seed seed = Seed.fromBase58EncodedSecret(Base58EncodedSecret.of("sp5fghtJtpUorTwvof1NpDXAzNwf5")); assertThat(seed.decodedSeed().bytes().hexValue()).isEqualTo("0102030405060708090A0B0C0D0E0F10"); } diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/SubmitPaymentIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/SubmitPaymentIT.java index c169c294e..9c4a0d472 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/SubmitPaymentIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/SubmitPaymentIT.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; +import org.xrpl.xrpl4j.crypto.core.keys.Base58EncodedSecret; import org.xrpl.xrpl4j.crypto.core.keys.Seed; import org.xrpl.xrpl4j.crypto.core.signing.SingleSingedTransaction; import org.xrpl.xrpl4j.crypto.core.wallet.Wallet; @@ -63,7 +64,9 @@ public void sendPayment() throws JsonRpcClientErrorException, JsonProcessingExce @Test public void sendPaymentFromSecp256k1Wallet() throws JsonRpcClientErrorException, JsonProcessingException { - Wallet senderWallet = walletFactory.fromSeed(Seed.fromBase58EncodedSecret("sp5fghtJtpUorTwvof1NpDXAzNwf5")); + Wallet senderWallet = walletFactory.fromSeed(Seed.fromBase58EncodedSecret( + Base58EncodedSecret.of("sp5fghtJtpUorTwvof1NpDXAzNwf5")) + ); logger.info("Generated source testnet wallet with address " + senderWallet.address()); fundAccount(senderWallet); From 8b0fd1740b584ccf22063835b47f6a3746b18bc0 Mon Sep 17 00:00:00 2001 From: Mukul Jangid <49061120+mukulljangid@users.noreply.github.com> Date: Wed, 8 Jun 2022 17:11:39 -0400 Subject: [PATCH 05/12] Normalize hex Strings to upper case when using Guava BaseEncoding.base16() and update guava (#271) * Normalize hex Strings to upper case when using Guava * update guava version to 31.1 --- pom.xml | 2 +- .../xrpl4j/crypto/signing/SingleKeySignatureService.java | 2 +- .../src/main/java/org/xrpl/xrpl4j/crypto/PrivateKey.java | 2 +- .../src/main/java/org/xrpl/xrpl4j/crypto/PublicKey.java | 2 +- .../java/org/xrpl/xrpl4j/crypto/core/keys/PublicKey.java | 2 +- .../org/xrpl/xrpl4j/crypto/core/signing/SignatureUtils.java | 2 +- .../java/org/xrpl/xrpl4j/crypto/signing/SignatureUtils.java | 2 +- .../org/xrpl/xrpl4j/crypto/core/keys/PublicKeyTest.java | 4 ++++ .../org/xrpl/xrpl4j/keypairs/AbstractKeyPairService.java | 2 +- .../org/xrpl/xrpl4j/keypairs/Ed25519KeyPairService.java | 6 +++--- .../org/xrpl/xrpl4j/keypairs/Secp256k1KeyPairService.java | 6 ++++-- 11 files changed, 19 insertions(+), 13 deletions(-) diff --git a/pom.xml b/pom.xml index 7017ee40f..f04c3d9a9 100644 --- a/pom.xml +++ b/pom.xml @@ -326,7 +326,7 @@ 11.6 1.7.30 5.7.1 - 29.0-jre + 31.1-jre diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/signing/SingleKeySignatureService.java b/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/signing/SingleKeySignatureService.java index fc3d85262..b06f5accc 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/signing/SingleKeySignatureService.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/signing/SingleKeySignatureService.java @@ -118,7 +118,7 @@ protected synchronized Signature edDsaSign( Objects.requireNonNull(signableTransactionBytes); Ed25519PrivateKeyParameters privateKeyParameters = new Ed25519PrivateKeyParameters( - BaseEncoding.base16().decode(privateKey.base16Encoded().substring(2)), // Remove ED prefix byte + BaseEncoding.base16().decode(privateKey.base16Encoded().substring(2).toUpperCase()), // Remove ED prefix byte 0 ); diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/PrivateKey.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/PrivateKey.java index 9027bbc62..b947c20e2 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/PrivateKey.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/PrivateKey.java @@ -77,7 +77,7 @@ static PrivateKey fromBase16EncodedPrivateKey(final String base16EncodedPrivateK Objects.requireNonNull(base16EncodedPrivateKey); return PrivateKey.builder() - .value(UnsignedByteArray.of(BaseEncoding.base16().decode(base16EncodedPrivateKey))) + .value(UnsignedByteArray.of(BaseEncoding.base16().decode(base16EncodedPrivateKey.toUpperCase()))) .build(); } diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/PublicKey.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/PublicKey.java index 76828c820..be9c92d37 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/PublicKey.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/PublicKey.java @@ -75,7 +75,7 @@ static PublicKey fromBase58EncodedPublicKey(final String base58EncodedPublicKey) static PublicKey fromBase16EncodedPublicKey(final String base16EncodedPublicKey) { Objects.requireNonNull(base16EncodedPublicKey); return PublicKey.builder() - .value(UnsignedByteArray.of(BaseEncoding.base16().decode(base16EncodedPublicKey))) + .value(UnsignedByteArray.of(BaseEncoding.base16().decode(base16EncodedPublicKey.toUpperCase()))) .build(); } diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/PublicKey.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/PublicKey.java index 35fac98f8..4d5247a8c 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/PublicKey.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/PublicKey.java @@ -67,7 +67,7 @@ static PublicKey fromBase58EncodedPublicKey(final String base58EncodedPublicKey) static PublicKey fromBase16EncodedPublicKey(final String base16EncodedPublicKey) { Objects.requireNonNull(base16EncodedPublicKey); return PublicKey.builder() - .value(UnsignedByteArray.of(BaseEncoding.base16().decode(base16EncodedPublicKey))) + .value(UnsignedByteArray.of(BaseEncoding.base16().decode(base16EncodedPublicKey.toUpperCase()))) .build(); } diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/SignatureUtils.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/SignatureUtils.java index da40f31c2..d3f26ec35 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/SignatureUtils.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/SignatureUtils.java @@ -234,7 +234,7 @@ public SingleSingedTransaction addSignatureToTransact .unsignedTransaction(transaction) .signature(signature) .signedTransaction((T) transactionWithSignature) - .signedTransactionBytes(UnsignedByteArray.of(BaseEncoding.base16().decode(signedBlob))) + .signedTransactionBytes(UnsignedByteArray.of(BaseEncoding.base16().decode(signedBlob.toUpperCase()))) .build(); } catch (JsonProcessingException e) { throw new RuntimeException(e.getMessage(), e); diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/signing/SignatureUtils.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/signing/SignatureUtils.java index afa61956a..e592c0262 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/signing/SignatureUtils.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/signing/SignatureUtils.java @@ -227,7 +227,7 @@ public SignedTransaction addSignatureToTransaction( .unsignedTransaction(unsignedTransaction) .signature(signature) .signedTransaction(signedTransaction) - .signedTransactionBytes(UnsignedByteArray.of(BaseEncoding.base16().decode(signedBlob))) + .signedTransactionBytes(UnsignedByteArray.of(BaseEncoding.base16().decode(signedBlob.toUpperCase()))) .build(); } catch (JsonProcessingException e) { throw new RuntimeException(e.getMessage(), e); diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/test/java/org/xrpl/xrpl4j/crypto/core/keys/PublicKeyTest.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/test/java/org/xrpl/xrpl4j/crypto/core/keys/PublicKeyTest.java index 08196ae9c..742511e29 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/test/java/org/xrpl/xrpl4j/crypto/core/keys/PublicKeyTest.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/test/java/org/xrpl/xrpl4j/crypto/core/keys/PublicKeyTest.java @@ -32,11 +32,15 @@ public void fromBase58EncodedStringSecp256k1() { @Test public void fromBase16EncodedStringEd25519() { assertThat(PublicKey.fromBase16EncodedPublicKey(ED_PUBLIC_KEY_HEX).base58Value()).isEqualTo(ED_PUBLIC_KEY_B58); + assertThat(PublicKey.fromBase16EncodedPublicKey(ED_PUBLIC_KEY_HEX.toLowerCase()).base58Value()) + .isEqualTo(ED_PUBLIC_KEY_B58); } @Test public void fromBase16EncodedStringSecp256k1() { assertThat(PublicKey.fromBase16EncodedPublicKey(EC_PUBLIC_KEY_HEX).base58Value()).isEqualTo(EC_PUBLIC_KEY_B58); + assertThat(PublicKey.fromBase16EncodedPublicKey(EC_PUBLIC_KEY_HEX.toLowerCase()).base58Value()) + .isEqualTo(EC_PUBLIC_KEY_B58); } @Test diff --git a/xrpl4j-keypairs/src/main/java/org/xrpl/xrpl4j/keypairs/AbstractKeyPairService.java b/xrpl4j-keypairs/src/main/java/org/xrpl/xrpl4j/keypairs/AbstractKeyPairService.java index cb3a49cbd..d99ed6f88 100644 --- a/xrpl4j-keypairs/src/main/java/org/xrpl/xrpl4j/keypairs/AbstractKeyPairService.java +++ b/xrpl4j-keypairs/src/main/java/org/xrpl/xrpl4j/keypairs/AbstractKeyPairService.java @@ -58,7 +58,7 @@ public boolean verify(String message, String signature, String publicKey) { @Override public Address deriveAddress(String publicKey) { - UnsignedByteArray publicKeyBytes = UnsignedByteArray.of(BaseEncoding.base16().decode(publicKey)); + UnsignedByteArray publicKeyBytes = UnsignedByteArray.of(BaseEncoding.base16().decode(publicKey.toUpperCase())); return this.deriveAddress(publicKeyBytes); } diff --git a/xrpl4j-keypairs/src/main/java/org/xrpl/xrpl4j/keypairs/Ed25519KeyPairService.java b/xrpl4j-keypairs/src/main/java/org/xrpl/xrpl4j/keypairs/Ed25519KeyPairService.java index be4c7da1d..344632bfc 100644 --- a/xrpl4j-keypairs/src/main/java/org/xrpl/xrpl4j/keypairs/Ed25519KeyPairService.java +++ b/xrpl4j-keypairs/src/main/java/org/xrpl/xrpl4j/keypairs/Ed25519KeyPairService.java @@ -98,7 +98,7 @@ private KeyPair deriveKeyPair(UnsignedByteArray seed) { @Override public String sign(UnsignedByteArray message, String privateKey) { Ed25519PrivateKeyParameters privateKeyParameters = new Ed25519PrivateKeyParameters( - BaseEncoding.base16().decode(privateKey.substring(2)), // Remove ED prefix byte + BaseEncoding.base16().decode(privateKey.substring(2).toUpperCase()), // Remove ED prefix byte 0 ); @@ -117,13 +117,13 @@ public String sign(UnsignedByteArray message, String privateKey) { @Override public boolean verify(UnsignedByteArray message, String signature, String publicKey) { Ed25519PublicKeyParameters publicKeyParameters = new Ed25519PublicKeyParameters( - BaseEncoding.base16().decode(publicKey.substring(2)), // Remove ED prefix byte + BaseEncoding.base16().decode(publicKey.substring(2).toUpperCase()), // Remove ED prefix byte 0 ); signer.reset(); signer.init(false, publicKeyParameters); signer.update(message.toByteArray(), 0, message.getUnsignedBytes().size()); - return signer.verifySignature(BaseEncoding.base16().decode(signature)); + return signer.verifySignature(BaseEncoding.base16().decode(signature.toUpperCase())); } } diff --git a/xrpl4j-keypairs/src/main/java/org/xrpl/xrpl4j/keypairs/Secp256k1KeyPairService.java b/xrpl4j-keypairs/src/main/java/org/xrpl/xrpl4j/keypairs/Secp256k1KeyPairService.java index e27b617ee..dd48552a8 100644 --- a/xrpl4j-keypairs/src/main/java/org/xrpl/xrpl4j/keypairs/Secp256k1KeyPairService.java +++ b/xrpl4j-keypairs/src/main/java/org/xrpl/xrpl4j/keypairs/Secp256k1KeyPairService.java @@ -157,13 +157,15 @@ private EcDsaSignature createEcdsaSignature(UnsignedByteArray messageHash, BigIn @Override public boolean verify(UnsignedByteArray message, String signature, String publicKey) { UnsignedByteArray messageHash = HashUtils.sha512Half(message); - EcDsaSignature sig = EcDsaSignature.fromDer(BaseEncoding.base16().decode(signature)); + EcDsaSignature sig = EcDsaSignature.fromDer(BaseEncoding.base16().decode(signature.toUpperCase())); if (sig == null) { return false; } ECDSASigner signer = new ECDSASigner(); - ECPoint publicKeyPoint = ecDomainParameters.getCurve().decodePoint(BaseEncoding.base16().decode(publicKey)); + ECPoint publicKeyPoint = ecDomainParameters.getCurve().decodePoint( + BaseEncoding.base16().decode(publicKey.toUpperCase()) + ); ECPublicKeyParameters params = new ECPublicKeyParameters(publicKeyPoint, ecDomainParameters); signer.init(false, params); return signer.verifySignature(messageHash.toByteArray(), sig.r(), sig.s()); From 9d28c3c41a127bbd486c3c03b482f13cc7948054 Mon Sep 17 00:00:00 2001 From: Mukul Jangid <49061120+mukulljangid@users.noreply.github.com> Date: Thu, 9 Jun 2022 09:22:30 -0400 Subject: [PATCH 06/12] Remove duplicated signing and verification logic in BcSignatureService and deprecate some methods in WalletFactory (#270) * move EcDsaSignature to BcSignatureService * add partial test coverage to isStrictlyCanonical() * deprecate random wallet generation from WalletFactory. * add random seed generation to `Seed` to be used to generate Wallet * keep only one EcDsaSignature (as it was) * clean up and tests for new methods for random seed generation * add test for bad der throwing case * Rectify javadoc and fixed formatting setting Signed-off-by: David Fuelling Co-authored-by: David Fuelling --- .../DerivedKeyDelegatedSignatureService.java | 101 +++++------------- .../crypto/bc/signing/EcDsaSignature.java | 2 +- .../crypto/bc/wallet/BcWalletFactory.java | 2 + .../crypto/bc/signing/EcDsaSignatureTest.java | 62 ++++++++++- .../crypto/core/keys/KeyPairService.java | 8 ++ .../xrpl/xrpl4j/crypto/core/keys/Seed.java | 26 ++++- .../crypto/core/wallet/WalletFactory.java | 24 +++-- .../xrpl4j/crypto/core/keys/SeedTest.java | 18 +++- 8 files changed, 150 insertions(+), 93 deletions(-) diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/bc/signing/DerivedKeyDelegatedSignatureService.java b/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/bc/signing/DerivedKeyDelegatedSignatureService.java index 2feda300e..fc3876419 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/bc/signing/DerivedKeyDelegatedSignatureService.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/bc/signing/DerivedKeyDelegatedSignatureService.java @@ -8,10 +8,6 @@ import com.google.common.collect.Sets; import com.google.common.hash.Hashing; import org.bouncycastle.crypto.digests.SHA256Digest; -import org.bouncycastle.crypto.params.ECPrivateKeyParameters; -import org.bouncycastle.crypto.params.ECPublicKeyParameters; -import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters; -import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; import org.bouncycastle.crypto.signers.ECDSASigner; import org.bouncycastle.crypto.signers.Ed25519Signer; import org.bouncycastle.crypto.signers.HMacDSAKCalculator; @@ -23,7 +19,6 @@ import org.xrpl.xrpl4j.crypto.bc.keys.Ed25519KeyPairService; import org.xrpl.xrpl4j.crypto.bc.keys.Secp256k1KeyPairService; import org.xrpl.xrpl4j.crypto.core.AddressUtils; -import org.xrpl.xrpl4j.crypto.core.HashingUtils; import org.xrpl.xrpl4j.crypto.core.KeyMetadata; import org.xrpl.xrpl4j.crypto.core.ServerSecret; import org.xrpl.xrpl4j.crypto.core.ServerSecretSupplier; @@ -44,7 +39,6 @@ import org.xrpl.xrpl4j.model.jackson.ObjectMapperFactory; import org.xrpl.xrpl4j.model.transactions.Transaction; -import java.math.BigInteger; import java.security.KeyStore; import java.util.Arrays; import java.util.Objects; @@ -354,6 +348,7 @@ private static class SingleKeyDelegatedTransactionSigner extends AbstractDelegat private final Ed25519Signer ed25519Signer; private final ECDSASigner ecdsaSigner; private final PrivateKey privateKey; + private final BcSignatureService bcSignatureService; private SingleKeyDelegatedTransactionSigner( PrivateKey privateKey, @@ -364,27 +359,19 @@ private SingleKeyDelegatedTransactionSigner( this.ed25519Signer = new Ed25519Signer(); this.ecdsaSigner = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest())); this.privateKey = privateKey; + this.bcSignatureService = new BcSignatureService( + signatureUtils, + addressUtils, + this.ed25519Signer, + this.ecdsaSigner + ); } @Override protected synchronized Signature edDsaSign( final KeyMetadata privateKeyMetadata, final UnsignedByteArray signableTransactionBytes ) { - Objects.requireNonNull(privateKeyMetadata); - Objects.requireNonNull(signableTransactionBytes); - - Ed25519PrivateKeyParameters privateKeyParameters = BcKeyUtils.toEd25519PrivateKeyParams(privateKey); - - ed25519Signer.reset(); - ed25519Signer.init(true, privateKeyParameters); - ed25519Signer.update( - signableTransactionBytes.toByteArray(), 0, signableTransactionBytes.getUnsignedBytes().size() - ); - - final UnsignedByteArray sigBytes = UnsignedByteArray.of(ed25519Signer.generateSignature()); - return Signature.builder() - .value(sigBytes) - .build(); + return bcSignatureService.edDsaSign(privateKey, signableTransactionBytes); } @SuppressWarnings("checkstyle:LocalVariableName") @@ -392,31 +379,7 @@ protected synchronized Signature edDsaSign( protected synchronized Signature ecDsaSign( final KeyMetadata keyMetadata, final UnsignedByteArray signableTransactionBytes ) { - Objects.requireNonNull(keyMetadata); - Objects.requireNonNull(signableTransactionBytes); - - final UnsignedByteArray messageHash = HashingUtils.sha512Half(signableTransactionBytes); - - final ECPrivateKeyParameters parameters = BcKeyUtils.toEcPrivateKeyParams(privateKey); - - ecdsaSigner.init(true, parameters); - final BigInteger[] signatures = ecdsaSigner.generateSignature(messageHash.toByteArray()); - final BigInteger r = signatures[0]; - BigInteger s = signatures[1]; - final BigInteger otherS = Secp256k1.EC_DOMAIN_PARAMETERS.getN().subtract(s); - if (s.compareTo(otherS) > 0) { - s = otherS; - } - - final EcDsaSignature sig = EcDsaSignature.builder() - .r(r) - .s(s) - .build(); - - UnsignedByteArray sigBytes = sig.der(); - return Signature.builder() - .value(sigBytes) - .build(); + return bcSignatureService.ecDsaSign(privateKey, signableTransactionBytes); } @Override @@ -435,6 +398,7 @@ private static class SingleKeyDelegatedTransactionVerifier extends AbstractDeleg private final Ed25519Signer ed25519Signer; private final ECDSASigner ecdsaSigner; private final PrivateKey privateKey; + private final BcSignatureService bcSignatureService; public SingleKeyDelegatedTransactionVerifier( PrivateKey privateKey, @@ -445,6 +409,12 @@ public SingleKeyDelegatedTransactionVerifier( this.ed25519Signer = new Ed25519Signer(); this.ecdsaSigner = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest())); this.privateKey = privateKey; + this.bcSignatureService = new BcSignatureService( + signatureUtils, + addressUtils, + this.ed25519Signer, + this.ecdsaSigner + ); } @Override @@ -453,21 +423,10 @@ protected synchronized boolean edDsaVerify( final UnsignedByteArray signableTransactionBytes, final Signature transactionSignature ) { - Objects.requireNonNull(keyMetadata); - Objects.requireNonNull(signableTransactionBytes); - Objects.requireNonNull(transactionSignature); - - final PublicKey publicKey = this.getPublicKey(keyMetadata); - final Ed25519PublicKeyParameters bcPublicKey = BcKeyUtils.toEd25519PublicKeyParameters( - publicKey); - - ed25519Signer.reset(); - ed25519Signer.init(false, bcPublicKey); - ed25519Signer.update(signableTransactionBytes.toByteArray(), 0, - signableTransactionBytes.getUnsignedBytes().size()); - - return ed25519Signer.verifySignature( - transactionSignature.value().toByteArray() + return bcSignatureService.edDsaVerify( + this.getPublicKey(keyMetadata), + signableTransactionBytes, + transactionSignature ); } @@ -477,21 +436,11 @@ protected synchronized boolean ecDsaVerify( final UnsignedByteArray signableTransactionBytes, final Signature transactionSignature ) { - Objects.requireNonNull(keyMetadata); - Objects.requireNonNull(signableTransactionBytes); - Objects.requireNonNull(transactionSignature); - - final PublicKey publicKey = this.getPublicKey(keyMetadata); - final ECPublicKeyParameters bcPublicKey = BcKeyUtils.toEcPublicKeyParameters(publicKey); - - UnsignedByteArray messageHash = HashingUtils.sha512Half(signableTransactionBytes); - EcDsaSignature sig = EcDsaSignature.fromDer(transactionSignature.value().toByteArray()); - if (sig == null) { - return false; - } - - ecdsaSigner.init(false, bcPublicKey); - return ecdsaSigner.verifySignature(messageHash.toByteArray(), sig.r(), sig.s()); + return bcSignatureService.ecDsaVerify( + this.getPublicKey(keyMetadata), + signableTransactionBytes, + transactionSignature + ); } @Override diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/bc/signing/EcDsaSignature.java b/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/bc/signing/EcDsaSignature.java index 68580b840..f3b098098 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/bc/signing/EcDsaSignature.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/bc/signing/EcDsaSignature.java @@ -150,4 +150,4 @@ default void isStrictlyCanonical() { Preconditions.checkArgument(order.subtract(s).compareTo(s) > -1); } -} +} \ No newline at end of file diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/bc/wallet/BcWalletFactory.java b/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/bc/wallet/BcWalletFactory.java index 9b302e975..63e67b93d 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/bc/wallet/BcWalletFactory.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/bc/wallet/BcWalletFactory.java @@ -49,6 +49,7 @@ public static WalletFactory getInstance() { return INSTANCE; } + @Deprecated @Override public SeedWalletGenerationResult randomWalletEd25519() { Seed seed = ed25519KeyPairService.generateSeed(); @@ -57,6 +58,7 @@ public SeedWalletGenerationResult randomWalletEd25519() { return SeedWalletGenerationResult.builder().seed(seed).wallet(wallet).build(); } + @Deprecated @Override public SeedWalletGenerationResult randomWalletSecp256k1() { Seed seed = secp256k1KeyPairService.generateSeed(); diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/test/java/org/xrpl/xrpl4j/crypto/bc/signing/EcDsaSignatureTest.java b/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/test/java/org/xrpl/xrpl4j/crypto/bc/signing/EcDsaSignatureTest.java index 2806efe7f..da0b378e2 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/test/java/org/xrpl/xrpl4j/crypto/bc/signing/EcDsaSignatureTest.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/test/java/org/xrpl/xrpl4j/crypto/bc/signing/EcDsaSignatureTest.java @@ -1,11 +1,15 @@ package org.xrpl.xrpl4j.crypto.bc.signing; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.google.common.io.BaseEncoding; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.xrpl.xrpl4j.codec.addresses.UnsignedByte; +import org.xrpl.xrpl4j.codec.addresses.UnsignedByteArray; import java.math.BigInteger; @@ -39,8 +43,58 @@ void der() { } @Test - @Disabled - void isStrictlyCanonical() { - // TODO: Add coverage. + void badDerThrowsWhenFormingSignature() { + assertThrows( + RuntimeException.class, + () -> EcDsaSignature.fromDer(new byte[] {1, 2, 3, 4}) + ); + } + + @Test + void ecDsaSignatureWithR0() { + IllegalArgumentException illegalArgumentException = assertThrows( + IllegalArgumentException.class, + () -> EcDsaSignature.builder() + .r(BigInteger.ZERO) + .s(BigInteger.ONE) + .build() + ); + assertThat(illegalArgumentException.getMessage()).isEqualTo("r cannot be 0."); + } + + @Test + void ecDsaSignatureWithS0() { + IllegalArgumentException illegalArgumentException = assertThrows( + IllegalArgumentException.class, + () -> EcDsaSignature.builder() + .r(BigInteger.ONE) + .s(BigInteger.ZERO) + .build() + ); + assertThat(illegalArgumentException.getMessage()).isEqualTo("s cannot be 0."); + } + + @Test + void ecDsaSignatureWithRNegativeThrows() { + IllegalArgumentException illegalArgumentException = assertThrows( + IllegalArgumentException.class, + () -> EcDsaSignature.builder() + .r(BigInteger.ONE.negate()) + .s(BigInteger.valueOf(2)) + .build() + ); + assertThat(illegalArgumentException.getMessage()).isEqualTo("r cannot be negative."); + } + + @Test + void ecDsaSignatureWithSNegativeThrows() { + IllegalArgumentException illegalArgumentException = assertThrows( + IllegalArgumentException.class, + () -> EcDsaSignature.builder() + .r(BigInteger.valueOf(2)) + .s(BigInteger.ONE.negate()) + .build() + ); + assertThat(illegalArgumentException.getMessage()).isEqualTo("s cannot be negative."); } } \ No newline at end of file diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/KeyPairService.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/KeyPairService.java index cd524a6c2..de642f60b 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/KeyPairService.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/KeyPairService.java @@ -11,7 +11,11 @@ public interface KeyPairService { * Generate a random 16-byte seed to be used to derive a private key. * * @return A {@link String} containing a randomly generated Base58Check encoded seed value. + * + * @deprecated This method will be removed in a future release. Prefer {@link Seed#ed25519Seed()} or + * {@link Seed#secp256k1Seed()}. */ + @Deprecated default Seed generateSeed() { return generateSeed(Entropy.newInstance()); } @@ -22,7 +26,11 @@ default Seed generateSeed() { * @param entropy An {@link UnsignedByteArray} containing the bytes of entropy to encode into a seed. * * @return A {@link String} containing the Base58Check encoded seed value. + * + * @deprecated This method will be removed in a future release. Prefer {@link Seed#ed25519SeedFromEntropy(Entropy)} or + * {@link Seed#secp256k1SeedFromEntropy(Entropy)}. */ + @Deprecated Seed generateSeed(Entropy entropy); /** diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/Seed.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/Seed.java index 85a949111..fe82771e7 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/Seed.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/keys/Seed.java @@ -25,7 +25,7 @@ public class Seed implements javax.security.auth.Destroyable { private boolean destroyed; /** - * Construct an Ed25519-compatible {@link Seed} from the supplied {@code passphrase}. + * Construct an Ed25519-compatible {@link Seed} from the supplied {@link Passphrase}. * * @param passphrase A {@link Passphrase} to generate a seed from. * @@ -65,7 +65,17 @@ public static Seed secp256k1SeedFromPassphrase(final Passphrase passphrase) { } /** - * Construct an Ed25519-compatible {@link Seed} from the supplied {@code passphrase}. + * Construct an Ed25519-compatible {@link Seed} using a random {@link Entropy} instance. This random {@link Entropy} + * is created using {@link Entropy#newInstance()}. + * + * @return A {@link Seed}. + */ + public static Seed ed25519Seed() { + return ed25519SeedFromEntropy(Entropy.newInstance()); + } + + /** + * Construct an Ed25519-compatible {@link Seed} from the supplied {@link Entropy}. * * @param entropy A {@link Entropy} to generate a {@link Seed} from. * @@ -84,7 +94,17 @@ public static Seed ed25519SeedFromEntropy(final Entropy entropy) { } /** - * Construct an SECP256K1-compatible {@link Seed} from the supplied {@link Passphrase}. + * Construct an SECP256K1-compatible {@link Seed} using a random {@link Entropy} instance. This random {@link Entropy} + * is created using {@link Entropy#newInstance()}. + * + * @return A {@link Seed}. + */ + public static Seed secp256k1Seed() { + return secp256k1SeedFromEntropy(Entropy.newInstance()); + } + + /** + * Construct an SECP256K1-compatible {@link Seed} from the supplied {@link Entropy}. * * @param entropy A {@link Entropy} to generate a {@link Seed} from. * diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/wallet/WalletFactory.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/wallet/WalletFactory.java index 0027acdc9..7449e81c3 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/wallet/WalletFactory.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/wallet/WalletFactory.java @@ -12,11 +12,11 @@ public interface WalletFactory { * Generate a {@link Wallet} by generating a random seed and deriving the public/private keys and XRPL address from * it. * - * @return A {@link SeedWalletGenerationResult}, which contains the seed that was generated, as well as the {@link - * Wallet}. + * @return A {@link SeedWalletGenerationResult}, which contains the seed that was generated, as well as the + * {@link Wallet}. * - * @deprecated This method will be removed in a future release. Prefer {@link #randomWalletEd25519()} or {@link - * #randomWalletSecp256k1()} instead. + * @deprecated This method will be removed in a future release. Prefer {@link #randomWalletEd25519()} or + * {@link #randomWalletSecp256k1()} instead. */ @Deprecated default SeedWalletGenerationResult randomWallet() { @@ -27,18 +27,26 @@ default SeedWalletGenerationResult randomWallet() { * Generate a {@link Wallet} by generating a random seed and deriving an Ed25519 public/private key pair, XRPL * address, and {@link Wallet}. * - * @return A {@link SeedWalletGenerationResult}, which contains the seed that was generated, as well as the {@link - * Wallet}. + * @return A {@link SeedWalletGenerationResult}, which contains the seed that was generated, as well as the + * {@link Wallet}. + * + * @deprecated This method will be removed in a future version. Use {@link Seed#ed25519Seed()} with + * {@link #fromSeed(Seed)}. */ + @Deprecated SeedWalletGenerationResult randomWalletEd25519(); /** * Generate a {@link Wallet} by generating a random seed and deriving a Secp256k1 public/private key pair, XRPL * address, and {@link Wallet}. * - * @return A {@link SeedWalletGenerationResult}, which contains the seed that was generated, as well as the {@link - * Wallet}. + * @return A {@link SeedWalletGenerationResult}, which contains the seed that was generated, as well as the + * {@link Wallet}. + * + * @deprecated This method will be removed in a future version. Use {@link Seed#secp256k1Seed()} with + * {@link #fromSeed(Seed)}. */ + @Deprecated SeedWalletGenerationResult randomWalletSecp256k1(); /** diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/test/java/org/xrpl/xrpl4j/crypto/core/keys/SeedTest.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/test/java/org/xrpl/xrpl4j/crypto/core/keys/SeedTest.java index f6e86c551..848a7ffc5 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/test/java/org/xrpl/xrpl4j/crypto/core/keys/SeedTest.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/test/java/org/xrpl/xrpl4j/crypto/core/keys/SeedTest.java @@ -10,7 +10,7 @@ import org.xrpl.xrpl4j.codec.addresses.VersionType; /** - * Unit tests for {@link org.xrpl.xrpl4j.crypto.Seed}. + * Unit tests for {@link Seed}. */ public class SeedTest { @@ -49,6 +49,22 @@ void constructorWithSeed() { assertThat(originalSeed.decodedSeed().bytes().hexValue()).isEqualTo(copiedSeed.decodedSeed().bytes().hexValue()); } + @Test + void testRandomEd25519SeedGeneration() { + Seed originalSeed = Seed.ed25519Seed(); + Seed copiedSeed = new Seed(originalSeed); + assertThat(originalSeed.equals(copiedSeed)).isTrue(); + assertThat(originalSeed.decodedSeed().bytes().hexValue()).isEqualTo(copiedSeed.decodedSeed().bytes().hexValue()); + } + + @Test + void testRandomSecp256k1SeedGeneration() { + Seed originalSeed = Seed.secp256k1Seed(); + Seed copiedSeed = new Seed(originalSeed); + assertThat(originalSeed.equals(copiedSeed)).isTrue(); + assertThat(originalSeed.decodedSeed().bytes().hexValue()).isEqualTo(copiedSeed.decodedSeed().bytes().hexValue()); + } + @Test void testSecp256k1SeedFromNullEntropy() { Assertions.assertThrows(NullPointerException.class, () -> { From f103b0f432f74bbc147492fc9c19d2222cd7bbe9 Mon Sep 17 00:00:00 2001 From: Mukul Jangid <49061120+mukulljangid@users.noreply.github.com> Date: Tue, 21 Jun 2022 18:08:41 -0400 Subject: [PATCH 07/12] Introduce class `FeeUtils` with dynamic fee calculation function (#260) * Introduce class `FeeUtils` with dynamic fee calculation function --- .../org/xrpl/xrpl4j/tests/AccountSetIT.java | 29 +-- .../java/org/xrpl/xrpl4j/tests/CheckIT.java | 13 +- .../xrpl/xrpl4j/tests/DepositPreAuthIT.java | 7 +- .../java/org/xrpl/xrpl4j/tests/IsFinalIT.java | 3 +- .../xrpl/xrpl4j/tests/IssuedCurrencyIT.java | 7 +- .../java/org/xrpl/xrpl4j/tests/OfferIT.java | 11 +- .../xrpl/xrpl4j/tests/PaymentChannelIT.java | 15 +- .../xrpl/xrpl4j/tests/SetRegularKeyIT.java | 11 +- .../xrpl/xrpl4j/tests/SignerListSetIT.java | 14 +- .../xrpl4j/tests/SubmitMultisignedIT.java | 7 +- .../xrpl/xrpl4j/tests/SubmitPaymentIT.java | 5 +- .../java/org/xrpl/xrpl4j/tests/TicketIT.java | 5 +- .../xrpl4j/tests/TransactionWithMemoIT.java | 5 +- .../xrpl4j/model/client/fees/FeeUtils.java | 94 ++++++++ .../model/transactions/Transaction.java | 7 +- .../model/client/fees/FeeUtilsTest.java | 224 ++++++++++++++++++ 16 files changed, 395 insertions(+), 62 deletions(-) create mode 100644 xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/fees/FeeUtils.java create mode 100644 xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/client/fees/FeeUtilsTest.java diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountSetIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountSetIT.java index 5a23a775d..cc061d57f 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountSetIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountSetIT.java @@ -9,9 +9,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,6 +27,7 @@ import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.flags.Flags.AccountRootFlags; import org.xrpl.xrpl4j.model.transactions.AccountSet; @@ -60,19 +61,19 @@ public void enableAllAndDisableOne() throws JsonRpcClientErrorException { // Set asfAccountTxnID (no corresponding ledger flag) FeeResult feeResult = xrplClient.fee(); AccountSet accountSet = AccountSet.builder() - .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) - .sequence(accountInfo.accountData().sequence()) - .setFlag(AccountSetFlag.ACCOUNT_TXN_ID) - .signingPublicKey(wallet.publicKey()) - .build(); + .account(wallet.classicAddress()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) + .sequence(accountInfo.accountData().sequence()) + .setFlag(AccountSetFlag.ACCOUNT_TXN_ID) + .signingPublicKey(wallet.publicKey()) + .build(); SubmitResult response = xrplClient.submit(wallet, accountSet); assertThat(response.result()).isEqualTo(TransactionResultCodes.TES_SUCCESS); assertThat(response.transactionResult().transaction().hash()).isNotEmpty().get() - .isEqualTo(response.transactionResult().hash()); + .isEqualTo(response.transactionResult().hash()); logger.info( - "AccountSet transaction successful: https://testnet.xrpl.org/transactions/" + response.transactionResult().hash() + "AccountSet transaction successful: https://testnet.xrpl.org/transactions/" + response.transactionResult().hash() ); /////////////////////// @@ -102,7 +103,7 @@ public void enableAllAndDisableOne() throws JsonRpcClientErrorException { ).accountData().flags(); assertThat(flags1.getValue() - flags2.getValue()) - .isEqualTo(AccountRootFlags.GLOBAL_FREEZE.getValue()); + .isEqualTo(AccountRootFlags.GLOBAL_FREEZE.getValue()); } @@ -123,7 +124,7 @@ public void disableAndEnableAllFlags() throws JsonRpcClientErrorException { FeeResult feeResult = xrplClient.fee(); AccountSet accountSet = AccountSet.builder() .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(accountInfo.accountData().sequence()) .setFlag(AccountSetFlag.ACCOUNT_TXN_ID) .signingPublicKey(wallet.publicKey()) @@ -182,7 +183,7 @@ private void assertSetFlag( FeeResult feeResult = xrplClient.fee(); AccountSet accountSet = AccountSet.builder() .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(sequence) .setFlag(accountSetFlag) .signingPublicKey(wallet.publicKey()) @@ -219,7 +220,7 @@ private void assertClearFlag( FeeResult feeResult = xrplClient.fee(); AccountSet accountSet = AccountSet.builder() .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(sequence) .clearFlag(accountSetFlag) .signingPublicKey(wallet.publicKey()) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/CheckIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/CheckIT.java index 56f73f385..72eb31a74 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/CheckIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/CheckIT.java @@ -28,6 +28,7 @@ import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.ledger.CheckObject; import org.xrpl.xrpl4j.model.ledger.LedgerObject; @@ -61,7 +62,7 @@ public void createXrpCheckAndCash() throws JsonRpcClientErrorException { Hash256 invoiceId = Hash256.of(Hashing.sha256().hashBytes("Check this out.".getBytes()).toString()); CheckCreate checkCreate = CheckCreate.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(accountInfoResult.accountData().sequence()) .destination(destinationWallet.classicAddress()) .sendMax(XrpCurrencyAmount.ofDrops(12345)) @@ -99,7 +100,7 @@ public void createXrpCheckAndCash() throws JsonRpcClientErrorException { .account(destinationWallet.classicAddress()) .amount(checkObject.sendMax()) .sequence(destinationAccountInfo.accountData().sequence()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .checkId(checkObject.index()) .signingPublicKey(destinationWallet.publicKey()) .build(); @@ -150,7 +151,7 @@ public void createCheckAndSourceCancels() throws JsonRpcClientErrorException { Hash256 invoiceId = Hash256.of(Hashing.sha256().hashBytes("Check this out.".getBytes()).toString()); CheckCreate checkCreate = CheckCreate.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(accountInfoResult.accountData().sequence()) .destination(destinationWallet.classicAddress()) .sendMax(XrpCurrencyAmount.ofDrops(12345)) @@ -184,7 +185,7 @@ public void createCheckAndSourceCancels() throws JsonRpcClientErrorException { CheckCancel checkCancel = CheckCancel.builder() .account(sourceWallet.classicAddress()) .sequence(accountInfoResult.accountData().sequence().plus(UnsignedInteger.ONE)) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .checkId(checkObject.index()) .signingPublicKey(sourceWallet.publicKey()) .build(); @@ -224,7 +225,7 @@ public void createCheckAndDestinationCancels() throws JsonRpcClientErrorExceptio Hash256 invoiceId = Hash256.of(Hashing.sha256().hashBytes("Check this out.".getBytes()).toString()); CheckCreate checkCreate = CheckCreate.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(accountInfoResult.accountData().sequence()) .destination(destinationWallet.classicAddress()) .sendMax(XrpCurrencyAmount.ofDrops(12345)) @@ -261,7 +262,7 @@ public void createCheckAndDestinationCancels() throws JsonRpcClientErrorExceptio CheckCancel checkCancel = CheckCancel.builder() .account(destinationWallet.classicAddress()) .sequence(destinationAccountInfo.accountData().sequence()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .checkId(checkObject.index()) .signingPublicKey(destinationWallet.publicKey()) .build(); diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/DepositPreAuthIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/DepositPreAuthIT.java index 7497bb05e..3e26af8ff 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/DepositPreAuthIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/DepositPreAuthIT.java @@ -29,6 +29,7 @@ import org.xrpl.xrpl4j.model.client.accounts.AccountObjectsResult; 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.path.DepositAuthorizedRequestParams; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.client.transactions.TransactionResult; @@ -59,7 +60,7 @@ public void preauthorizeAccountAndReceivePayment() throws JsonRpcClientErrorExce // Give Preauthorization for the sender to send a funds to the receiver DepositPreAuth depositPreAuth = DepositPreAuth.builder() .account(receiverWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(receiverAccountInfo.accountData().sequence()) .signingPublicKey(receiverWallet.publicKey()) .authorize(senderWallet.classicAddress()) @@ -100,7 +101,7 @@ public void preauthorizeAccountAndReceivePayment() throws JsonRpcClientErrorExce ); Payment payment = Payment.builder() .account(senderWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(senderAccountInfo.accountData().sequence()) .signingPublicKey(senderWallet.publicKey()) .amount(XrpCurrencyAmount.ofDrops(12345)) @@ -164,7 +165,7 @@ public void accountUnableToReceivePaymentsWithoutPreauthorization() throws JsonR ); Payment payment = Payment.builder() .account(senderWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(senderAccountInfo.accountData().sequence()) .signingPublicKey(senderWallet.publicKey()) .amount(XrpCurrencyAmount.ofDrops(12345)) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/IsFinalIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/IsFinalIT.java index c22b746e1..682a8a395 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/IsFinalIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/IsFinalIT.java @@ -12,6 +12,7 @@ import org.xrpl.xrpl4j.model.client.common.LedgerIndex; import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.ledger.LedgerRequestParams; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.transactions.ImmutablePayment; @@ -52,7 +53,7 @@ void setup() throws JsonRpcClientErrorException { Wallet destinationWallet = createRandomAccount(); payment = Payment.builder() .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(accountInfo.accountData().sequence()) .destination(destinationWallet.classicAddress()) .amount(XrpCurrencyAmount.ofDrops(10)) 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 805a28faa..0132ff4da 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 @@ -32,6 +32,7 @@ import org.xrpl.xrpl4j.model.client.accounts.TrustLine; import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.transactions.AccountSet; import org.xrpl.xrpl4j.model.transactions.IssuedCurrencyAmount; @@ -163,7 +164,7 @@ public void sendSimpleRipplingIssuedCurrencyPayment() throws JsonRpcClientErrorE AccountInfoResult aliceAccountInfo = getValidatedAccountInfo(aliceWallet.classicAddress()); Payment aliceToBobPayment = Payment.builder() .account(aliceWallet.classicAddress()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(aliceAccountInfo.accountData().sequence()) .destination(bobWallet.classicAddress()) .amount(IssuedCurrencyAmount.builder() @@ -307,7 +308,7 @@ public void sendMultiHopSameCurrencyPayment() throws JsonRpcClientErrorException AccountInfoResult charlieAccountInfo = getValidatedAccountInfo(charlieWallet.classicAddress()); Payment charlieToDanielPayment = Payment.builder() .account(charlieWallet.classicAddress()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(charlieAccountInfo.accountData().sequence()) .destination(danielWallet.classicAddress()) .amount(IssuedCurrencyAmount.builder() @@ -371,7 +372,7 @@ public void setDefaultRipple(Wallet issuerWallet, FeeResult feeResult) throws Js AccountSet setDefaultRipple = AccountSet.builder() .account(issuerWallet.classicAddress()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(issuerAccountInfo.accountData().sequence()) .signingPublicKey(issuerWallet.publicKey()) .setFlag(AccountSet.AccountSetFlag.DEFAULT_RIPPLE) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/OfferIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/OfferIT.java index 8367a0b6d..ea8375af8 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/OfferIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/OfferIT.java @@ -30,6 +30,7 @@ import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.flags.Flags; import org.xrpl.xrpl4j.model.ledger.OfferObject; @@ -77,7 +78,7 @@ public void ensureUsdIssued() throws JsonRpcClientErrorException { UnsignedInteger sequence = accountInfoResult.accountData().sequence(); OfferCreate offerCreate = OfferCreate.builder() .account(issuerWallet.classicAddress()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(sequence) .signingPublicKey(issuerWallet.publicKey()) .takerGets(IssuedCurrencyAmount.builder() @@ -126,7 +127,7 @@ public void createOpenOfferAndCancel() throws JsonRpcClientErrorException { UnsignedInteger sequence = accountInfoResult.accountData().sequence(); OfferCreate offerCreate = OfferCreate.builder() .account(purchaser.classicAddress()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(sequence) .signingPublicKey(purchaser.publicKey()) .takerPays(IssuedCurrencyAmount.builder() @@ -181,7 +182,7 @@ private void cancelOffer( OfferCancel offerCancel = OfferCancel.builder() .account(purchaser.classicAddress()) - .fee(xrplClient.fee().drops().minimumFee()) + .fee(FeeUtils.calculateFeeDynamically(xrplClient.fee())) .sequence(nextSequence) .offerSequence(offerSequence) .signingPublicKey(purchaser.publicKey()) @@ -213,7 +214,7 @@ public void createUnmatchedKillOrFill() throws JsonRpcClientErrorException { UnsignedInteger sequence = accountInfoResult.accountData().sequence(); OfferCreate offerCreate = OfferCreate.builder() .account(purchaser.classicAddress()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(sequence) .signingPublicKey(purchaser.publicKey()) .takerPays(IssuedCurrencyAmount.builder() @@ -278,7 +279,7 @@ public void createFullyMatchedOffer() throws JsonRpcClientErrorException { OfferCreate offerCreate = OfferCreate.builder() .account(purchaser.classicAddress()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(sequence) .signingPublicKey(purchaser.publicKey()) .takerPays(requestCurrencyAmount) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/PaymentChannelIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/PaymentChannelIT.java index 6fee8ed53..01617668c 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/PaymentChannelIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/PaymentChannelIT.java @@ -40,6 +40,7 @@ import org.xrpl.xrpl4j.model.client.channels.UnsignedClaim; 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.transactions.SubmitResult; import org.xrpl.xrpl4j.model.jackson.ObjectMapperFactory; import org.xrpl.xrpl4j.model.ledger.PayChannelObject; @@ -76,7 +77,7 @@ public void createPaymentChannel() throws JsonRpcClientErrorException { // the source and destination accounts PaymentChannelCreate createPaymentChannel = PaymentChannelCreate.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(senderAccountInfo.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(10000)) .destination(destinationWallet.classicAddress()) @@ -158,7 +159,7 @@ void createAndClaimPaymentChannel() throws JsonRpcClientErrorException, JsonProc // the source and destination accounts PaymentChannelCreate createPaymentChannel = PaymentChannelCreate.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(senderAccountInfo.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(10000000)) .destination(destinationWallet.classicAddress()) @@ -233,7 +234,7 @@ void createAndClaimPaymentChannel() throws JsonRpcClientErrorException, JsonProc // Destination account submits the signed claim to the ledger to get their XRP PaymentChannelClaim signedClaim = PaymentChannelClaim.builder() .account(destinationWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(destinationAccountInfo.accountData().sequence()) .channel(paymentChannel.channelId()) .balance(paymentChannel.balance().plus(unsignedClaim.amount())) @@ -280,7 +281,7 @@ void createAddFundsAndSetExpirationToPaymentChannel() throws JsonRpcClientErrorE // the source and destination accounts PaymentChannelCreate createPaymentChannel = PaymentChannelCreate.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(senderAccountInfo.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(10000000)) .destination(destinationWallet.classicAddress()) @@ -320,7 +321,7 @@ void createAddFundsAndSetExpirationToPaymentChannel() throws JsonRpcClientErrorE PaymentChannelFund addFunds = PaymentChannelFund.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(senderAccountInfo.accountData().sequence().plus(UnsignedInteger.ONE)) .signingPublicKey(sourceWallet.publicKey()) .channel(paymentChannel.channelId()) @@ -358,7 +359,7 @@ void createAddFundsAndSetExpirationToPaymentChannel() throws JsonRpcClientErrorE PaymentChannelFund setExpiry = PaymentChannelFund.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(senderAccountInfo.accountData().sequence().plus(UnsignedInteger.valueOf(2))) .signingPublicKey(sourceWallet.publicKey()) .channel(paymentChannel.channelId()) @@ -407,7 +408,7 @@ void testCurrentAccountChannels() throws JsonRpcClientErrorException { // the source and destination accounts PaymentChannelCreate createPaymentChannel = PaymentChannelCreate.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(senderAccountInfo.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(10000)) .destination(destinationWallet.classicAddress()) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SetRegularKeyIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SetRegularKeyIT.java index 6c89f907f..fec144e06 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SetRegularKeyIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SetRegularKeyIT.java @@ -27,6 +27,7 @@ import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.transactions.AccountSet; import org.xrpl.xrpl4j.model.transactions.SetRegularKey; @@ -55,7 +56,7 @@ void setRegularKeyOnAccount() throws JsonRpcClientErrorException { FeeResult feeResult = xrplClient.fee(); SetRegularKey setRegularKey = SetRegularKey.builder() .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(accountInfo.accountData().sequence()) .regularKey(newWallet.classicAddress()) .signingPublicKey(wallet.publicKey()) @@ -77,7 +78,7 @@ void setRegularKeyOnAccount() throws JsonRpcClientErrorException { () -> { AccountSet accountSet = AccountSet.builder() .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(accountInfo.accountData().sequence().plus(UnsignedInteger.ONE)) .signingPublicKey(newWallet.publicKey()) .build(); @@ -112,7 +113,7 @@ void removeRegularKeyFromAccount() throws JsonRpcClientErrorException { FeeResult feeResult = xrplClient.fee(); SetRegularKey setRegularKey = SetRegularKey.builder() .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(accountInfo.accountData().sequence()) .regularKey(newWallet.classicAddress()) .signingPublicKey(wallet.publicKey()) @@ -134,7 +135,7 @@ void removeRegularKeyFromAccount() throws JsonRpcClientErrorException { () -> { AccountSet accountSet = AccountSet.builder() .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(accountInfo.accountData().sequence().plus(UnsignedInteger.ONE)) .signingPublicKey(newWallet.publicKey()) .build(); @@ -150,7 +151,7 @@ void removeRegularKeyFromAccount() throws JsonRpcClientErrorException { SetRegularKey removeRegularKey = SetRegularKey.builder() .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(accountInfo.accountData().sequence().plus(UnsignedInteger.valueOf(2))) .signingPublicKey(wallet.publicKey()) .build(); diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SignerListSetIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SignerListSetIT.java index 8b9727b7a..c9fd9b812 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SignerListSetIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SignerListSetIT.java @@ -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. @@ -33,6 +33,7 @@ import org.xrpl.xrpl4j.keypairs.KeyPairService; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitMultiSignedResult; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.jackson.ObjectMapperFactory; @@ -47,6 +48,7 @@ import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount; import org.xrpl.xrpl4j.wallet.Wallet; +import java.math.BigDecimal; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; @@ -85,7 +87,7 @@ void addSignersToSignerListAndSendPayment() throws JsonRpcClientErrorException { FeeResult feeResult = xrplClient.fee(); SignerListSet signerListSet = SignerListSet.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(sourceAccountInfo.accountData().sequence()) .signerQuorum(UnsignedInteger.valueOf(2)) .addSignerEntries( @@ -110,7 +112,7 @@ void addSignersToSignerListAndSendPayment() throws JsonRpcClientErrorException { SubmitResult signerListSetResult = xrplClient.submit(sourceWallet, signerListSet); assertThat(signerListSetResult.result()).isEqualTo(TransactionResultCodes.TES_SUCCESS); assertThat(signerListSetResult.transactionResult().transaction().hash()).isNotEmpty().get() - .isEqualTo(signerListSetResult.transactionResult().hash()); + .isEqualTo(signerListSetResult.transactionResult().hash()); logger.info( "SignerListSet transaction successful: https://testnet.xrpl.org/transactions/" + signerListSetResult.transactionResult().hash() @@ -138,7 +140,7 @@ void addSignersToSignerListAndSendPayment() throws JsonRpcClientErrorException { Payment unsignedPayment = Payment.builder() .account(sourceWallet.classicAddress()) .fee( - Transaction.computeMultiSigFee( + FeeUtils.computeMultiSigFee( feeResult.drops().openLedgerFee(), sourceAccountInfoAfterSignerListSet.accountData().signerLists().get(0) ) @@ -214,7 +216,7 @@ void addSignersToSignerListThenDeleteSignerList() throws JsonRpcClientErrorExcep FeeResult feeResult = xrplClient.fee(); SignerListSet signerListSet = SignerListSet.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(sourceAccountInfo.accountData().sequence()) .signerQuorum(UnsignedInteger.valueOf(2)) .addSignerEntries( diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SubmitMultisignedIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SubmitMultisignedIT.java index e3a05f496..47f687e78 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SubmitMultisignedIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SubmitMultisignedIT.java @@ -15,6 +15,7 @@ import org.xrpl.xrpl4j.keypairs.KeyPairService; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SignedTransaction; import org.xrpl.xrpl4j.model.client.transactions.SubmitMultiSignedResult; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; @@ -75,7 +76,7 @@ public void setUp() throws JsonRpcClientErrorException { feeResult = xrplClient.fee(); SignerListSet signerListSet = SignerListSet.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(sourceAccountInfo.accountData().sequence()) .signerQuorum(UnsignedInteger.valueOf(2)) .addSignerEntries( @@ -132,7 +133,7 @@ public void submitMultisignedAndVerifyHash() throws JsonRpcClientErrorException, Payment unsignedPayment = Payment.builder() .account(sourceWallet.classicAddress()) .fee( - Transaction.computeMultiSigFee( + FeeUtils.computeMultiSigFee( feeResult.drops().openLedgerFee(), sourceAccountInfoAfterSignerListSet.accountData().signerLists().get(0) ) @@ -201,7 +202,7 @@ public void submitMultisignedWithSignersInDescOrderAndVerifyHash() throws Payment unsignedPayment = Payment.builder() .account(sourceWallet.classicAddress()) .fee( - Transaction.computeMultiSigFee( + FeeUtils.computeMultiSigFee( feeResult.drops().openLedgerFee(), sourceAccountInfoAfterSignerListSet.accountData().signerLists().get(0) ) 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 ad712fe9d..9f38b320d 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 @@ -27,6 +27,7 @@ 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.LedgerRequestParams; import org.xrpl.xrpl4j.model.client.ledger.LedgerResult; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; @@ -50,7 +51,7 @@ public void sendPayment() throws JsonRpcClientErrorException { XrpCurrencyAmount amount = XrpCurrencyAmount.ofDrops(12345); Payment payment = Payment.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(accountInfo.accountData().sequence()) .destination(destinationWallet.classicAddress()) .amount(amount) @@ -93,7 +94,7 @@ public void sendPaymentFromSecp256k1Wallet() throws JsonRpcClientErrorException Payment payment = Payment.builder() .account(senderWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(accountInfo.accountData().sequence()) .destination(destinationWallet.classicAddress()) .amount(XrpCurrencyAmount.ofDrops(12345)) 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 f4a89fa18..29e31572b 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 @@ -27,6 +27,7 @@ import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.ledger.TicketObject; import org.xrpl.xrpl4j.model.transactions.AccountSet; @@ -49,7 +50,7 @@ void createTicketAndUseSequenceNumber() throws JsonRpcClientErrorException { TicketCreate ticketCreate = TicketCreate.builder() .account(sourceWallet.classicAddress()) .sequence(accountInfo.accountData().sequence()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .ticketCount(UnsignedInteger.ONE) .signingPublicKey(sourceWallet.publicKey()) .build(); @@ -71,7 +72,7 @@ void createTicketAndUseSequenceNumber() throws JsonRpcClientErrorException { AccountSet accountSet = AccountSet.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .ticketSequence(tickets.get(0).ticketSequence()) .signingPublicKey(sourceWallet.publicKey()) .build(); diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/TransactionWithMemoIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/TransactionWithMemoIT.java index 1575c0615..8456d5a57 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/TransactionWithMemoIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/TransactionWithMemoIT.java @@ -26,6 +26,7 @@ import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.client.transactions.TransactionResult; import org.xrpl.xrpl4j.model.transactions.Memo; @@ -51,7 +52,7 @@ public void transactionWithMemoNibble() throws JsonRpcClientErrorException { XrpCurrencyAmount amount = XrpCurrencyAmount.ofDrops(12345); Payment payment = Payment.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(accountInfo.accountData().sequence()) .destination(destinationWallet.classicAddress()) .amount(amount) @@ -96,7 +97,7 @@ public void transactionWithPlaintextMemo() throws JsonRpcClientErrorException { XrpCurrencyAmount amount = XrpCurrencyAmount.ofDrops(12345); Payment payment = Payment.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.calculateFeeDynamically(feeResult)) .sequence(accountInfo.accountData().sequence()) .destination(destinationWallet.classicAddress()) .amount(amount) diff --git a/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/fees/FeeUtils.java b/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/fees/FeeUtils.java new file mode 100644 index 000000000..9fe1fa631 --- /dev/null +++ b/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/fees/FeeUtils.java @@ -0,0 +1,94 @@ +package org.xrpl.xrpl4j.model.client.fees; + +import com.google.common.primitives.UnsignedLong; +import org.xrpl.xrpl4j.model.ledger.SignerListObject; +import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount; + +import java.util.Objects; + +public class FeeUtils { + + /** + * Computes the fee necessary for a multisigned transaction. + * + *

The transaction cost of a multisigned transaction must be at least {@code (N + 1) * (the normal + * transaction cost)}, where {@code N} is the number of signatures provided. + * + * @param currentLedgerFeeDrops The current ledger fee, represented as an {@link XrpCurrencyAmount}. + * @param signerList The {@link SignerListObject} containing the signers of the transaction. + * + * @return An {@link XrpCurrencyAmount} representing the multisig fee. + */ + public static XrpCurrencyAmount computeMultiSigFee( + final XrpCurrencyAmount currentLedgerFeeDrops, + final SignerListObject signerList + ) { + Objects.requireNonNull(currentLedgerFeeDrops); + Objects.requireNonNull(signerList); + + return currentLedgerFeeDrops + .times(XrpCurrencyAmount.of(UnsignedLong.valueOf(signerList.signerEntries().size() + 1))); + } + + + /** + * Get value of fee to be used for submitting a transaction on the ledger. The value is calculated depending on the + * load on the job queue. + * + * @param feeResult {@link FeeResult} object received from XrplClient#fee() rippled call. + * + * @return {@link XrpCurrencyAmount} value of the fee that should be used for the transaction. + */ + public static XrpCurrencyAmount calculateFeeDynamically(final FeeResult feeResult) { + Objects.requireNonNull(feeResult); + + double currentQueueSize = feeResult.currentQueueSize().doubleValue(); + double maxQueueSize = feeResult.maxQueueSize().get().doubleValue(); + double queuePercentage = currentQueueSize / maxQueueSize; + FeeDrops drops = feeResult.drops(); + int minimumFee = drops.minimumFee().value().intValue(); + int medianFee = drops.medianFee().value().intValue(); + int openLedgerFee = drops.openLedgerFee().value().intValue(); + + // calculate the lowest fee the user is able to pay if the queue is empty + final long feeLow = Math.round( + Math.min( + Math.max( + minimumFee * 1.5, + Math.round(Math.max(medianFee, openLedgerFee) / 500) + ), + 1000 + ) + ); + + long possibleFeeMedium; + if (queuePercentage > 0.1) { + possibleFeeMedium = Math.round((minimumFee + openLedgerFee) / 3); + } else if (queuePercentage == 0) { + possibleFeeMedium = Math.max(10 * minimumFee, openLedgerFee); + } else { + possibleFeeMedium = Math.max(10 * minimumFee, Math.round((minimumFee + medianFee) / 2)); + } + + // calculate the lowest fee the user is able to pay if there are txns in the queue + final long feeMedium = Math.round(Math.min(possibleFeeMedium, Math.min((int) (feeLow * 15), 10000))); + + // calculate the lowest fee the user is able to pay if the txn queue is full + final long feeHigh = Math.round(Math.min( + Math.max(10 * minimumFee, Math.round(Math.max(medianFee, openLedgerFee) * 1.1)), + 100000 + )); + + XrpCurrencyAmount fee; + if (queuePercentage == 0) { // if queue is empty + fee = XrpCurrencyAmount.ofDrops(feeLow); + } else if (0 < queuePercentage && queuePercentage < 1) { // queue has txns in it but is not full + fee = XrpCurrencyAmount.ofDrops(feeMedium); + } else { // if queue is full + fee = XrpCurrencyAmount.ofDrops(feeHigh); + } + + return fee; + } + +} diff --git a/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/transactions/Transaction.java b/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/transactions/Transaction.java index 528dbb4fa..e55fc71f3 100644 --- a/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/transactions/Transaction.java +++ b/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/transactions/Transaction.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. @@ -95,6 +95,7 @@ public interface Transaction { * * @return An {@link XrpCurrencyAmount} representing the multisig fee. */ + @Deprecated static XrpCurrencyAmount computeMultiSigFee( final XrpCurrencyAmount currentLedgerFeeDrops, final SignerListObject signerList @@ -232,7 +233,7 @@ default UnsignedInteger sequence() { * * @return An optionally-present {@link UnsignedLong}. * @deprecated This field will be removed in favor of {@link - * org.xrpl.xrpl4j.model.client.transactions.TransactionResult#closeDate()}; + * org.xrpl.xrpl4j.model.client.transactions.TransactionResult#closeDate()}; */ @JsonProperty("date") @Deprecated diff --git a/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/client/fees/FeeUtilsTest.java b/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/client/fees/FeeUtilsTest.java new file mode 100644 index 000000000..8e934e76b --- /dev/null +++ b/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/client/fees/FeeUtilsTest.java @@ -0,0 +1,224 @@ +package org.xrpl.xrpl4j.model.client.fees; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.xrpl.xrpl4j.model.client.fees.FeeUtils.calculateFeeDynamically; +import static org.xrpl.xrpl4j.model.client.fees.FeeUtils.computeMultiSigFee; + +import com.google.common.primitives.UnsignedInteger; +import org.junit.jupiter.api.Test; +import org.xrpl.xrpl4j.model.client.common.LedgerIndex; +import org.xrpl.xrpl4j.model.flags.Flags; +import org.xrpl.xrpl4j.model.ledger.SignerEntry; +import org.xrpl.xrpl4j.model.ledger.SignerEntryWrapper; +import org.xrpl.xrpl4j.model.ledger.SignerListObject; +import org.xrpl.xrpl4j.model.transactions.Address; +import org.xrpl.xrpl4j.model.transactions.Hash256; +import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount; + +/** + * Unit tests for {@link FeeUtils}. + */ +public class FeeUtilsTest { + + @Test + public void nullInputForComputeMultiSigFee() { + assertThrows( + NullPointerException.class, + () -> computeMultiSigFee(null, null) + ); + + assertThrows( + NullPointerException.class, + () -> computeMultiSigFee(XrpCurrencyAmount.ofDrops(20), null) + ); + } + + @Test + public void simpleComputeMultiSigFee() { + XrpCurrencyAmount xrpCurrencyAmount = XrpCurrencyAmount.ofDrops(20); + SignerListObject object = SignerListObject.builder() + .flags(Flags.SignerListFlags.UNSET) + .ownerNode("0000000000000000") + .previousTransactionId(Hash256.of("5904C0DC72C58A83AEFED2FFC5386356AA83FCA6A88C89D00646E51E687CDBE4")) + .previousTransactionLedgerSequence(UnsignedInteger.valueOf(16061435)) + .addSignerEntries( + SignerEntryWrapper.of( + SignerEntry.builder() + .account(Address.of("rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW")) + .signerWeight(UnsignedInteger.valueOf(2)) + .build() + ), + SignerEntryWrapper.of( + SignerEntry.builder() + .account(Address.of("raKEEVSGnKSD9Zyvxu4z6Pqpm4ABH8FS6n")) + .signerWeight(UnsignedInteger.valueOf(1)) + .build() + ) + ) + .signerListId(UnsignedInteger.ZERO) + .signerQuorum(UnsignedInteger.valueOf(3)) + .index(Hash256.of("A9C28A28B85CD533217F5C0A0C7767666B093FA58A0F2D80026FCC4CD932DDC7")) + .build(); + + assertThat(computeMultiSigFee(xrpCurrencyAmount, object)).isEqualTo(XrpCurrencyAmount.ofDrops(60)); + } + + @Test + public void nullInputForCalculateFeeDynamically() { + assertThrows( + NullPointerException.class, + () -> calculateFeeDynamically(null) + ); + } + + @Test + public void calculateFeeDynamicallyForAlmostEmptyQueue() { + FeeResult feeResult = FeeResult.builder() + .currentLedgerSize(UnsignedInteger.valueOf(56)) + .currentQueueSize(UnsignedInteger.valueOf(1)) + .drops( + FeeDrops.builder() + .baseFee(XrpCurrencyAmount.ofDrops(10)) + .medianFee(XrpCurrencyAmount.ofDrops(10000)) + .minimumFee(XrpCurrencyAmount.ofDrops(10)) + .openLedgerFee(XrpCurrencyAmount.ofDrops(2653937)) + .build() + ) + .expectedLedgerSize(UnsignedInteger.valueOf(55)) + .ledgerCurrentIndex(LedgerIndex.of(UnsignedInteger.valueOf(26575101))) + .levels( + FeeLevels.builder() + .medianLevel(XrpCurrencyAmount.ofDrops(256000)) + .minimumLevel(XrpCurrencyAmount.ofDrops(256)) + .openLedgerLevel(XrpCurrencyAmount.ofDrops(67940792)) + .referenceLevel(XrpCurrencyAmount.ofDrops(256)) + .build() + ) + .maxQueueSize(UnsignedInteger.valueOf(1100)) + .status("success") + .build(); + + assertThat(calculateFeeDynamically(feeResult)).isEqualTo(XrpCurrencyAmount.ofDrops(5005)); + } + + @Test + public void calculateFeeDynamicallyForModeratelyFilledQueue() { + FeeResult feeResult = FeeResult.builder() + .currentLedgerSize(UnsignedInteger.valueOf(56)) + .currentQueueSize(UnsignedInteger.valueOf(220)) + .drops( + FeeDrops.builder() + .baseFee(XrpCurrencyAmount.ofDrops(10)) + .medianFee(XrpCurrencyAmount.ofDrops(10000)) + .minimumFee(XrpCurrencyAmount.ofDrops(10)) + .openLedgerFee(XrpCurrencyAmount.ofDrops(2653937)) + .build() + ) + .expectedLedgerSize(UnsignedInteger.valueOf(55)) + .ledgerCurrentIndex(LedgerIndex.of(UnsignedInteger.valueOf(26575101))) + .levels( + FeeLevels.builder() + .medianLevel(XrpCurrencyAmount.ofDrops(256000)) + .minimumLevel(XrpCurrencyAmount.ofDrops(256)) + .openLedgerLevel(XrpCurrencyAmount.ofDrops(67940792)) + .referenceLevel(XrpCurrencyAmount.ofDrops(256)) + .build() + ) + .maxQueueSize(UnsignedInteger.valueOf(1100)) + .status("success") + .build(); + + assertThat(calculateFeeDynamically(feeResult)).isEqualTo(XrpCurrencyAmount.ofDrops(10000)); + } + + @Test + public void calculateFeeDynamicallyForLessThanModerateTraffic() { + FeeResult feeResult = FeeResult.builder() + .currentLedgerSize(UnsignedInteger.valueOf(56)) + .currentQueueSize(UnsignedInteger.valueOf(100)) + .drops( + FeeDrops.builder() + .baseFee(XrpCurrencyAmount.ofDrops(10)) + .medianFee(XrpCurrencyAmount.ofDrops(10000)) + .minimumFee(XrpCurrencyAmount.ofDrops(10)) + .openLedgerFee(XrpCurrencyAmount.ofDrops(2653937)) + .build() + ) + .expectedLedgerSize(UnsignedInteger.valueOf(55)) + .ledgerCurrentIndex(LedgerIndex.of(UnsignedInteger.valueOf(26575101))) + .levels( + FeeLevels.builder() + .medianLevel(XrpCurrencyAmount.ofDrops(256000)) + .minimumLevel(XrpCurrencyAmount.ofDrops(256)) + .openLedgerLevel(XrpCurrencyAmount.ofDrops(67940792)) + .referenceLevel(XrpCurrencyAmount.ofDrops(256)) + .build() + ) + .maxQueueSize(UnsignedInteger.valueOf(1100)) + .status("success") + .build(); + + assertThat(calculateFeeDynamically(feeResult)).isEqualTo(XrpCurrencyAmount.ofDrops(5005)); + } + + @Test + public void calculateFeeDynamicallyForCompletelyFilledQueue() { + FeeResult feeResult = FeeResult.builder() + .currentLedgerSize(UnsignedInteger.valueOf(56)) + .currentQueueSize(UnsignedInteger.valueOf(110)) + .drops( + FeeDrops.builder() + .baseFee(XrpCurrencyAmount.ofDrops(10)) + .medianFee(XrpCurrencyAmount.ofDrops(100)) + .minimumFee(XrpCurrencyAmount.ofDrops(10)) + .openLedgerFee(XrpCurrencyAmount.ofDrops(2657)) + .build() + ) + .expectedLedgerSize(UnsignedInteger.valueOf(55)) + .ledgerCurrentIndex(LedgerIndex.of(UnsignedInteger.valueOf(26575101))) + .levels( + FeeLevels.builder() + .medianLevel(XrpCurrencyAmount.ofDrops(256000)) + .minimumLevel(XrpCurrencyAmount.ofDrops(256)) + .openLedgerLevel(XrpCurrencyAmount.ofDrops(67940792)) + .referenceLevel(XrpCurrencyAmount.ofDrops(256)) + .build() + ) + .maxQueueSize(UnsignedInteger.valueOf(110)) + .status("success") + .build(); + + assertThat(calculateFeeDynamically(feeResult)).isEqualTo(XrpCurrencyAmount.ofDrops(2923)); + } + + @Test + public void calculateFeeDynamicallyForEmptyQueue() { + FeeResult feeResult = FeeResult.builder() + .currentLedgerSize(UnsignedInteger.valueOf(56)) + .currentQueueSize(UnsignedInteger.valueOf(0)) + .drops( + FeeDrops.builder() + .baseFee(XrpCurrencyAmount.ofDrops(10)) + .medianFee(XrpCurrencyAmount.ofDrops(100)) + .minimumFee(XrpCurrencyAmount.ofDrops(10)) + .openLedgerFee(XrpCurrencyAmount.ofDrops(2657)) + .build() + ) + .expectedLedgerSize(UnsignedInteger.valueOf(55)) + .ledgerCurrentIndex(LedgerIndex.of(UnsignedInteger.valueOf(26575101))) + .levels( + FeeLevels.builder() + .medianLevel(XrpCurrencyAmount.ofDrops(256000)) + .minimumLevel(XrpCurrencyAmount.ofDrops(256)) + .openLedgerLevel(XrpCurrencyAmount.ofDrops(67940792)) + .referenceLevel(XrpCurrencyAmount.ofDrops(256)) + .build() + ) + .maxQueueSize(UnsignedInteger.valueOf(110)) + .status("success") + .build(); + + assertThat(calculateFeeDynamically(feeResult)).isEqualTo(XrpCurrencyAmount.ofDrops(15)); + } +} From f38c2ebcd5af69c1dafe9b3c1aff72e5ebc3b77a Mon Sep 17 00:00:00 2001 From: David Fuelling Date: Thu, 23 Jun 2022 08:37:07 -0600 Subject: [PATCH 08/12] Update Javadoc Signed-off-by: David Fuelling --- .../java/org/xrpl/xrpl4j/crypto/bc/BcAddressUtils.java | 4 ++++ .../java/org/xrpl/xrpl4j/crypto/core/AddressUtils.java | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/bc/BcAddressUtils.java b/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/bc/BcAddressUtils.java index 9dc6c9c88..6ea08b32c 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/bc/BcAddressUtils.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/main/java/org/xrpl/xrpl4j/crypto/bc/BcAddressUtils.java @@ -12,7 +12,11 @@ /** * A service to help with interactions involving XRPL addresses. + * + * @deprecated This interface will be removed in a future version. Prefer + * {@link org.xrpl.xrpl4j.crypto.core.keys.KeyPairService} instead. */ +@Deprecated public class BcAddressUtils implements AddressUtils { private static final BcAddressUtils INSTANCE = new BcAddressUtils(); diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/AddressUtils.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/AddressUtils.java index 9353c14c2..0ee5f5827 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/AddressUtils.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/AddressUtils.java @@ -5,7 +5,11 @@ /** * A utility interface to help with interactions involving XRPL addresses. + * + * @deprecated This interface will be removed in a future version. Prefer + * {@link org.xrpl.xrpl4j.crypto.core.keys.KeyPairService} instead. */ +@Deprecated public interface AddressUtils { /** @@ -14,7 +18,11 @@ public interface AddressUtils { * @param publicKey The hexadecimal encoded public key of the account. * * @return A Base58Check encoded XRPL address in Classic Address form. + * + * @deprecated This interface will be removed in a future version. Prefer + * {@link org.xrpl.xrpl4j.crypto.core.keys.KeyPairService} instead. */ + @Deprecated Address deriveAddress(final PublicKey publicKey); } From 0aa0c9ae48d77ea8c659842436844bf0f15eab1c Mon Sep 17 00:00:00 2001 From: David Fuelling <323659+sappenin@users.noreply.github.com> Date: Mon, 27 Jun 2022 12:53:35 -0600 Subject: [PATCH 09/12] Df/276 Enhance `AddressUtils` (#281) * Update Javadoc * Update and commit to AddressUtils Signed-off-by: David Fuelling --- .../xrpl4j/crypto/bc/BcAddressUtilsTest.java | 14 ++++++- .../xrpl/xrpl4j/crypto/core/AddressUtils.java | 39 ++++++++++++++++++ .../xrpl/xrpl4j/keypairs/KeyPairService.java | 41 +++++++++++++++---- 3 files changed, 85 insertions(+), 9 deletions(-) diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/test/java/org/xrpl/xrpl4j/crypto/bc/BcAddressUtilsTest.java b/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/test/java/org/xrpl/xrpl4j/crypto/bc/BcAddressUtilsTest.java index c06964a1c..11193558e 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/test/java/org/xrpl/xrpl4j/crypto/bc/BcAddressUtilsTest.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-bouncycastle/src/test/java/org/xrpl/xrpl4j/crypto/bc/BcAddressUtilsTest.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.xrpl.xrpl4j.crypto.bc.wallet.BcWalletFactory; +import org.xrpl.xrpl4j.crypto.core.AddressUtils; import org.xrpl.xrpl4j.crypto.core.keys.Passphrase; import org.xrpl.xrpl4j.crypto.core.keys.PublicKey; import org.xrpl.xrpl4j.crypto.core.keys.Seed; @@ -16,16 +17,25 @@ class BcAddressUtilsTest { @Test - public void deriveAddressWithNull() { + void deriveAddressWithNull() { Assertions.assertThrows(NullPointerException.class, () -> BcAddressUtils.getInstance().deriveAddress(null)); } @Test - public void deriveAddress() { + void deriveAddress() { PublicKey publicKey = BcWalletFactory.getInstance() .fromSeed(Seed.ed25519SeedFromPassphrase(Passphrase.of("hello"))).publicKey(); Address address = BcAddressUtils.getInstance().deriveAddress(publicKey); assertThat(address.value()).isEqualTo("rwGWYtRR6jJJJq7FKQg74YwtkiPyUqJ466"); } + @Test + void testAddressConstants() { + assertThat(AddressUtils.ACCOUNT_ZERO.value()).isEqualTo("rrrrrrrrrrrrrrrrrrrrrhoLvTp"); + assertThat(AddressUtils.ACCOUNT_ONE.value()).isEqualTo("rrrrrrrrrrrrrrrrrrrrBZbvji"); + assertThat(AddressUtils.GENESIS_ACCOUNT.value()).isEqualTo("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"); + assertThat(AddressUtils.NAME_RESERVATION_BLACKHOLD.value()).isEqualTo("rrrrrrrrrrrrrrrrrNAMEtxvNvQ"); + assertThat(AddressUtils.NAN_ADDRESS.value()).isEqualTo("rrrrrrrrrrrrrrrrrrrn5RM1rHd"); + } + } \ No newline at end of file diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/AddressUtils.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/AddressUtils.java index 9353c14c2..48afba0ba 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/AddressUtils.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/AddressUtils.java @@ -8,6 +8,45 @@ */ public interface AddressUtils { + /** + * An address that is the XRP Ledger's base58 encoding of the value 0. In peer-to-peer communications, rippled uses + * this address as the issuer for XRP. This is a Black hole account. + * + * @see "https://xrpl.org/accounts.html#special-addresses" + */ + Address ACCOUNT_ZERO = Address.of("rrrrrrrrrrrrrrrrrrrrrhoLvTp"); + + /** + * An address that is the XRP Ledger's base58 encoding of the value 1. In the ledger, RippleState entries use this + * address as a placeholder for the issuer of a trust line balance. This is a Black hole account. + * + * @see "https://xrpl.org/accounts.html#special-addresses" + */ + Address ACCOUNT_ONE = Address.of("rrrrrrrrrrrrrrrrrrrrBZbvji"); + + /** + * When rippled starts a new genesis ledger from scratch (for example, in stand-alone mode), this account holds all + * the XRP. This address is generated from the seed value masterpassphrase which is hard-coded. + * + * @see "https://xrpl.org/accounts.html#special-addresses" + */ + Address GENESIS_ACCOUNT = Address.of("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"); + + /** + * In the past, Ripple asked users to send XRP to this account to reserve Ripple Names. This is a Black hole account. + * + * @see "https://xrpl.org/accounts.html#special-addresses" + */ + Address NAME_RESERVATION_BLACKHOLD = Address.of("rrrrrrrrrrrrrrrrrNAMEtxvNvQ"); + + /** + * Previous versions of ripple-lib generated this address when encoding the value NaN using the XRP Ledger's base58 + * string encoding format. This is a Black hole account. + * + * @see "https://xrpl.org/accounts.html#special-addresses" + */ + Address NAN_ADDRESS = Address.of("rrrrrrrrrrrrrrrrrrrn5RM1rHd"); + /** * Derive an XRPL address from a public key. * diff --git a/xrpl4j-keypairs/src/main/java/org/xrpl/xrpl4j/keypairs/KeyPairService.java b/xrpl4j-keypairs/src/main/java/org/xrpl/xrpl4j/keypairs/KeyPairService.java index 116406322..e67e216ad 100644 --- a/xrpl4j-keypairs/src/main/java/org/xrpl/xrpl4j/keypairs/KeyPairService.java +++ b/xrpl4j-keypairs/src/main/java/org/xrpl/xrpl4j/keypairs/KeyPairService.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. @@ -24,8 +24,8 @@ import org.xrpl.xrpl4j.model.transactions.Address; /** - * Interface of a service that can perform the crypto operations necessary to create a wallet, - * sign and verify XRPL transactions, and derive XRPL addresses. + * Interface of a service that can perform the crypto operations necessary to create a wallet, sign and verify XRPL + * transactions, and derive XRPL addresses. * * @deprecated This class will go away in a future version. Prefer xrpl4j-crypto variants instead. */ @@ -36,7 +36,10 @@ public interface KeyPairService { * Generate a random 16 byte seed to be used to derive a private key. * * @return A {@link String} containing a randomly generated Base58Check encoded seed value. + * + * @deprecated This class will go away in a future version. Prefer xrpl4j-crypto variants instead. */ + @Deprecated String generateSeed(); /** @@ -45,7 +48,10 @@ public interface KeyPairService { * @param entropy An {@link UnsignedByteArray} containing the bytes of entropy to encode into a seed. * * @return A {@link String} containing the Base58Check encoded seed value. + * + * @deprecated This class will go away in a future version. Prefer xrpl4j-crypto variants instead. */ + @Deprecated String generateSeed(UnsignedByteArray entropy); /** @@ -54,7 +60,10 @@ public interface KeyPairService { * @param seed A Base58Check encoded {@link String} containing the seed. * * @return The {@link KeyPair} derived from the seed. + * + * @deprecated This class will go away in a future version. Prefer xrpl4j-crypto variants instead. */ + @Deprecated KeyPair deriveKeyPair(String seed); /** @@ -64,7 +73,10 @@ public interface KeyPairService { * @param privateKey The hexadecimal encoded private key used to sign the transaction. * * @return The signed message, in hexadecimal form. + * + * @deprecated This class will go away in a future version. Prefer xrpl4j-crypto variants instead. */ + @Deprecated String sign(UnsignedByteArray message, String privateKey); /** @@ -74,7 +86,8 @@ public interface KeyPairService { * @param privateKey The hexadecimal encoded private key used to sign the transaction. * * @return The signed message, in hexadecimal form. - * @deprecated Consider using TransactionSigner instead. + * + * @deprecated Consider using TransactionSigner instead from xrpl4j-crypto. */ @Deprecated String sign(String message, String privateKey); @@ -84,10 +97,14 @@ public interface KeyPairService { * * @param message The arbitrary message that was signed with a private key. * @param signature The hexadecimal encoded {@link String} containing the signature to verify. - * @param publicKey The hexadecimal encoded public key derived from the private key that was used to sign the message. + * @param publicKey The hexadecimal encoded public key derived from the private key that was used to sign the + * message. * * @return true if the signature is valid, false if not. + * + * @deprecated Consider using TransactionVerifier instead from xrpl4j-crypto. */ + @Deprecated boolean verify(UnsignedByteArray message, String signature, String publicKey); /** @@ -95,10 +112,14 @@ public interface KeyPairService { * * @param message The hexadecimal encoded arbitrary message that was signed with a private key. * @param signature The hexadecimal encoded {@link String} containing the signature to verify. - * @param publicKey The hexadecimal encoded public key derived from the private key that was used to sign the message. + * @param publicKey The hexadecimal encoded public key derived from the private key that was used to sign the + * message. * * @return true if the signature is valid, false if not. + * + * @deprecated Consider using TransactionVerifier instead from xrpl4j-crypto. */ + @Deprecated boolean verify(String message, String signature, String publicKey); /** @@ -107,7 +128,10 @@ public interface KeyPairService { * @param publicKey The hexadecimal encoded public key of the account. * * @return A Base58Check encoded XRPL address in Classic Address form. + * + * @deprecated Consider using AddressUtils instead. */ + @Deprecated Address deriveAddress(String publicKey); /** @@ -116,7 +140,10 @@ public interface KeyPairService { * @param publicKey The public key of the account. * * @return A Base58Check encoded XRPL address in Classic Address form. + * + * @deprecated Consider using AddressUtils instead. */ + @Deprecated Address deriveAddress(UnsignedByteArray publicKey); } From 698c030a7438d4b51d548fd8beffb932dfb327b9 Mon Sep 17 00:00:00 2001 From: Mukul Jangid <49061120+mukulljangid@users.noreply.github.com> Date: Mon, 18 Jul 2022 12:36:52 -0400 Subject: [PATCH 10/12] Mj/use feeutils v3 (#280) * use FeeUtils for v3 ITs Co-authored-by: David Fuelling --- .../org/xrpl/xrpl4j/tests/AbstractIT.java | 36 +- .../org/xrpl/xrpl4j/tests/AccountSetIT.java | 29 +- .../java/org/xrpl/xrpl4j/tests/CheckIT.java | 13 +- .../xrpl/xrpl4j/tests/DepositPreAuthIT.java | 7 +- .../java/org/xrpl/xrpl4j/tests/IsFinalIT.java | 3 +- .../xrpl/xrpl4j/tests/IssuedCurrencyIT.java | 7 +- .../java/org/xrpl/xrpl4j/tests/OfferIT.java | 11 +- .../xrpl/xrpl4j/tests/PaymentChannelIT.java | 15 +- .../xrpl/xrpl4j/tests/SetRegularKeyIT.java | 11 +- .../xrpl/xrpl4j/tests/SignerListSetIT.java | 18 +- .../xrpl4j/tests/SubmitMultisignedIT.java | 16 +- .../xrpl/xrpl4j/tests/SubmitPaymentIT.java | 5 +- .../java/org/xrpl/xrpl4j/tests/TicketIT.java | 5 +- .../xrpl4j/tests/TransactionWithMemoIT.java | 5 +- .../xrpl/xrpl4j/tests/v3/AccountSetIT.java | 9 +- .../org/xrpl/xrpl4j/tests/v3/CheckIT.java | 13 +- .../xrpl4j/tests/v3/DepositPreAuthIT.java | 12 +- .../tests/v3/FreezeIssuedCurrencyIT.java | 45 +- .../xrpl4j/tests/v3/IssuedCurrencyIT.java | 46 +- .../org/xrpl/xrpl4j/tests/v3/OfferIT.java | 13 +- .../xrpl4j/tests/v3/PaymentChannelIT.java | 15 +- .../xrpl/xrpl4j/tests/v3/SetRegularKeyIT.java | 11 +- .../xrpl/xrpl4j/tests/v3/SignerListSetIT.java | 12 +- .../xrpl/xrpl4j/tests/v3/SubmitPaymentIT.java | 5 +- ...ransactUsingDelegatedSignatureService.java | 14 +- .../v3/TransactUsingSignatureService.java | 14 +- .../AccountCurrenciesRequestParams.java | 16 +- .../client/fees/ComputedNetworkFees.java | 91 +++ .../xrpl4j/model/client/fees/FeeResult.java | 16 +- .../xrpl4j/model/client/fees/FeeUtils.java | 432 +++++++++++++ .../model/transactions/CurrencyAmount.java | 24 +- .../model/transactions/Transaction.java | 7 +- .../xrpl4j/model/transactions/Wrappers.java | 30 +- .../model/client/fees/DecomposedFeesTest.java | 151 +++++ .../model/client/fees/FeeUtilsTest.java | 574 ++++++++++++++++++ .../client/fees/NetworkFeeResultTests.java | 78 +++ .../transactions/CurrencyAmountTest.java | 11 +- .../model/transactions/EscrowFinishTest.java | 2 +- 38 files changed, 1621 insertions(+), 201 deletions(-) create mode 100644 xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/fees/ComputedNetworkFees.java create mode 100644 xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/fees/FeeUtils.java create mode 100644 xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/client/fees/DecomposedFeesTest.java create mode 100644 xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/client/fees/FeeUtilsTest.java create mode 100644 xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/client/fees/NetworkFeeResultTests.java 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 5c4c53f1b..368834d06 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. @@ -41,6 +41,9 @@ import org.xrpl.xrpl4j.model.client.accounts.AccountObjectsResult; import org.xrpl.xrpl4j.model.client.accounts.TrustLine; import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; +import org.xrpl.xrpl4j.model.client.fees.ComputedNetworkFees; +import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.ledger.LedgerRequestParams; import org.xrpl.xrpl4j.model.client.ledger.LedgerResult; import org.xrpl.xrpl4j.model.client.path.RipplePathFindRequestParams; @@ -63,6 +66,7 @@ import org.xrpl.xrpl4j.wallet.Wallet; import org.xrpl.xrpl4j.wallet.WalletFactory; +import java.math.BigDecimal; import java.time.Instant; import java.util.List; import java.util.Objects; @@ -262,8 +266,8 @@ 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 given issuer and counterparty accounts for the given currency code and with the + * given limit. * * @param currency The currency code of the trustline to create. * @param value The trustline limit of the trustline to create. @@ -272,6 +276,7 @@ protected Instant xrpTimestampToInstant(UnsignedLong xrpTimeStamp) { * @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( @@ -367,4 +372,27 @@ public void sendIssuedCurrency( ); } + + /** + * Send issued currency funds from an issuer to a counterparty. + * + * @param feeResult The {@link FeeResult} which has information from the api call to the network. + * + * @return The {@link ComputedNetworkFees} object woth 3 levels of fees. + * + * @throws JsonRpcClientErrorException If anything goes wrong while communicating with rippled. + */ + protected XrpCurrencyAmount getComputedNetworkFee(FeeResult feeResult) { + ComputedNetworkFees networkFeeResult = FeeUtils.computeNetworkFees(feeResult); + final FeeUtils.DecomposedFees decomposedFees = FeeUtils.DecomposedFees.builder(feeResult); + final BigDecimal queuePercentage = decomposedFees.queuePercentage(); + + if (FeeUtils.queueIsEmpty(queuePercentage)) { + return networkFeeResult.feeLow(); + } else if (FeeUtils.queueIsNotEmptyAndNotFull(queuePercentage)) { + return networkFeeResult.feeMedium(); + } else { + return networkFeeResult.feeHigh(); + } + } } diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountSetIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountSetIT.java index 8b8982c06..ec72e4c05 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountSetIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountSetIT.java @@ -9,9 +9,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,6 +27,7 @@ import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.flags.Flags; import org.xrpl.xrpl4j.model.flags.Flags.AccountRootFlags; @@ -61,19 +62,19 @@ public void enableAllAndDisableOne() throws JsonRpcClientErrorException { // Set asfAccountTxnID (no corresponding ledger flag) FeeResult feeResult = xrplClient.fee(); AccountSet accountSet = AccountSet.builder() - .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) - .sequence(accountInfo.accountData().sequence()) - .setFlag(AccountSetFlag.ACCOUNT_TXN_ID) - .signingPublicKey(wallet.publicKey()) - .build(); + .account(wallet.classicAddress()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) + .sequence(accountInfo.accountData().sequence()) + .setFlag(AccountSetFlag.ACCOUNT_TXN_ID) + .signingPublicKey(wallet.publicKey()) + .build(); SubmitResult response = xrplClient.submit(wallet, accountSet); assertThat(response.result()).isEqualTo(TransactionResultCodes.TES_SUCCESS); assertThat(response.transactionResult().transaction().hash()).isNotEmpty().get() - .isEqualTo(response.transactionResult().hash()); + .isEqualTo(response.transactionResult().hash()); logger.info( - "AccountSet transaction successful: https://testnet.xrpl.org/transactions/" + response.transactionResult().hash() + "AccountSet transaction successful: https://testnet.xrpl.org/transactions/" + response.transactionResult().hash() ); /////////////////////// @@ -103,7 +104,7 @@ public void enableAllAndDisableOne() throws JsonRpcClientErrorException { ).accountData().flags(); assertThat(flags1.getValue() - flags2.getValue()) - .isEqualTo(AccountRootFlags.GLOBAL_FREEZE.getValue()); + .isEqualTo(AccountRootFlags.GLOBAL_FREEZE.getValue()); } @Test @@ -123,7 +124,7 @@ public void disableAndEnableAllFlags() throws JsonRpcClientErrorException { FeeResult feeResult = xrplClient.fee(); AccountSet accountSet = AccountSet.builder() .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence()) .setFlag(AccountSetFlag.ACCOUNT_TXN_ID) .signingPublicKey(wallet.publicKey()) @@ -292,7 +293,7 @@ private void assertSetFlag( FeeResult feeResult = xrplClient.fee(); AccountSet accountSet = AccountSet.builder() .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(sequence) .setFlag(accountSetFlag) .signingPublicKey(wallet.publicKey()) @@ -329,7 +330,7 @@ private void assertClearFlag( FeeResult feeResult = xrplClient.fee(); AccountSet accountSet = AccountSet.builder() .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(sequence) .clearFlag(accountSetFlag) .signingPublicKey(wallet.publicKey()) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/CheckIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/CheckIT.java index 56f73f385..3bd556d09 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/CheckIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/CheckIT.java @@ -28,6 +28,7 @@ import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.ledger.CheckObject; import org.xrpl.xrpl4j.model.ledger.LedgerObject; @@ -61,7 +62,7 @@ public void createXrpCheckAndCash() throws JsonRpcClientErrorException { Hash256 invoiceId = Hash256.of(Hashing.sha256().hashBytes("Check this out.".getBytes()).toString()); CheckCreate checkCreate = CheckCreate.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfoResult.accountData().sequence()) .destination(destinationWallet.classicAddress()) .sendMax(XrpCurrencyAmount.ofDrops(12345)) @@ -99,7 +100,7 @@ public void createXrpCheckAndCash() throws JsonRpcClientErrorException { .account(destinationWallet.classicAddress()) .amount(checkObject.sendMax()) .sequence(destinationAccountInfo.accountData().sequence()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .checkId(checkObject.index()) .signingPublicKey(destinationWallet.publicKey()) .build(); @@ -150,7 +151,7 @@ public void createCheckAndSourceCancels() throws JsonRpcClientErrorException { Hash256 invoiceId = Hash256.of(Hashing.sha256().hashBytes("Check this out.".getBytes()).toString()); CheckCreate checkCreate = CheckCreate.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfoResult.accountData().sequence()) .destination(destinationWallet.classicAddress()) .sendMax(XrpCurrencyAmount.ofDrops(12345)) @@ -184,7 +185,7 @@ public void createCheckAndSourceCancels() throws JsonRpcClientErrorException { CheckCancel checkCancel = CheckCancel.builder() .account(sourceWallet.classicAddress()) .sequence(accountInfoResult.accountData().sequence().plus(UnsignedInteger.ONE)) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .checkId(checkObject.index()) .signingPublicKey(sourceWallet.publicKey()) .build(); @@ -224,7 +225,7 @@ public void createCheckAndDestinationCancels() throws JsonRpcClientErrorExceptio Hash256 invoiceId = Hash256.of(Hashing.sha256().hashBytes("Check this out.".getBytes()).toString()); CheckCreate checkCreate = CheckCreate.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfoResult.accountData().sequence()) .destination(destinationWallet.classicAddress()) .sendMax(XrpCurrencyAmount.ofDrops(12345)) @@ -261,7 +262,7 @@ public void createCheckAndDestinationCancels() throws JsonRpcClientErrorExceptio CheckCancel checkCancel = CheckCancel.builder() .account(destinationWallet.classicAddress()) .sequence(destinationAccountInfo.accountData().sequence()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .checkId(checkObject.index()) .signingPublicKey(destinationWallet.publicKey()) .build(); diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/DepositPreAuthIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/DepositPreAuthIT.java index 7497bb05e..4ff074ab2 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/DepositPreAuthIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/DepositPreAuthIT.java @@ -29,6 +29,7 @@ import org.xrpl.xrpl4j.model.client.accounts.AccountObjectsResult; 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.path.DepositAuthorizedRequestParams; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.client.transactions.TransactionResult; @@ -59,7 +60,7 @@ public void preauthorizeAccountAndReceivePayment() throws JsonRpcClientErrorExce // Give Preauthorization for the sender to send a funds to the receiver DepositPreAuth depositPreAuth = DepositPreAuth.builder() .account(receiverWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(receiverAccountInfo.accountData().sequence()) .signingPublicKey(receiverWallet.publicKey()) .authorize(senderWallet.classicAddress()) @@ -100,7 +101,7 @@ public void preauthorizeAccountAndReceivePayment() throws JsonRpcClientErrorExce ); Payment payment = Payment.builder() .account(senderWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(senderAccountInfo.accountData().sequence()) .signingPublicKey(senderWallet.publicKey()) .amount(XrpCurrencyAmount.ofDrops(12345)) @@ -164,7 +165,7 @@ public void accountUnableToReceivePaymentsWithoutPreauthorization() throws JsonR ); Payment payment = Payment.builder() .account(senderWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(senderAccountInfo.accountData().sequence()) .signingPublicKey(senderWallet.publicKey()) .amount(XrpCurrencyAmount.ofDrops(12345)) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/IsFinalIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/IsFinalIT.java index c22b746e1..2e385d199 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/IsFinalIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/IsFinalIT.java @@ -12,6 +12,7 @@ import org.xrpl.xrpl4j.model.client.common.LedgerIndex; import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.ledger.LedgerRequestParams; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.transactions.ImmutablePayment; @@ -52,7 +53,7 @@ void setup() throws JsonRpcClientErrorException { Wallet destinationWallet = createRandomAccount(); payment = Payment.builder() .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence()) .destination(destinationWallet.classicAddress()) .amount(XrpCurrencyAmount.ofDrops(10)) 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 805a28faa..3aaecb74c 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 @@ -32,6 +32,7 @@ import org.xrpl.xrpl4j.model.client.accounts.TrustLine; import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.transactions.AccountSet; import org.xrpl.xrpl4j.model.transactions.IssuedCurrencyAmount; @@ -163,7 +164,7 @@ public void sendSimpleRipplingIssuedCurrencyPayment() throws JsonRpcClientErrorE AccountInfoResult aliceAccountInfo = getValidatedAccountInfo(aliceWallet.classicAddress()); Payment aliceToBobPayment = Payment.builder() .account(aliceWallet.classicAddress()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(aliceAccountInfo.accountData().sequence()) .destination(bobWallet.classicAddress()) .amount(IssuedCurrencyAmount.builder() @@ -307,7 +308,7 @@ public void sendMultiHopSameCurrencyPayment() throws JsonRpcClientErrorException AccountInfoResult charlieAccountInfo = getValidatedAccountInfo(charlieWallet.classicAddress()); Payment charlieToDanielPayment = Payment.builder() .account(charlieWallet.classicAddress()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(charlieAccountInfo.accountData().sequence()) .destination(danielWallet.classicAddress()) .amount(IssuedCurrencyAmount.builder() @@ -371,7 +372,7 @@ public void setDefaultRipple(Wallet issuerWallet, FeeResult feeResult) throws Js AccountSet setDefaultRipple = AccountSet.builder() .account(issuerWallet.classicAddress()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(issuerAccountInfo.accountData().sequence()) .signingPublicKey(issuerWallet.publicKey()) .setFlag(AccountSet.AccountSetFlag.DEFAULT_RIPPLE) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/OfferIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/OfferIT.java index 8367a0b6d..a25d49911 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/OfferIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/OfferIT.java @@ -30,6 +30,7 @@ import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.flags.Flags; import org.xrpl.xrpl4j.model.ledger.OfferObject; @@ -77,7 +78,7 @@ public void ensureUsdIssued() throws JsonRpcClientErrorException { UnsignedInteger sequence = accountInfoResult.accountData().sequence(); OfferCreate offerCreate = OfferCreate.builder() .account(issuerWallet.classicAddress()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(sequence) .signingPublicKey(issuerWallet.publicKey()) .takerGets(IssuedCurrencyAmount.builder() @@ -126,7 +127,7 @@ public void createOpenOfferAndCancel() throws JsonRpcClientErrorException { UnsignedInteger sequence = accountInfoResult.accountData().sequence(); OfferCreate offerCreate = OfferCreate.builder() .account(purchaser.classicAddress()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(sequence) .signingPublicKey(purchaser.publicKey()) .takerPays(IssuedCurrencyAmount.builder() @@ -181,7 +182,7 @@ private void cancelOffer( OfferCancel offerCancel = OfferCancel.builder() .account(purchaser.classicAddress()) - .fee(xrplClient.fee().drops().minimumFee()) + .fee(getComputedNetworkFee(xrplClient.fee())) .sequence(nextSequence) .offerSequence(offerSequence) .signingPublicKey(purchaser.publicKey()) @@ -213,7 +214,7 @@ public void createUnmatchedKillOrFill() throws JsonRpcClientErrorException { UnsignedInteger sequence = accountInfoResult.accountData().sequence(); OfferCreate offerCreate = OfferCreate.builder() .account(purchaser.classicAddress()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(sequence) .signingPublicKey(purchaser.publicKey()) .takerPays(IssuedCurrencyAmount.builder() @@ -278,7 +279,7 @@ public void createFullyMatchedOffer() throws JsonRpcClientErrorException { OfferCreate offerCreate = OfferCreate.builder() .account(purchaser.classicAddress()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(sequence) .signingPublicKey(purchaser.publicKey()) .takerPays(requestCurrencyAmount) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/PaymentChannelIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/PaymentChannelIT.java index 6fee8ed53..c3b7a4ae9 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/PaymentChannelIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/PaymentChannelIT.java @@ -40,6 +40,7 @@ import org.xrpl.xrpl4j.model.client.channels.UnsignedClaim; 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.transactions.SubmitResult; import org.xrpl.xrpl4j.model.jackson.ObjectMapperFactory; import org.xrpl.xrpl4j.model.ledger.PayChannelObject; @@ -76,7 +77,7 @@ public void createPaymentChannel() throws JsonRpcClientErrorException { // the source and destination accounts PaymentChannelCreate createPaymentChannel = PaymentChannelCreate.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(senderAccountInfo.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(10000)) .destination(destinationWallet.classicAddress()) @@ -158,7 +159,7 @@ void createAndClaimPaymentChannel() throws JsonRpcClientErrorException, JsonProc // the source and destination accounts PaymentChannelCreate createPaymentChannel = PaymentChannelCreate.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(senderAccountInfo.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(10000000)) .destination(destinationWallet.classicAddress()) @@ -233,7 +234,7 @@ void createAndClaimPaymentChannel() throws JsonRpcClientErrorException, JsonProc // Destination account submits the signed claim to the ledger to get their XRP PaymentChannelClaim signedClaim = PaymentChannelClaim.builder() .account(destinationWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(destinationAccountInfo.accountData().sequence()) .channel(paymentChannel.channelId()) .balance(paymentChannel.balance().plus(unsignedClaim.amount())) @@ -280,7 +281,7 @@ void createAddFundsAndSetExpirationToPaymentChannel() throws JsonRpcClientErrorE // the source and destination accounts PaymentChannelCreate createPaymentChannel = PaymentChannelCreate.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(senderAccountInfo.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(10000000)) .destination(destinationWallet.classicAddress()) @@ -320,7 +321,7 @@ void createAddFundsAndSetExpirationToPaymentChannel() throws JsonRpcClientErrorE PaymentChannelFund addFunds = PaymentChannelFund.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(senderAccountInfo.accountData().sequence().plus(UnsignedInteger.ONE)) .signingPublicKey(sourceWallet.publicKey()) .channel(paymentChannel.channelId()) @@ -358,7 +359,7 @@ void createAddFundsAndSetExpirationToPaymentChannel() throws JsonRpcClientErrorE PaymentChannelFund setExpiry = PaymentChannelFund.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(senderAccountInfo.accountData().sequence().plus(UnsignedInteger.valueOf(2))) .signingPublicKey(sourceWallet.publicKey()) .channel(paymentChannel.channelId()) @@ -407,7 +408,7 @@ void testCurrentAccountChannels() throws JsonRpcClientErrorException { // the source and destination accounts PaymentChannelCreate createPaymentChannel = PaymentChannelCreate.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(senderAccountInfo.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(10000)) .destination(destinationWallet.classicAddress()) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SetRegularKeyIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SetRegularKeyIT.java index 6c89f907f..25a1fa6a2 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SetRegularKeyIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SetRegularKeyIT.java @@ -27,6 +27,7 @@ import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.transactions.AccountSet; import org.xrpl.xrpl4j.model.transactions.SetRegularKey; @@ -55,7 +56,7 @@ void setRegularKeyOnAccount() throws JsonRpcClientErrorException { FeeResult feeResult = xrplClient.fee(); SetRegularKey setRegularKey = SetRegularKey.builder() .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence()) .regularKey(newWallet.classicAddress()) .signingPublicKey(wallet.publicKey()) @@ -77,7 +78,7 @@ void setRegularKeyOnAccount() throws JsonRpcClientErrorException { () -> { AccountSet accountSet = AccountSet.builder() .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence().plus(UnsignedInteger.ONE)) .signingPublicKey(newWallet.publicKey()) .build(); @@ -112,7 +113,7 @@ void removeRegularKeyFromAccount() throws JsonRpcClientErrorException { FeeResult feeResult = xrplClient.fee(); SetRegularKey setRegularKey = SetRegularKey.builder() .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence()) .regularKey(newWallet.classicAddress()) .signingPublicKey(wallet.publicKey()) @@ -134,7 +135,7 @@ void removeRegularKeyFromAccount() throws JsonRpcClientErrorException { () -> { AccountSet accountSet = AccountSet.builder() .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence().plus(UnsignedInteger.ONE)) .signingPublicKey(newWallet.publicKey()) .build(); @@ -150,7 +151,7 @@ void removeRegularKeyFromAccount() throws JsonRpcClientErrorException { SetRegularKey removeRegularKey = SetRegularKey.builder() .account(wallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence().plus(UnsignedInteger.valueOf(2))) .signingPublicKey(wallet.publicKey()) .build(); diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SignerListSetIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SignerListSetIT.java index 8b9727b7a..f23971d68 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SignerListSetIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SignerListSetIT.java @@ -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. @@ -33,6 +33,7 @@ import org.xrpl.xrpl4j.keypairs.KeyPairService; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitMultiSignedResult; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.jackson.ObjectMapperFactory; @@ -42,7 +43,6 @@ import org.xrpl.xrpl4j.model.transactions.Signer; import org.xrpl.xrpl4j.model.transactions.SignerListSet; import org.xrpl.xrpl4j.model.transactions.SignerWrapper; -import org.xrpl.xrpl4j.model.transactions.Transaction; import org.xrpl.xrpl4j.model.transactions.TransactionResultCodes; import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount; import org.xrpl.xrpl4j.wallet.Wallet; @@ -85,7 +85,7 @@ void addSignersToSignerListAndSendPayment() throws JsonRpcClientErrorException { FeeResult feeResult = xrplClient.fee(); SignerListSet signerListSet = SignerListSet.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(sourceAccountInfo.accountData().sequence()) .signerQuorum(UnsignedInteger.valueOf(2)) .addSignerEntries( @@ -110,7 +110,7 @@ void addSignersToSignerListAndSendPayment() throws JsonRpcClientErrorException { SubmitResult signerListSetResult = xrplClient.submit(sourceWallet, signerListSet); assertThat(signerListSetResult.result()).isEqualTo(TransactionResultCodes.TES_SUCCESS); assertThat(signerListSetResult.transactionResult().transaction().hash()).isNotEmpty().get() - .isEqualTo(signerListSetResult.transactionResult().hash()); + .isEqualTo(signerListSetResult.transactionResult().hash()); logger.info( "SignerListSet transaction successful: https://testnet.xrpl.org/transactions/" + signerListSetResult.transactionResult().hash() @@ -138,10 +138,10 @@ void addSignersToSignerListAndSendPayment() throws JsonRpcClientErrorException { Payment unsignedPayment = Payment.builder() .account(sourceWallet.classicAddress()) .fee( - Transaction.computeMultiSigFee( - feeResult.drops().openLedgerFee(), + FeeUtils.computeMultisigNetworkFees( + feeResult, sourceAccountInfoAfterSignerListSet.accountData().signerLists().get(0) - ) + ).recommendedFee() ) .sequence(sourceAccountInfoAfterSignerListSet.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(12345)) @@ -214,7 +214,7 @@ void addSignersToSignerListThenDeleteSignerList() throws JsonRpcClientErrorExcep FeeResult feeResult = xrplClient.fee(); SignerListSet signerListSet = SignerListSet.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(sourceAccountInfo.accountData().sequence()) .signerQuorum(UnsignedInteger.valueOf(2)) .addSignerEntries( diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SubmitMultisignedIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SubmitMultisignedIT.java index e3a05f496..39110161d 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SubmitMultisignedIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/SubmitMultisignedIT.java @@ -15,6 +15,7 @@ import org.xrpl.xrpl4j.keypairs.KeyPairService; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SignedTransaction; import org.xrpl.xrpl4j.model.client.transactions.SubmitMultiSignedResult; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; @@ -26,7 +27,6 @@ import org.xrpl.xrpl4j.model.transactions.Signer; import org.xrpl.xrpl4j.model.transactions.SignerListSet; import org.xrpl.xrpl4j.model.transactions.SignerWrapper; -import org.xrpl.xrpl4j.model.transactions.Transaction; import org.xrpl.xrpl4j.model.transactions.TransactionResultCodes; import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount; import org.xrpl.xrpl4j.wallet.Wallet; @@ -75,7 +75,7 @@ public void setUp() throws JsonRpcClientErrorException { feeResult = xrplClient.fee(); SignerListSet signerListSet = SignerListSet.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(sourceAccountInfo.accountData().sequence()) .signerQuorum(UnsignedInteger.valueOf(2)) .addSignerEntries( @@ -132,10 +132,10 @@ public void submitMultisignedAndVerifyHash() throws JsonRpcClientErrorException, Payment unsignedPayment = Payment.builder() .account(sourceWallet.classicAddress()) .fee( - Transaction.computeMultiSigFee( - feeResult.drops().openLedgerFee(), + FeeUtils.computeMultisigNetworkFees( + feeResult, sourceAccountInfoAfterSignerListSet.accountData().signerLists().get(0) - ) + ).recommendedFee() ) .sequence(sourceAccountInfoAfterSignerListSet.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(12345)) @@ -201,10 +201,10 @@ public void submitMultisignedWithSignersInDescOrderAndVerifyHash() throws Payment unsignedPayment = Payment.builder() .account(sourceWallet.classicAddress()) .fee( - Transaction.computeMultiSigFee( - feeResult.drops().openLedgerFee(), + FeeUtils.computeMultisigNetworkFees( + feeResult, sourceAccountInfoAfterSignerListSet.accountData().signerLists().get(0) - ) + ).recommendedFee() ) .sequence(sourceAccountInfoAfterSignerListSet.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(12345)) 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 ad712fe9d..66fc80871 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 @@ -27,6 +27,7 @@ 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.LedgerRequestParams; import org.xrpl.xrpl4j.model.client.ledger.LedgerResult; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; @@ -50,7 +51,7 @@ public void sendPayment() throws JsonRpcClientErrorException { XrpCurrencyAmount amount = XrpCurrencyAmount.ofDrops(12345); Payment payment = Payment.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence()) .destination(destinationWallet.classicAddress()) .amount(amount) @@ -93,7 +94,7 @@ public void sendPaymentFromSecp256k1Wallet() throws JsonRpcClientErrorException Payment payment = Payment.builder() .account(senderWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence()) .destination(destinationWallet.classicAddress()) .amount(XrpCurrencyAmount.ofDrops(12345)) 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 f4a89fa18..a31b4d7a3 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 @@ -27,6 +27,7 @@ import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.ledger.TicketObject; import org.xrpl.xrpl4j.model.transactions.AccountSet; @@ -49,7 +50,7 @@ void createTicketAndUseSequenceNumber() throws JsonRpcClientErrorException { TicketCreate ticketCreate = TicketCreate.builder() .account(sourceWallet.classicAddress()) .sequence(accountInfo.accountData().sequence()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .ticketCount(UnsignedInteger.ONE) .signingPublicKey(sourceWallet.publicKey()) .build(); @@ -71,7 +72,7 @@ void createTicketAndUseSequenceNumber() throws JsonRpcClientErrorException { AccountSet accountSet = AccountSet.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .ticketSequence(tickets.get(0).ticketSequence()) .signingPublicKey(sourceWallet.publicKey()) .build(); diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/TransactionWithMemoIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/TransactionWithMemoIT.java index 1575c0615..261e1ba8a 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/TransactionWithMemoIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/TransactionWithMemoIT.java @@ -26,6 +26,7 @@ import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.client.transactions.TransactionResult; import org.xrpl.xrpl4j.model.transactions.Memo; @@ -51,7 +52,7 @@ public void transactionWithMemoNibble() throws JsonRpcClientErrorException { XrpCurrencyAmount amount = XrpCurrencyAmount.ofDrops(12345); Payment payment = Payment.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence()) .destination(destinationWallet.classicAddress()) .amount(amount) @@ -96,7 +97,7 @@ public void transactionWithPlaintextMemo() throws JsonRpcClientErrorException { XrpCurrencyAmount amount = XrpCurrencyAmount.ofDrops(12345); Payment payment = Payment.builder() .account(sourceWallet.classicAddress()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence()) .destination(destinationWallet.classicAddress()) .amount(amount) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/AccountSetIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/AccountSetIT.java index 3232bab1a..bf5017998 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/AccountSetIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/AccountSetIT.java @@ -10,6 +10,7 @@ import org.xrpl.xrpl4j.crypto.core.wallet.Wallet; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.flags.Flags.AccountRootFlags; import org.xrpl.xrpl4j.model.transactions.AccountSet; @@ -43,7 +44,7 @@ public void enableAllAndDisableOne() throws JsonRpcClientErrorException, JsonPro FeeResult feeResult = xrplClient.fee(); AccountSet accountSet = AccountSet.builder() .account(wallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence()) .setFlag(AccountSetFlag.ACCOUNT_TXN_ID) .signingPublicKey(wallet.publicKey().base16Value()) @@ -107,7 +108,7 @@ public void disableAndEnableAllFlags() throws JsonRpcClientErrorException, JsonP FeeResult feeResult = xrplClient.fee(); AccountSet accountSet = AccountSet.builder() .account(wallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence()) .setFlag(AccountSetFlag.ACCOUNT_TXN_ID) .signingPublicKey(wallet.publicKey().base16Value()) @@ -169,7 +170,7 @@ private void assertSetFlag( FeeResult feeResult = xrplClient.fee(); AccountSet accountSet = AccountSet.builder() .account(wallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(sequence) .setFlag(accountSetFlag) .signingPublicKey(wallet.publicKey().base16Value()) @@ -209,7 +210,7 @@ private void assertClearFlag( FeeResult feeResult = xrplClient.fee(); AccountSet accountSet = AccountSet.builder() .account(wallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(sequence) .clearFlag(accountSetFlag) .signingPublicKey(wallet.publicKey().base16Value()) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/CheckIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/CheckIT.java index 1daf68769..97d36502b 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/CheckIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/CheckIT.java @@ -11,6 +11,7 @@ import org.xrpl.xrpl4j.crypto.core.wallet.Wallet; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.ledger.CheckObject; import org.xrpl.xrpl4j.model.ledger.LedgerObject; @@ -46,7 +47,7 @@ public void createXrpCheckAndCash() throws JsonRpcClientErrorException, JsonProc Hash256 invoiceId = Hash256.of(Hashing.sha256().hashBytes("Check this out.".getBytes()).toString()); CheckCreate checkCreate = CheckCreate.builder() .account(sourceWallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfoResult.accountData().sequence()) .destination(destinationWallet.address()) .sendMax(XrpCurrencyAmount.ofDrops(12345)) @@ -86,7 +87,7 @@ public void createXrpCheckAndCash() throws JsonRpcClientErrorException, JsonProc .account(destinationWallet.address()) .amount(checkObject.sendMax()) .sequence(destinationAccountInfo.accountData().sequence()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .checkId(checkObject.index()) .signingPublicKey(destinationWallet.publicKey().base16Value()) .build(); @@ -138,7 +139,7 @@ public void createCheckAndSourceCancels() throws JsonRpcClientErrorException, Js Hash256 invoiceId = Hash256.of(Hashing.sha256().hashBytes("Check this out.".getBytes()).toString()); CheckCreate checkCreate = CheckCreate.builder() .account(sourceWallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfoResult.accountData().sequence()) .destination(destinationWallet.address()) .sendMax(XrpCurrencyAmount.ofDrops(12345)) @@ -173,7 +174,7 @@ public void createCheckAndSourceCancels() throws JsonRpcClientErrorException, Js CheckCancel checkCancel = CheckCancel.builder() .account(sourceWallet.address()) .sequence(accountInfoResult.accountData().sequence().plus(UnsignedInteger.ONE)) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .checkId(checkObject.index()) .signingPublicKey(sourceWallet.publicKey().base16Value()) .build(); @@ -214,7 +215,7 @@ public void createCheckAndDestinationCancels() throws JsonRpcClientErrorExceptio Hash256 invoiceId = Hash256.of(Hashing.sha256().hashBytes("Check this out.".getBytes()).toString()); CheckCreate checkCreate = CheckCreate.builder() .account(sourceWallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfoResult.accountData().sequence()) .destination(destinationWallet.address()) .sendMax(XrpCurrencyAmount.ofDrops(12345)) @@ -253,7 +254,7 @@ public void createCheckAndDestinationCancels() throws JsonRpcClientErrorExceptio CheckCancel checkCancel = CheckCancel.builder() .account(destinationWallet.address()) .sequence(destinationAccountInfo.accountData().sequence()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .checkId(checkObject.index()) .signingPublicKey(destinationWallet.publicKey().base16Value()) .build(); diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/DepositPreAuthIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/DepositPreAuthIT.java index cdd0a64d0..1876a3edf 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/DepositPreAuthIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/DepositPreAuthIT.java @@ -12,6 +12,7 @@ import org.xrpl.xrpl4j.model.client.accounts.AccountObjectsResult; 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.path.DepositAuthorizedRequestParams; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.client.transactions.TransactionResult; @@ -37,13 +38,14 @@ public void preauthorizeAccountAndReceivePayment() throws JsonRpcClientErrorExce ///////////////////////// // Enable Deposit Preauthorization on the receiver account FeeResult feeResult = xrplClient.fee(); - AccountInfoResult receiverAccountInfo = enableDepositPreauth(receiverWallet, feeResult.drops().openLedgerFee()); + AccountInfoResult receiverAccountInfo = + enableDepositPreauth(receiverWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee()); ///////////////////////// // Give Preauthorization for the sender to send a funds to the receiver DepositPreAuth depositPreAuth = DepositPreAuth.builder() .account(receiverWallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(receiverAccountInfo.accountData().sequence()) .signingPublicKey(receiverWallet.publicKey().base16Value()) .authorize(senderWallet.address()) @@ -88,7 +90,7 @@ public void preauthorizeAccountAndReceivePayment() throws JsonRpcClientErrorExce ); Payment payment = Payment.builder() .account(senderWallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(senderAccountInfo.accountData().sequence()) .signingPublicKey(senderWallet.publicKey().base16Value()) .amount(XrpCurrencyAmount.ofDrops(12345)) @@ -135,7 +137,7 @@ public void accountUnableToReceivePaymentsWithoutPreauthorization() ///////////////////////// // Enable Deposit Preauthorization on the receiver account FeeResult feeResult = xrplClient.fee(); - enableDepositPreauth(receiverWallet, feeResult.drops().openLedgerFee()); + enableDepositPreauth(receiverWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee()); ///////////////////////// // Validate that the receiver has not given authorization to anyone to send them Payments @@ -154,7 +156,7 @@ public void accountUnableToReceivePaymentsWithoutPreauthorization() ); Payment payment = Payment.builder() .account(senderWallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(senderAccountInfo.accountData().sequence()) .signingPublicKey(senderWallet.publicKey().base16Value()) .amount(XrpCurrencyAmount.ofDrops(12345)) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/FreezeIssuedCurrencyIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/FreezeIssuedCurrencyIT.java index 7a7bb05c8..02d5b6446 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/FreezeIssuedCurrencyIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/FreezeIssuedCurrencyIT.java @@ -13,6 +13,7 @@ import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.accounts.TrustLine; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.flags.Flags.TrustSetFlags; import org.xrpl.xrpl4j.model.flags.Flags.TrustSetFlags.Builder; @@ -67,7 +68,7 @@ public void issueAndFreezeFundsIndividual() throws JsonRpcClientErrorException, TrustLine badActorTrustLine = this.createTrustLine( issuerWallet, badActorWallet, - feeResult.drops().minimumFee() + FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); assertThat(badActorTrustLine.freeze()).isFalse(); assertThat(badActorTrustLine.freezePeer()).isFalse(); @@ -79,7 +80,7 @@ public void issueAndFreezeFundsIndividual() throws JsonRpcClientErrorException, TrustLine goodActorTrustLine = this.createTrustLine( issuerWallet, goodActorWallet, - feeResult.drops().minimumFee() + FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); assertThat(goodActorTrustLine.freeze()).isFalse(); assertThat(goodActorTrustLine.freezePeer()).isFalse(); @@ -92,7 +93,7 @@ public void issueAndFreezeFundsIndividual() throws JsonRpcClientErrorException, // Send funds from issuer to the badActor. sendFunds( - TEN_THOUSAND, issuerWallet, badActorWallet, feeResult.drops().minimumFee() + TEN_THOUSAND, issuerWallet, badActorWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); /////////////////////////// @@ -105,7 +106,7 @@ public void issueAndFreezeFundsIndividual() throws JsonRpcClientErrorException, // Send funds from badActor to the goodActor. sendFunds( - FIVE_THOUSAND, badActorWallet, goodActorWallet, feeResult.drops().minimumFee() + FIVE_THOUSAND, badActorWallet, goodActorWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); /////////////////////////// @@ -120,7 +121,7 @@ public void issueAndFreezeFundsIndividual() throws JsonRpcClientErrorException, badActorTrustLine = this.adjustTrustlineFreeze( issuerWallet, badActorWallet, - feeResult.drops().minimumFee(), + FeeUtils.computeNetworkFees(feeResult).recommendedFee(), FREEZE ); assertThat(badActorTrustLine.freeze()).isTrue(); @@ -138,25 +139,25 @@ public void issueAndFreezeFundsIndividual() throws JsonRpcClientErrorException, // Try to send funds from badActor to goodActor should not work because the badActor is frozen. sendFunds( - FIVE_THOUSAND, badActorWallet, goodActorWallet, feeResult.drops().minimumFee(), + FIVE_THOUSAND, badActorWallet, goodActorWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee(), "tecPATH_DRY" ); // Sending from the badActor to the issuer should still work sendFunds( - FIVE_THOUSAND, badActorWallet, issuerWallet, feeResult.drops().minimumFee() + FIVE_THOUSAND, badActorWallet, issuerWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); // Sending from the goodActor to the badActor should still work sendFunds( - FIVE_THOUSAND, goodActorWallet, badActorWallet, feeResult.drops().minimumFee() + FIVE_THOUSAND, goodActorWallet, badActorWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); // Unfreeze the bad actor. badActorTrustLine = this.adjustTrustlineFreeze( issuerWallet, badActorWallet, - feeResult.drops().minimumFee(), + FeeUtils.computeNetworkFees(feeResult).recommendedFee(), UN_FREEZE ); assertThat(badActorTrustLine.freeze()).isTrue(); @@ -180,7 +181,7 @@ public void issueAndFreezeFundsGlobal() throws JsonRpcClientErrorException, Json TrustLine badActorTrustLine = this.createTrustLine( issuerWallet, badActorWallet, - feeResult.drops().minimumFee() + FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); assertThat(badActorTrustLine.freeze()).isFalse(); assertThat(badActorTrustLine.freezePeer()).isFalse(); @@ -192,7 +193,7 @@ public void issueAndFreezeFundsGlobal() throws JsonRpcClientErrorException, Json TrustLine goodActorTrustLine = this.createTrustLine( issuerWallet, goodActorWallet, - feeResult.drops().minimumFee() + FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); assertThat(goodActorTrustLine.freeze()).isFalse(); assertThat(goodActorTrustLine.freezePeer()).isFalse(); @@ -205,7 +206,7 @@ public void issueAndFreezeFundsGlobal() throws JsonRpcClientErrorException, Json // Send funds from issuer to the badActor. sendFunds( - TEN_THOUSAND, issuerWallet, badActorWallet, feeResult.drops().minimumFee() + TEN_THOUSAND, issuerWallet, badActorWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); /////////////////////////// @@ -218,7 +219,7 @@ public void issueAndFreezeFundsGlobal() throws JsonRpcClientErrorException, Json // Send funds from badActor to the goodActor. sendFunds( - FIVE_THOUSAND, badActorWallet, goodActorWallet, feeResult.drops().minimumFee() + FIVE_THOUSAND, badActorWallet, goodActorWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); /////////////////////////// @@ -232,7 +233,7 @@ public void issueAndFreezeFundsGlobal() throws JsonRpcClientErrorException, Json // Global-Freeze the trustline for the issuer. AccountInfoResult issuerAccountInfo = this.adjustGlobalTrustlineFreeze( issuerWallet, - feeResult.drops().minimumFee(), + FeeUtils.computeNetworkFees(feeResult).recommendedFee(), FREEZE ); assertThat(issuerAccountInfo.accountData().flags().lsfGlobalFreeze()).isTrue(); @@ -246,43 +247,43 @@ public void issueAndFreezeFundsGlobal() throws JsonRpcClientErrorException, Json // Try to send funds from badActor to goodActor should not work because the badActor is frozen. sendFunds( - "500", badActorWallet, goodActorWallet, feeResult.drops().minimumFee(), + "500", badActorWallet, goodActorWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee(), "tecPATH_DRY" ); // Sending from the goodActor to the badActor should not work sendFunds( - "500", goodActorWallet, badActorWallet, feeResult.drops().minimumFee(), + "500", goodActorWallet, badActorWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee(), "tecPATH_DRY" ); // Try to send funds from issuer to goodActor should work per // https://xrpl.org/enact-global-freeze.html#intermission-while-frozen sendFunds( - "100", issuerWallet, goodActorWallet, feeResult.drops().minimumFee() + "100", issuerWallet, goodActorWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); // Try to send funds from issuer to badActor should work per // https://xrpl.org/enact-global-freeze.html#intermission-while-frozen sendFunds( - "100", issuerWallet, badActorWallet, feeResult.drops().minimumFee() + "100", issuerWallet, badActorWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); // Try to send funds from issuer to goodActor should work per // https://xrpl.org/enact-global-freeze.html#intermission-while-frozen sendFunds( - FIVE_THOUSAND, badActorWallet, issuerWallet, feeResult.drops().minimumFee() + FIVE_THOUSAND, badActorWallet, issuerWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); // Try to send funds from issuer to goodActor should work per // https://xrpl.org/enact-global-freeze.html#intermission-while-frozen sendFunds( - FIVE_THOUSAND, goodActorWallet, issuerWallet, feeResult.drops().minimumFee() + FIVE_THOUSAND, goodActorWallet, issuerWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); // Unfreeze the bad actor. issuerAccountInfo = this.adjustGlobalTrustlineFreeze( issuerWallet, - feeResult.drops().minimumFee(), + FeeUtils.computeNetworkFees(feeResult).recommendedFee(), UN_FREEZE ); assertThat(issuerAccountInfo.accountData().flags().lsfGlobalFreeze()).isFalse(); @@ -427,7 +428,7 @@ protected void enableDefaultRipple(final Wallet wallet) AccountSet accountSet = AccountSet.builder() .sequence(issuerWalletAccountInfo.accountData().sequence()) .account(wallet.address()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .setFlag(AccountSetFlag.DEFAULT_RIPPLE) .signingPublicKey(wallet.publicKey().base16Value()) .build(); diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/IssuedCurrencyIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/IssuedCurrencyIT.java index c993a47d5..d190abbaa 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/IssuedCurrencyIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/IssuedCurrencyIT.java @@ -15,6 +15,7 @@ import org.xrpl.xrpl4j.model.client.accounts.TrustLine; import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.transactions.AccountSet; import org.xrpl.xrpl4j.model.transactions.IssuedCurrencyAmount; @@ -51,7 +52,7 @@ void createTrustlineWithMaxLimit() throws JsonRpcClientErrorException, JsonProce IssuedCurrencyAmount.MAX_VALUE, issuerWallet, counterpartyWallet, - feeResult.drops().minimumFee() + FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); assertThat(trustLine.limitPeer()).isEqualTo("9999999999999999e80"); @@ -76,7 +77,7 @@ void createTrustlineWithMaxLimitMinusOneExponent() throws JsonRpcClientErrorExce new BigDecimal(IssuedCurrencyAmount.MAX_VALUE).scaleByPowerOfTen(-1).toEngineeringString(), issuerWallet, counterpartyWallet, - feeResult.drops().minimumFee() + FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); assertThat(trustLine.limitPeer()).isEqualTo("9999999999999999e79"); @@ -101,7 +102,7 @@ void createTrustlineWithSmallestPositiveLimit() throws JsonRpcClientErrorExcepti IssuedCurrencyAmount.MIN_POSITIVE_VALUE, issuerWallet, counterpartyWallet, - feeResult.drops().minimumFee() + FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); assertThat(trustLine.limitPeer()).isEqualTo("1000000000000000e-96"); @@ -128,7 +129,7 @@ void createTrustlineWithSmalletPositiveLimitPlusOne() throws JsonRpcClientErrorE limitValue.toString(), issuerWallet, counterpartyWallet, - feeResult.drops().minimumFee() + FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); assertThat(trustLine.limitPeer()).isEqualTo("1100000000000000e-96"); @@ -153,12 +154,15 @@ public void issueIssuedCurrencyBalance() throws JsonRpcClientErrorException, Jso "10000", issuerWallet, counterpartyWallet, - feeResult.drops().minimumFee() + FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); /////////////////////////// // Send some xrpl4jCoin to the counterparty account. - issueBalance(xrpl4jCoin, trustLine.limitPeer(), issuerWallet, counterpartyWallet, feeResult.drops().minimumFee()); + issueBalance( + xrpl4jCoin, trustLine.limitPeer(), issuerWallet, counterpartyWallet, + FeeUtils.computeNetworkFees(feeResult).recommendedFee() + ); /////////////////////////// // Validate that the TrustLine balance was updated as a result of the Payment. @@ -203,7 +207,7 @@ public void sendSimpleRipplingIssuedCurrencyPayment() throws JsonRpcClientErrorE "10000", issuerWallet, aliceWallet, - feeResult.drops().minimumFee() + FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); /////////////////////////// @@ -213,16 +217,16 @@ public void sendSimpleRipplingIssuedCurrencyPayment() throws JsonRpcClientErrorE "10000", issuerWallet, bobWallet, - feeResult.drops().minimumFee() + FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); /////////////////////////// // Issuer issues 50 USD to alice - issueBalance("USD", "50", issuerWallet, aliceWallet, feeResult.drops().minimumFee()); + issueBalance("USD", "50", issuerWallet, aliceWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee()); /////////////////////////// // Issuer issues 50 USD to bob - issueBalance("USD", "50", issuerWallet, bobWallet, feeResult.drops().minimumFee()); + issueBalance("USD", "50", issuerWallet, bobWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee()); /////////////////////////// // Try to find a path for this Payment. @@ -245,7 +249,7 @@ public void sendSimpleRipplingIssuedCurrencyPayment() throws JsonRpcClientErrorE AccountInfoResult aliceAccountInfo = getValidatedAccountInfo(aliceWallet.address()); Payment aliceToBobPayment = Payment.builder() .account(aliceWallet.address()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(aliceAccountInfo.accountData().sequence()) .destination(bobWallet.address()) .amount(IssuedCurrencyAmount.builder() @@ -316,7 +320,7 @@ public void sendMultiHopSameCurrencyPayment() throws JsonRpcClientErrorException "10000", issuerAWallet, charlieWallet, - feeResult.drops().minimumFee() + FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); /////////////////////////// @@ -326,7 +330,7 @@ public void sendMultiHopSameCurrencyPayment() throws JsonRpcClientErrorException "10000", issuerAWallet, emilyWallet, - feeResult.drops().minimumFee() + FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); /////////////////////////// @@ -336,7 +340,7 @@ public void sendMultiHopSameCurrencyPayment() throws JsonRpcClientErrorException "10000", issuerBWallet, emilyWallet, - feeResult.drops().minimumFee() + FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); /////////////////////////// @@ -346,28 +350,28 @@ public void sendMultiHopSameCurrencyPayment() throws JsonRpcClientErrorException "10000", issuerBWallet, danielWallet, - feeResult.drops().minimumFee() + FeeUtils.computeNetworkFees(feeResult).recommendedFee() ); /////////////////////////// // Issue 10 USD from issuerA to charlie. // IssuerA now owes Charlie 10 USD. - issueBalance("USD", "10", issuerAWallet, charlieWallet, feeResult.drops().minimumFee()); + issueBalance("USD", "10", issuerAWallet, charlieWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee()); /////////////////////////// // Issue 1 USD from issuerA to emily. // IssuerA now owes Emily 1 USD - issueBalance("USD", "1", issuerAWallet, emilyWallet, feeResult.drops().minimumFee()); + issueBalance("USD", "1", issuerAWallet, emilyWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee()); /////////////////////////// // Issue 100 USD from issuerB to emily. // IssuerB now owes Emily 100 USD - issueBalance("USD", "100", issuerBWallet, emilyWallet, feeResult.drops().minimumFee()); + issueBalance("USD", "100", issuerBWallet, emilyWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee()); /////////////////////////// // Issue 2 USD from issuerB to daniel. // IssuerB now owes Daniel 2 USD - issueBalance("USD", "2", issuerBWallet, danielWallet, feeResult.drops().minimumFee()); + issueBalance("USD", "2", issuerBWallet, danielWallet, FeeUtils.computeNetworkFees(feeResult).recommendedFee()); /////////////////////////// // Look for a payment path from charlie to daniel. @@ -398,7 +402,7 @@ public void sendMultiHopSameCurrencyPayment() throws JsonRpcClientErrorException AccountInfoResult charlieAccountInfo = getValidatedAccountInfo(charlieWallet.address()); Payment charlieToDanielPayment = Payment.builder() .account(charlieWallet.address()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(charlieAccountInfo.accountData().sequence()) .destination(danielWallet.address()) .amount(IssuedCurrencyAmount.builder() @@ -464,7 +468,7 @@ public void setDefaultRipple(Wallet issuerWallet, FeeResult feeResult) AccountSet setDefaultRipple = AccountSet.builder() .account(issuerWallet.address()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(issuerAccountInfo.accountData().sequence()) .signingPublicKey(issuerWallet.publicKey().base16Value()) .setFlag(AccountSet.AccountSetFlag.DEFAULT_RIPPLE) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/OfferIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/OfferIT.java index 13994a6ef..fdad3d33a 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/OfferIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/OfferIT.java @@ -14,6 +14,7 @@ import org.xrpl.xrpl4j.crypto.core.wallet.Wallet; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.flags.Flags; import org.xrpl.xrpl4j.model.ledger.OfferObject; @@ -61,7 +62,7 @@ public void ensureUsdIssued() throws JsonRpcClientErrorException, JsonProcessing UnsignedInteger sequence = accountInfoResult.accountData().sequence(); OfferCreate offerCreate = OfferCreate.builder() .account(issuerWallet.address()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(sequence) .signingPublicKey(issuerWallet.publicKey().base16Value()) .takerGets(IssuedCurrencyAmount.builder() @@ -111,7 +112,7 @@ public void createOpenOfferAndCancel() throws JsonRpcClientErrorException, JsonP UnsignedInteger sequence = accountInfoResult.accountData().sequence(); OfferCreate offerCreate = OfferCreate.builder() .account(purchaser.address()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(sequence) .signingPublicKey(purchaser.publicKey().base16Value()) .takerPays(IssuedCurrencyAmount.builder() @@ -165,9 +166,11 @@ private void cancelOffer( AccountInfoResult infoResult = this.scanForResult(() -> this.getValidatedAccountInfo(purchaser.address())); UnsignedInteger nextSequence = infoResult.accountData().sequence(); + FeeResult feeResult = xrplClient.fee(); + OfferCancel offerCancel = OfferCancel.builder() .account(purchaser.address()) - .fee(xrplClient.fee().drops().minimumFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(nextSequence) .offerSequence(offerSequence) .signingPublicKey(purchaser.publicKey().base16Value()) @@ -202,7 +205,7 @@ public void createUnmatchedKillOrFill() throws JsonRpcClientErrorException, Json UnsignedInteger sequence = accountInfoResult.accountData().sequence(); OfferCreate offerCreate = OfferCreate.builder() .account(purchaser.address()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(sequence) .signingPublicKey(purchaser.publicKey().base16Value()) .takerPays(IssuedCurrencyAmount.builder() @@ -270,7 +273,7 @@ public void createFullyMatchedOffer() throws JsonRpcClientErrorException, JsonPr OfferCreate offerCreate = OfferCreate.builder() .account(purchaser.address()) - .fee(feeResult.drops().minimumFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(sequence) .signingPublicKey(purchaser.publicKey().base16Value()) .takerPays(requestCurrencyAmount) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/PaymentChannelIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/PaymentChannelIT.java index 8529b1928..64eba6b03 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/PaymentChannelIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/PaymentChannelIT.java @@ -19,6 +19,7 @@ import org.xrpl.xrpl4j.model.client.channels.UnsignedClaim; 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.transactions.SubmitResult; import org.xrpl.xrpl4j.model.ledger.PayChannelObject; import org.xrpl.xrpl4j.model.transactions.PaymentChannelClaim; @@ -52,7 +53,7 @@ public void createPaymentChannel() throws JsonRpcClientErrorException, JsonProce // the source and destination accounts PaymentChannelCreate paymentChannelCreate = PaymentChannelCreate.builder() .account(sourceWallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(senderAccountInfo.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(10000)) .destination(destinationWallet.address()) @@ -138,7 +139,7 @@ void createAndClaimPaymentChannel() throws JsonRpcClientErrorException, JsonProc // the source and destination accounts PaymentChannelCreate paymentChannelCreate = PaymentChannelCreate.builder() .account(sourceWallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(senderAccountInfo.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(10000000)) .destination(destinationWallet.address()) @@ -207,7 +208,7 @@ void createAndClaimPaymentChannel() throws JsonRpcClientErrorException, JsonProc // Destination account submits the signed claim to the ledger to get their XRP PaymentChannelClaim paymentChannelClaim = PaymentChannelClaim.builder() .account(destinationWallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(destinationAccountInfo.accountData().sequence()) .channel(paymentChannel.channelId()) .balance(paymentChannel.balance().plus(unsignedClaim.amount())) @@ -256,7 +257,7 @@ void createAddFundsAndSetExpirationToPaymentChannel() throws JsonRpcClientErrorE // the source and destination accounts PaymentChannelCreate paymentChannelCreate = PaymentChannelCreate.builder() .account(sourceWallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(senderAccountInfo.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(10000000)) .destination(destinationWallet.address()) @@ -298,7 +299,7 @@ void createAddFundsAndSetExpirationToPaymentChannel() throws JsonRpcClientErrorE PaymentChannelFund paymentChannelFund = PaymentChannelFund.builder() .account(sourceWallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(senderAccountInfo.accountData().sequence().plus(UnsignedInteger.ONE)) .signingPublicKey(sourceWallet.publicKey().base16Value()) .channel(paymentChannel.channelId()) @@ -338,7 +339,7 @@ void createAddFundsAndSetExpirationToPaymentChannel() throws JsonRpcClientErrorE PaymentChannelFund paymentChannelFundWithNewExpiry = PaymentChannelFund.builder() .account(sourceWallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(senderAccountInfo.accountData().sequence().plus(UnsignedInteger.valueOf(2))) .signingPublicKey(sourceWallet.publicKey().base16Value()) .channel(paymentChannel.channelId()) @@ -389,7 +390,7 @@ void testCurrentAccountChannels() throws JsonRpcClientErrorException, JsonProces // the source and destination accounts PaymentChannelCreate createPaymentChannel = PaymentChannelCreate.builder() .account(sourceWallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(senderAccountInfo.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(10000)) .destination(destinationWallet.address()) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/SetRegularKeyIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/SetRegularKeyIT.java index d73d50931..4a37ddd64 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/SetRegularKeyIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/SetRegularKeyIT.java @@ -10,6 +10,7 @@ import org.xrpl.xrpl4j.crypto.core.wallet.Wallet; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.transactions.AccountSet; import org.xrpl.xrpl4j.model.transactions.SetRegularKey; @@ -39,7 +40,7 @@ void setRegularKeyOnAccount() throws JsonRpcClientErrorException, JsonProcessing FeeResult feeResult = xrplClient.fee(); SetRegularKey setRegularKey = SetRegularKey.builder() .account(wallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence()) .regularKey(newWallet.address()) .signingPublicKey(wallet.publicKey().base16Value()) @@ -63,7 +64,7 @@ void setRegularKeyOnAccount() throws JsonRpcClientErrorException, JsonProcessing () -> { AccountSet accountSet = AccountSet.builder() .account(wallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence().plus(UnsignedInteger.ONE)) .signingPublicKey(newWallet.publicKey().base16Value()) .build(); @@ -100,7 +101,7 @@ void removeRegularKeyFromAccount() throws JsonRpcClientErrorException, JsonProce FeeResult feeResult = xrplClient.fee(); SetRegularKey setRegularKey = SetRegularKey.builder() .account(wallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence()) .regularKey(newWallet.address()) .signingPublicKey(wallet.publicKey().base16Value()) @@ -124,7 +125,7 @@ void removeRegularKeyFromAccount() throws JsonRpcClientErrorException, JsonProce () -> { AccountSet accountSet = AccountSet.builder() .account(wallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence().plus(UnsignedInteger.ONE)) .signingPublicKey(newWallet.publicKey().base16Value()) .build(); @@ -142,7 +143,7 @@ void removeRegularKeyFromAccount() throws JsonRpcClientErrorException, JsonProce SetRegularKey removeRegularKey = SetRegularKey.builder() .account(wallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence().plus(UnsignedInteger.valueOf(2))) .signingPublicKey(wallet.publicKey().base16Value()) .build(); diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/SignerListSetIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/SignerListSetIT.java index 6700ae283..e066eb51f 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/SignerListSetIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/SignerListSetIT.java @@ -12,6 +12,7 @@ import org.xrpl.xrpl4j.crypto.core.wallet.Wallet; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitMultiSignedResult; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.ledger.SignerEntry; @@ -20,7 +21,6 @@ import org.xrpl.xrpl4j.model.transactions.Signer; import org.xrpl.xrpl4j.model.transactions.SignerListSet; import org.xrpl.xrpl4j.model.transactions.SignerWrapper; -import org.xrpl.xrpl4j.model.transactions.Transaction; import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount; import java.util.Comparator; @@ -60,7 +60,7 @@ void addSignersToSignerListAndSendPayment() throws JsonRpcClientErrorException, FeeResult feeResult = xrplClient.fee(); SignerListSet signerListSet = SignerListSet.builder() .account(sourceWallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(sourceAccountInfo.accountData().sequence()) .signerQuorum(UnsignedInteger.valueOf(2)) .addSignerEntries( @@ -112,10 +112,10 @@ void addSignersToSignerListAndSendPayment() throws JsonRpcClientErrorException, Payment unsignedPayment = Payment.builder() .account(sourceWallet.address()) .fee( - Transaction.computeMultiSigFee( - feeResult.drops().openLedgerFee(), + FeeUtils.computeMultisigNetworkFees( + feeResult, sourceAccountInfoAfterSignerListSet.accountData().signerLists().get(0) - ) + ).recommendedFee() ) .sequence(sourceAccountInfoAfterSignerListSet.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(12345)) @@ -178,7 +178,7 @@ void addSignersToSignerListThenDeleteSignerList() throws JsonRpcClientErrorExcep FeeResult feeResult = xrplClient.fee(); SignerListSet signerListSet = SignerListSet.builder() .account(sourceWallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(sourceAccountInfo.accountData().sequence()) .signerQuorum(UnsignedInteger.valueOf(2)) .addSignerEntries( diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/SubmitPaymentIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/SubmitPaymentIT.java index 9c4a0d472..13d550a42 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/SubmitPaymentIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/SubmitPaymentIT.java @@ -12,6 +12,7 @@ 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.LedgerRequestParams; import org.xrpl.xrpl4j.model.client.ledger.LedgerResult; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; @@ -40,7 +41,7 @@ public void sendPayment() throws JsonRpcClientErrorException, JsonProcessingExce XrpCurrencyAmount amount = XrpCurrencyAmount.ofDrops(12345); Payment payment = Payment.builder() .account(sourceWallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence()) .destination(destinationWallet.address()) .amount(amount) @@ -80,7 +81,7 @@ public void sendPaymentFromSecp256k1Wallet() throws JsonRpcClientErrorException, Payment payment = Payment.builder() .account(senderWallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence()) .destination(destinationWallet.address()) .amount(XrpCurrencyAmount.ofDrops(12345)) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/TransactUsingDelegatedSignatureService.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/TransactUsingDelegatedSignatureService.java index 10f4eea3e..547a5859d 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/TransactUsingDelegatedSignatureService.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/TransactUsingDelegatedSignatureService.java @@ -15,6 +15,7 @@ import org.xrpl.xrpl4j.crypto.core.signing.SingleSingedTransaction; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitMultiSignedResult; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.ledger.SignerEntry; @@ -24,7 +25,6 @@ import org.xrpl.xrpl4j.model.transactions.Signer; import org.xrpl.xrpl4j.model.transactions.SignerListSet; import org.xrpl.xrpl4j.model.transactions.SignerWrapper; -import org.xrpl.xrpl4j.model.transactions.Transaction; import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount; import java.util.Comparator; @@ -54,7 +54,7 @@ public void sendPaymentFromEd25519Account() throws JsonRpcClientErrorException, AccountInfoResult accountInfo = this.scanForResult(() -> this.getValidatedAccountInfo(sourceWalletAddress)); Payment payment = Payment.builder() .account(sourceWalletAddress) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence()) .destination(destinationWalletAddress) .amount(XrpCurrencyAmount.ofDrops(12345)) @@ -88,7 +88,7 @@ public void sendPaymentFromSecp256k1Account() throws JsonRpcClientErrorException .scanForResult(() -> this.getValidatedAccountInfo(sourceWalletAddress)); Payment payment = Payment.builder() .account(sourceWalletAddress) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence()) .destination(destinationWalletAddress) .amount(XrpCurrencyAmount.ofDrops(12345)) @@ -169,7 +169,7 @@ private void multiSigSendPaymentHelper(final DelegatedSignatureService delegated FeeResult feeResult = xrplClient.fee(); SignerListSet signerListSet = SignerListSet.builder() .account(sourceAddress) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(sourceAccountInfo.accountData().sequence()) .signerQuorum(UnsignedInteger.valueOf(2)) .addSignerEntries( @@ -221,10 +221,10 @@ private void multiSigSendPaymentHelper(final DelegatedSignatureService delegated Payment unsignedPayment = Payment.builder() .account(sourceAddress) .fee( - Transaction.computeMultiSigFee( - feeResult.drops().openLedgerFee(), + FeeUtils.computeMultisigNetworkFees( + feeResult, sourceAccountInfoAfterSignerListSet.accountData().signerLists().get(0) - ) + ).recommendedFee() ) .sequence(sourceAccountInfoAfterSignerListSet.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(12345)) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/TransactUsingSignatureService.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/TransactUsingSignatureService.java index bbc7e9088..461e01aac 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/TransactUsingSignatureService.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/TransactUsingSignatureService.java @@ -18,6 +18,7 @@ import org.xrpl.xrpl4j.crypto.core.wallet.Wallet; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoResult; import org.xrpl.xrpl4j.model.client.fees.FeeResult; +import org.xrpl.xrpl4j.model.client.fees.FeeUtils; import org.xrpl.xrpl4j.model.client.transactions.SubmitMultiSignedResult; import org.xrpl.xrpl4j.model.client.transactions.SubmitResult; import org.xrpl.xrpl4j.model.ledger.SignerEntry; @@ -27,7 +28,6 @@ import org.xrpl.xrpl4j.model.transactions.Signer; import org.xrpl.xrpl4j.model.transactions.SignerListSet; import org.xrpl.xrpl4j.model.transactions.SignerWrapper; -import org.xrpl.xrpl4j.model.transactions.Transaction; import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount; import java.util.Comparator; @@ -64,7 +64,7 @@ public void sendPaymentFromEd25519Wallet() throws JsonRpcClientErrorException, J AccountInfoResult accountInfo = this.scanForResult(() -> this.getValidatedAccountInfo(sourceWalletAddress)); Payment payment = Payment.builder() .account(sourceWalletAddress) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence()) .destination(destinationWalletAddress) .amount(XrpCurrencyAmount.ofDrops(12345)) @@ -98,7 +98,7 @@ public void sendPaymentFromSecp256k1Wallet() throws JsonRpcClientErrorException, .scanForResult(() -> this.getValidatedAccountInfo(sourceWalletAddress)); Payment payment = Payment.builder() .account(sourceWalletAddress) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(accountInfo.accountData().sequence()) .destination(destinationWalletAddress) .amount(XrpCurrencyAmount.ofDrops(12345)) @@ -173,7 +173,7 @@ private void multiSigSendPaymentHelper( FeeResult feeResult = xrplClient.fee(); SignerListSet signerListSet = SignerListSet.builder() .account(sourceWallet.address()) - .fee(feeResult.drops().openLedgerFee()) + .fee(FeeUtils.computeNetworkFees(feeResult).recommendedFee()) .sequence(sourceAccountInfo.accountData().sequence()) .signerQuorum(UnsignedInteger.valueOf(2)) .addSignerEntries( @@ -225,10 +225,10 @@ private void multiSigSendPaymentHelper( Payment unsignedPayment = Payment.builder() .account(sourceWallet.address()) .fee( - Transaction.computeMultiSigFee( - feeResult.drops().openLedgerFee(), + FeeUtils.computeMultisigNetworkFees( + feeResult, sourceAccountInfoAfterSignerListSet.accountData().signerLists().get(0) - ) + ).recommendedFee() ) .sequence(sourceAccountInfoAfterSignerListSet.accountData().sequence()) .amount(XrpCurrencyAmount.ofDrops(12345)) diff --git a/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/accounts/AccountCurrenciesRequestParams.java b/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/accounts/AccountCurrenciesRequestParams.java index 9430427c6..1a25b8c8e 100644 --- a/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/accounts/AccountCurrenciesRequestParams.java +++ b/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/accounts/AccountCurrenciesRequestParams.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. @@ -28,7 +28,6 @@ import org.xrpl.xrpl4j.model.client.LegacyLedgerSpecifierUtils; import org.xrpl.xrpl4j.model.client.XrplRequestParams; import org.xrpl.xrpl4j.model.client.common.LedgerIndex; -import org.xrpl.xrpl4j.model.client.common.LedgerIndexShortcut; import org.xrpl.xrpl4j.model.client.common.LedgerSpecifier; import org.xrpl.xrpl4j.model.transactions.Address; import org.xrpl.xrpl4j.model.transactions.Hash256; @@ -44,6 +43,11 @@ @JsonDeserialize(as = ImmutableAccountCurrenciesRequestParams.class) public interface AccountCurrenciesRequestParams extends XrplRequestParams { + /** + * Builder for {@link AccountCurrenciesRequestParams}. + * + * @return A {@link ImmutableAccountCurrenciesRequestParams.Builder}. + */ static ImmutableAccountCurrenciesRequestParams.Builder builder() { return ImmutableAccountCurrenciesRequestParams.builder(); } @@ -59,6 +63,7 @@ static ImmutableAccountCurrenciesRequestParams.Builder builder() { * A 20-byte hex string for the ledger version to use. * * @return An optionally-present {@link Hash256}. + * * @deprecated Ledger hash should be specified in {@link #ledgerSpecifier()}. */ @JsonIgnore @@ -70,6 +75,7 @@ static ImmutableAccountCurrenciesRequestParams.Builder builder() { * The ledger index of the ledger to use, or a shortcut string to choose a ledger automatically. * * @return A {@link LedgerIndex}. Defaults to {@link LedgerIndex#CURRENT}. + * * @deprecated Ledger index and any shortcut values should be specified in {@link #ledgerSpecifier()}. */ @JsonIgnore @@ -79,8 +85,8 @@ static ImmutableAccountCurrenciesRequestParams.Builder builder() { LedgerIndex ledgerIndex(); /** - * Specifies the ledger version to request. A ledger version can be specified by ledger hash, - * numerical ledger index, or a shortcut value. + * Specifies the ledger version to request. A ledger version can be specified by ledger hash, numerical ledger index, + * or a shortcut value. * * @return A {@link LedgerSpecifier} specifying the ledger version to request. */ diff --git a/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/fees/ComputedNetworkFees.java b/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/fees/ComputedNetworkFees.java new file mode 100644 index 000000000..bda1ada78 --- /dev/null +++ b/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/fees/ComputedNetworkFees.java @@ -0,0 +1,91 @@ +package org.xrpl.xrpl4j.model.client.fees; + +import org.immutables.value.Value; +import org.immutables.value.Value.Derived; +import org.xrpl.xrpl4j.model.immutables.FluentCompareTo; +import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount; + +import java.math.BigDecimal; + +/** + * An object that holds fee options that were calculated based upon current ledger stats. + */ +@Value.Immutable +public interface ComputedNetworkFees { + + /** + * A builder of for {@link ComputedNetworkFees}. + * + * @return An {@link ImmutableComputedNetworkFees.Builder}. + */ + static ImmutableComputedNetworkFees.Builder builder() { + return ImmutableComputedNetworkFees.builder(); + } + + /** + * The `low` fee, which is the fee that should be used if the transaction queue is empty. + * + * @return An {@link XrpCurrencyAmount} representing the `low` fee (in drops). + */ + XrpCurrencyAmount feeLow(); + + /** + * The `medium` fee, which is the fee that should be used if the transaction queue is neither empty nor full. + * + * @return An {@link XrpCurrencyAmount} representing the `medium` fee (in drops). + */ + XrpCurrencyAmount feeMedium(); + + /** + * The `high` fee, which is the fee that should be used if the transaction queue is full. + * + * @return An {@link XrpCurrencyAmount} representing the `high` fee (in drops). + */ + XrpCurrencyAmount feeHigh(); + + /** + * Measures the fullness of the transaction queue by representing the percent full that the queue is. For example, if + * the transaction queue can hold two transactions, and one is in the queue, then this value would be 50%, or 0.5. + * + * @return A {@link BigDecimal}. + */ + // TODO: Introduce Percentage. + BigDecimal queuePercentage(); + + /** + * Helper method to return the recommened fee to use. + * + * @return An {@link XrpCurrencyAmount} that is the recommended fee. + */ + @Derived + default XrpCurrencyAmount recommendedFee() { + if (isTransactionQueueEmpty()) { + return feeLow(); + } else if (isTranactionQueueFull()) { + return feeHigh(); + } else { // queue is neither empty nor full + return feeMedium(); + } + } + + /** + * Determines if the transaction queue is full. + * + * @return {@code true} if the transaction queue is full; {@code false} otherwise. + */ + @Derived + default boolean isTranactionQueueFull() { + return FluentCompareTo.is(queuePercentage()).greaterThanEqualTo(BigDecimal.ONE); + } + + /** + * Helper method to determine if a transaction queue is empty. + * + * @return {@code true} if the queue is empty; {@code false} otherwise. + */ + @Derived + default boolean isTransactionQueueEmpty() { + return FluentCompareTo.is(queuePercentage()).lessThanOrEqualTo(BigDecimal.ZERO); + } + +} diff --git a/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/fees/FeeResult.java b/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/fees/FeeResult.java index 375d9cb45..6e6701751 100644 --- a/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/fees/FeeResult.java +++ b/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/fees/FeeResult.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. @@ -33,8 +33,8 @@ import java.util.Optional; /** - * The result of a "fee" rippled API call, which reports the current state of the open-ledger requirements - * for the transaction cost. + * The result of a "fee" rippled API call, which reports the current state of the open-ledger requirements for the + * transaction cost. */ @Immutable @JsonSerialize(as = ImmutableFeeResult.class) @@ -78,8 +78,8 @@ static ImmutableFeeResult.Builder builder() { FeeDrops drops(); /** - * The approximate number of transactions expected to be included in the current ledger. - * This is based on the number of transactions in the previous ledger. + * The approximate number of transactions expected to be included in the current ledger. This is based on the number + * of transactions in the previous ledger. * * @return An {@link UnsignedInteger} denoting the expected ledger size. */ @@ -106,8 +106,8 @@ static ImmutableFeeResult.Builder builder() { FeeLevels levels(); /** - * The maximum number of transactions that the transaction queue can currently hold. - * Optional because this may not be present on older versions of rippled. + * The maximum number of transactions that the transaction queue can currently hold. Optional because this may not be + * present on older versions of rippled. * * @return An optionally-present {@link UnsignedInteger} denoting the maximum queue size. */ diff --git a/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/fees/FeeUtils.java b/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/fees/FeeUtils.java new file mode 100644 index 000000000..cf5c6002c --- /dev/null +++ b/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/client/fees/FeeUtils.java @@ -0,0 +1,432 @@ +package org.xrpl.xrpl4j.model.client.fees; + +import static org.xrpl.xrpl4j.model.transactions.CurrencyAmount.MAX_XRP_IN_DROPS; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.primitives.UnsignedInteger; +import com.google.common.primitives.UnsignedLong; +import org.immutables.value.Value.Derived; +import org.immutables.value.Value.Immutable; +import org.xrpl.xrpl4j.model.immutables.FluentCompareTo; +import org.xrpl.xrpl4j.model.ledger.SignerListObject; +import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.MathContext; +import java.math.RoundingMode; +import java.util.Arrays; +import java.util.Objects; + +/** + * Utils relating to XRPL fees. + */ +public class FeeUtils { + + private static final BigInteger MAX_UNSIGNED_LONG = UnsignedLong.MAX_VALUE.bigIntegerValue(); + + private static final BigDecimal ONE_POINT_ONE = new BigDecimal("1.1"); + + private static final BigDecimal ZERO_POINT_ONE = new BigDecimal("0.1"); + + private static final BigInteger FIVE_HUNDRED = BigInteger.valueOf(500); + + private static final BigDecimal TWO = new BigDecimal(2); + + private static final BigDecimal THREE = new BigDecimal(3); + + private static final BigInteger FIFTEEN = BigInteger.valueOf(15); + + private static final BigInteger TEN_THOUSAND = BigInteger.valueOf(10000); + + private static final BigInteger ONE_THOUSAND = BigInteger.valueOf(1000); + + + /** + * Computes the fee necessary for a multisigned transaction. + * + *

The transaction cost of a multisigned transaction must be at least {@code (N + 1) * (the normal + * transaction cost)}, where {@code N} is the number of signatures provided. + * + * @param feeResult {@link FeeResult} object obtained by querying the ledger (e.g., via an `XrplClient#fee()` call). + * @param signerList The {@link SignerListObject} containing the signers of the transaction. + * + * @return An {@link XrpCurrencyAmount} representing the multisig fee. + */ + public static ComputedNetworkFees computeMultisigNetworkFees( + final FeeResult feeResult, + final SignerListObject signerList + ) { + Objects.requireNonNull(feeResult); + Objects.requireNonNull(signerList); + + ComputedNetworkFees computedNetworkFees = computeNetworkFees(feeResult); + XrpCurrencyAmount numberOfSignersAsAmount = XrpCurrencyAmount.of( + UnsignedLong.valueOf(signerList.signerEntries().size() + 1) + ); + return ComputedNetworkFees.builder() + .feeLow(computedNetworkFees.feeLow().times(numberOfSignersAsAmount)) + .feeMedium(computedNetworkFees.feeMedium().times(numberOfSignersAsAmount)) + .feeHigh(computedNetworkFees.feeHigh().times(numberOfSignersAsAmount)) + .queuePercentage(computedNetworkFees.queuePercentage()) + .build(); + } + + /** + * Calculate a suggested fee to be used for submitting a transaction to the XRPL. The calculated value depends on the + * current size of the job queue as compared to its total capacity. + * + * @param feeResult {@link FeeResult} object obtained by querying the ledger (e.g., via an `XrplClient#fee()` call). + * + * @return {@link ComputedNetworkFees} with low, medium and high fee levels to choose from for the transaction. + * + * @see "https://xrpl.org/fee.html" + * @see "https://github.com/XRPL-Labs/XUMM-App/blob/master/src/services/LedgerService.ts#L244" + */ + public static ComputedNetworkFees computeNetworkFees(final FeeResult feeResult) { + Objects.requireNonNull(feeResult); + + final DecomposedFees decomposedFees = DecomposedFees.builder(feeResult); + final XrpCurrencyAmount feeLow = computeFeeLow(decomposedFees); + + return ComputedNetworkFees.builder() + .feeLow(feeLow) + .feeMedium(computeFeeMedium(decomposedFees, feeLow)) + .feeHigh(computeFeeHigh(decomposedFees)) + .queuePercentage(decomposedFees.queuePercentage()) + .build(); + } + + /** + * Calculate the lowest fee the user is able to pay if the queue is empty. + * + * @param decomposedFees A {@link DecomposedFees} that contains information about current XRPL fees. + * + * @return An {@link XrpCurrencyAmount} representing the `low` fee. + */ + private static XrpCurrencyAmount computeFeeLow(final DecomposedFees decomposedFees) { + Objects.requireNonNull(decomposedFees); + + final BigInteger adjustedMinimumFeeDrops = decomposedFees.adjustedMinimumFeeDrops(); + final BigInteger medianFee = decomposedFees.medianFeeDrops(); + final BigInteger openLedgerFee = decomposedFees.openLedgerFeeDrops(); + + // Cap `feeLow` to the size of an UnsignedLong. + return XrpCurrencyAmount.ofDrops( + toUnsignedLongSafe( + min( + max( + adjustedMinimumFeeDrops, // min fee * 1.50 + divideToBigInteger(max(medianFee, openLedgerFee), FIVE_HUNDRED) + ), + ONE_THOUSAND + ) + ) + ); + } + + /** + * Compute the `medium` fee, which is the fee to use when the transaction queue is neither empty nor full. + * + * @param decomposedFees A {@link DecomposedFees} with precomputed values to use. + * @param feeLow The computed `low` fee as found in {@link #computeFeeLow(DecomposedFees)}. + * + * @return An {@link UnsignedLong} representing the `medium` fee. + */ + private static XrpCurrencyAmount computeFeeMedium(final DecomposedFees decomposedFees, + final XrpCurrencyAmount feeLow) { + Objects.requireNonNull(decomposedFees); + Objects.requireNonNull(feeLow); + + final BigInteger minimumFee = decomposedFees.adjustedMinimumFeeDrops(); + final BigDecimal minimumFeeBd = decomposedFees.adjustedMinimumFeeDropsAsBigDecimal(); + final BigDecimal medianFeeBd = decomposedFees.medianFeeDropsAsBigDecimal(); + final BigDecimal queuePercentage = decomposedFees.queuePercentage(); + + final BigInteger possibleFeeMedium; + if (FluentCompareTo.is(queuePercentage).greaterThan(ZERO_POINT_ONE)) { + possibleFeeMedium = minimumFeeBd + .add(medianFeeBd) + .add(decomposedFees.openLedgerFeeDropsAsBigDecimal()) + .divide(THREE, 0, RoundingMode.HALF_UP) + .toBigIntegerExact(); + } else { // 0 > `queuePercentage` < 0.1 + // Note: `computeFeeMedium` is not called if `queuePercentage` is 0, so we omit that check even though it's in + // the original xumm code. + possibleFeeMedium = max(minimumFee.multiply(BigInteger.TEN), + minimumFeeBd.add(medianFeeBd).divide(TWO, 0, RoundingMode.HALF_UP).toBigIntegerExact()); + } + + // calculate the lowest fee the user is able to pay if there are txns in the queue + final BigInteger feeMedium = min( + possibleFeeMedium, + feeLow.value().bigIntegerValue().multiply(FIFTEEN), TEN_THOUSAND + ); + + return XrpCurrencyAmount.ofDrops( + toUnsignedLongSafe(feeMedium) + ); + } + + /** + * Compute the `high` fee, which is the fee to use when the transaction queue is full. + * + * @param decomposedFees A {@link DecomposedFees} with precomputed values to use. + * + * @return An {@link UnsignedLong} representing the `high` fee. + */ + private static XrpCurrencyAmount computeFeeHigh(final DecomposedFees decomposedFees) { + Objects.requireNonNull(decomposedFees); + + final BigInteger minimumFee = decomposedFees.adjustedMinimumFeeDrops(); + final BigInteger medianFee = decomposedFees.medianFeeDrops(); + final BigInteger openLedgerFee = decomposedFees.openLedgerFeeDrops(); + + final BigInteger feeHigh = min( + max(minimumFee.multiply(BigInteger.TEN), multiplyToBigInteger(max(medianFee, openLedgerFee), ONE_POINT_ONE)), + TEN_THOUSAND); + return XrpCurrencyAmount.ofDrops( + toUnsignedLongSafe(feeHigh) + ); + } + + /** + * Helper method to determine if a transaction queue is empty by inspecting a `percent-full` measurement. + * + * @param queuePercentage A {@link BigDecimal} representing the percent-full value for a tx queue. + * + * @return {@code true} if the queue is empty; {@code false} otherwise. + */ + @VisibleForTesting + public static boolean queueIsEmpty(final BigDecimal queuePercentage) { + Objects.requireNonNull(queuePercentage); + return FluentCompareTo.is(queuePercentage).lessThanOrEqualTo(BigDecimal.ZERO); + } + + /** + * Helper method to determine if a transaction queue is both non-empty, but not completely full, by inspecting a + * `percent-full` measurement. + * + * @param queuePercentage A {@link BigDecimal} representing the percent-full value for a tx queue. + * + * @return {@code true} if the queue is empty; {@code false} otherwise. + */ + @VisibleForTesting + public static boolean queueIsNotEmptyAndNotFull(final BigDecimal queuePercentage) { + Objects.requireNonNull(queuePercentage); + return FluentCompareTo.is(queuePercentage).betweenExclusive(BigDecimal.ZERO, BigDecimal.ONE); + } + + /** + * Convert a {@link BigInteger} into an {@link UnsignedLong} without overflowing. If the input overflows, return + * {@link UnsignedLong#MAX_VALUE} instead. + * + * @param value A {@link BigInteger} to convert. + * + * @return An equivalent {@code value} as an {@link UnsignedLong}, or {@link UnsignedLong#MAX_VALUE} if the input + * would overflow during conversion. + */ + // TODO: Move to MathUtils once all v3 modules are condensed and MathUtils is accessible. + @VisibleForTesting + static UnsignedLong toUnsignedLongSafe(final BigInteger value) { + Objects.requireNonNull(value); + return UnsignedLong.valueOf(min(value, MAX_UNSIGNED_LONG)); + } + + /** + * Pick the smaller of the two supplied inputs. + * + * @param input1 A {@link BigInteger} to potentially choose as the min (i.e., smallest) value. + * @param otherInputs A potentially empty array of {@link BigInteger}'s to compare and potentially choose from. + * + * @return The smallest value of any supplied inputs, or {@code input1} if that is the only supplied input. + */ + // TODO: Move to MathUtils once all v3 modules are condensed and MathUtils is accessible. + @VisibleForTesting + static BigInteger min(final BigInteger input1, final BigInteger... otherInputs) { + Objects.requireNonNull(input1); + Objects.requireNonNull(otherInputs); + + return Arrays.stream(otherInputs).min(BigInteger::compareTo).orElse(input1).min(input1); + } + + /** + * Pick the larger of the two supplied inputs. + * + * @param input1 A {@link BigInteger} to potentially choose as the max (i.e., largest) value. + * @param otherInputs A potentially empty array of {@link BigInteger}'s to compare and potentially choose from. + * + * @return The largest value of any supplied inputs, or {@code input1} if that is the only supplied input. + */ + // TODO: Move to MathUtils once all v3 modules are condensed and MathUtils is accessible. + @VisibleForTesting + static BigInteger max(final BigInteger input1, final BigInteger... otherInputs) { + Objects.requireNonNull(input1); + Objects.requireNonNull(otherInputs); + + return Arrays.stream(otherInputs).max(BigInteger::compareTo).orElse(input1).max(input1); + } + + /** + * Divides two {@link BigDecimal} numbers and then converts the result into a rounded {@link BigInteger}. + * + * @param numerator A {@link BigInteger} numerator for purposes of division. + * @param denominator A {@link BigInteger} denominator for purposes of division. + * + * @return A {@link BigInteger} result. + */ + @VisibleForTesting + static BigInteger divideToBigInteger(final BigDecimal numerator, final BigDecimal denominator) { + Objects.requireNonNull(numerator); + Objects.requireNonNull(denominator); + Preconditions.checkArgument(FluentCompareTo.is(denominator).greaterThan(BigDecimal.ZERO)); + return numerator.divide(denominator, 0, RoundingMode.HALF_UP).toBigIntegerExact(); + } + + /** + * Divides two {@link BigInteger} numbers and then converts the result into a rounded {@link BigInteger}. + * + * @param numerator A {@link BigInteger} numerator for purposes of division. + * @param denominator A {@link BigInteger} denominator for purposes of division. + * + * @return A {@link BigInteger} result. + */ + @VisibleForTesting + static BigInteger divideToBigInteger(final BigInteger numerator, final BigInteger denominator) { + return divideToBigInteger(new BigDecimal(numerator), new BigDecimal(denominator)); + } + + /** + * Multiply input1 {@link BigInteger} by input1 {@link BigDecimal} and then return the result as input1 rounded + * {@link BigInteger}. + * + * @param input1 The first {@link BigInteger}. + * @param input2 The second {@link BigInteger}. + * + * @return The multiplied amount. + */ + @VisibleForTesting + static BigInteger multiplyToBigInteger(final BigInteger input1, final BigDecimal input2) { + Objects.requireNonNull(input1); + Objects.requireNonNull(input2); + return new BigDecimal(input1).multiply(input2).setScale(0, RoundingMode.HALF_UP).toBigIntegerExact(); + } + + /** + * Helper object that exists solely to aid fee calculation so that BigInteger/BigDecimal objects don't have to be + * created more than once per call, and to put data into a state that simplifies fee calculation logic. + */ + @Immutable + public interface DecomposedFees { + + BigDecimal ONE_POINT_FIVE = new BigDecimal("1.5"); + BigInteger MAX_XRP_IN_DROPS_BIG_INT = BigInteger.valueOf(MAX_XRP_IN_DROPS); + + /** + * Build a new instance of {@link DecomposedFees} from the supplied input. + * + * @param feeResult A {@link FeeResult} to use as input. + * + * @return A {@link DecomposedFees}. + */ + static DecomposedFees builder(final FeeResult feeResult) { + Objects.requireNonNull(feeResult); + + final BigDecimal currentQueueSize = new BigDecimal(feeResult.currentQueueSize().bigIntegerValue()); + final BigDecimal maxQueueSize = feeResult.maxQueueSize().map(UnsignedInteger::bigIntegerValue) + .map(BigDecimal::new).orElse(new BigDecimal(5000)); // Arbitrary value, but should generally be present. + // Don't divide by 0 + final BigDecimal queuePercentage = FluentCompareTo.is(currentQueueSize).equalTo(BigDecimal.ZERO) ? BigDecimal.ZERO + : currentQueueSize.divide(maxQueueSize, MathContext.DECIMAL128); + + return builder(feeResult.drops(), queuePercentage); + } + + /** + * Build a new instance of {@link DecomposedFees} from the supplied input. + * + * @param feeDrops A {@link FeeDrops} to use as input. + * @param queuePercentage A {@link BigDecimal} representing how full the transaction queue is. + * + * @return A {@link DecomposedFees}. + */ + static DecomposedFees builder(final FeeDrops feeDrops, final BigDecimal queuePercentage) { + Objects.requireNonNull(feeDrops); + Objects.requireNonNull(queuePercentage); + Preconditions.checkArgument(FluentCompareTo.is(queuePercentage).greaterThanEqualTo(BigDecimal.ZERO)); + Preconditions.checkArgument(FluentCompareTo.is(queuePercentage).lessThanOrEqualTo(BigDecimal.ONE)); + + // Min fee should be slightly larger than the indicated min. + final BigInteger adjustedMinimumFeeDrops = min(MAX_XRP_IN_DROPS_BIG_INT, + new BigDecimal(feeDrops.minimumFee().value().bigIntegerValue()).multiply(ONE_POINT_FIVE) + .setScale(0, RoundingMode.HALF_DOWN).toBigIntegerExact()); + + return ImmutableDecomposedFees.builder().adjustedMinimumFeeDrops(adjustedMinimumFeeDrops) + .medianFeeDrops(feeDrops.medianFee().value().bigIntegerValue()) + .openLedgerFeeDrops(feeDrops.openLedgerFee().value().bigIntegerValue()).queuePercentage(queuePercentage) + .build(); + } + + /** + * The minimum ledger fee as found in the supplied {@link FeeDrops} that was used to construct this instance., + * adjusted to be at least 50% larger than what was supplied in order to provide a buffer for fee calculations. + * + * @return A {@link BigInteger} representing the adjusted minimum fee (in drops). + */ + BigInteger adjustedMinimumFeeDrops(); + + /** + * An equivalent of {@link #adjustedMinimumFeeDrops()}, but as a {@link BigDecimal}. + * + * @return A {@link BigDecimal} representing the adjusted minimum transaction fee on ledger (in drops). + */ + @Derived + default BigDecimal adjustedMinimumFeeDropsAsBigDecimal() { + return new BigDecimal(adjustedMinimumFeeDrops()); + } + + /** + * The median ledger fee as found in the supplied {@link FeeDrops} that was used to construct this instance. + * + * @return A {@link BigInteger} representing the median transaction fee on ledger (in drops). + */ + BigInteger medianFeeDrops(); + + /** + * An equivalent of {@link #medianFeeDrops()}, but as a {@link BigDecimal}. + * + * @return A {@link BigDecimal} representing the median transaction fee on ledger (in drops). + */ + @Derived + default BigDecimal medianFeeDropsAsBigDecimal() { + return new BigDecimal(medianFeeDrops()); + } + + /** + * The open ledger fee as found in the supplied {@link FeeDrops} that was used to construct this instance. + * + * @return A {@link BigInteger} representing open ledger fee on ledger (in drops). + */ + BigInteger openLedgerFeeDrops(); + + /** + * An equivalent of {@link #openLedgerFeeDrops()}, but as a {@link BigDecimal}. + * + * @return A {@link BigInteger} representing open ledger fee on ledger (in drops). + */ + @Derived + default BigDecimal openLedgerFeeDropsAsBigDecimal() { + return new BigDecimal(openLedgerFeeDrops()); + } + + /** + * Measures the fullness of the transaction queue by representing the percent full that the queue is. For example, + * if the transaction queue can hold two transactions, and one is in the queue, then this value would be 50%, or + * 0.5. + * + * @return A {@link BigDecimal}. + */ + BigDecimal queuePercentage(); + } +} diff --git a/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/transactions/CurrencyAmount.java b/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/transactions/CurrencyAmount.java index 585ee04b8..9d9e6e346 100644 --- a/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/transactions/CurrencyAmount.java +++ b/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/transactions/CurrencyAmount.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. @@ -31,13 +31,17 @@ */ public interface CurrencyAmount { + long ONE_XRP_IN_DROPS = 1_000_000L; + long MAX_XRP = 100_000_000_000L; // <-- per https://xrpl.org/rippleapi-reference.html#value + long MAX_XRP_IN_DROPS = MAX_XRP * ONE_XRP_IN_DROPS; + /** * Handle this {@link CurrencyAmount} depending on its actual polymorphic sub-type. * - * @param xrpCurrencyAmountHandler A {@link Consumer} that is called if this instance is of type {@link - * XrpCurrencyAmount}. - * @param issuedCurrencyAmountConsumer A {@link Consumer} that is called if this instance is of type {@link - * IssuedCurrencyAmount}. + * @param xrpCurrencyAmountHandler A {@link Consumer} that is called if this instance is of type + * {@link XrpCurrencyAmount}. + * @param issuedCurrencyAmountConsumer A {@link Consumer} that is called if this instance is of type + * {@link IssuedCurrencyAmount}. */ default void handle( final Consumer xrpCurrencyAmountHandler, @@ -58,10 +62,10 @@ default void handle( /** * Map this {@link CurrencyAmount} to an instance of {@link R}, depending on its actualy polymorphic sub-type. * - * @param xrpCurrencyAmountMapper A {@link Function} that is called if this instance is of type {@link - * XrpCurrencyAmount}. - * @param issuedCurrencyAmountMapper A {@link Function} that is called if this instance is of type {@link - * IssuedCurrencyAmount}. + * @param xrpCurrencyAmountMapper A {@link Function} that is called if this instance is of type + * {@link XrpCurrencyAmount}. + * @param issuedCurrencyAmountMapper A {@link Function} that is called if this instance is of type + * {@link IssuedCurrencyAmount}. * @param The type of object to return after mapping. * * @return A {@link R} that is constructed by the appropriate mapper function. diff --git a/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/transactions/Transaction.java b/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/transactions/Transaction.java index 528dbb4fa..e55fc71f3 100644 --- a/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/transactions/Transaction.java +++ b/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/transactions/Transaction.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. @@ -95,6 +95,7 @@ public interface Transaction { * * @return An {@link XrpCurrencyAmount} representing the multisig fee. */ + @Deprecated static XrpCurrencyAmount computeMultiSigFee( final XrpCurrencyAmount currentLedgerFeeDrops, final SignerListObject signerList @@ -232,7 +233,7 @@ default UnsignedInteger sequence() { * * @return An optionally-present {@link UnsignedLong}. * @deprecated This field will be removed in favor of {@link - * org.xrpl.xrpl4j.model.client.transactions.TransactionResult#closeDate()}; + * org.xrpl.xrpl4j.model.client.transactions.TransactionResult#closeDate()}; */ @JsonProperty("date") @Deprecated diff --git a/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/transactions/Wrappers.java b/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/transactions/Wrappers.java index 9e57c5274..5c77a8e77 100644 --- a/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/transactions/Wrappers.java +++ b/xrpl4j-model/src/main/java/org/xrpl/xrpl4j/model/transactions/Wrappers.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. @@ -61,7 +61,7 @@ public String toString() { */ @Value.Check public void validateAddress() { - Preconditions.checkArgument(this.value().startsWith("r"),"Invalid Address: Bad Prefix"); + Preconditions.checkArgument(this.value().startsWith("r"), "Invalid Address: Bad Prefix"); Preconditions.checkArgument(this.value().length() >= 25 && this.value().length() <= 35, "Classic Addresses must be (25,35) characters long inclusive."); } @@ -133,9 +133,30 @@ public int hashCode() { @JsonDeserialize(as = XrpCurrencyAmount.class) abstract static class _XrpCurrencyAmount extends Wrapper implements Serializable, CurrencyAmount { + /** + * One XRP, in drops. + * + * @deprecated Prefer {@link CurrencyAmount#ONE_XRP_IN_DROPS}. + */ + @Deprecated static final long ONE_XRP_IN_DROPS = 1_000_000L; + + /** + * The largest XRP amount. + * + * @deprecated Prefer {@link CurrencyAmount#MAX_XRP}. + */ + @Deprecated static final long MAX_XRP = 100_000_000_000L; // <-- per https://xrpl.org/rippleapi-reference.html#value + + /** + * The largest XRP amount, in drops. + * + * @deprecated Prefer {@link CurrencyAmount#MAX_XRP_IN_DROPS}. + */ + @Deprecated static final long MAX_XRP_IN_DROPS = MAX_XRP * ONE_XRP_IN_DROPS; + static final BigDecimal SMALLEST_XRP = new BigDecimal("0.000001"); static final DecimalFormat FORMATTER = new DecimalFormat("###,###"); @@ -232,7 +253,8 @@ protected void check() { Preconditions.checkState( FluentCompareTo.is(value()).lessThanOrEqualTo(UnsignedLong.valueOf(MAX_XRP_IN_DROPS)), String.format( - "XRP Amounts may not exceed %s drops (100B XRP, denominated in Drops)", FORMATTER.format(MAX_XRP_IN_DROPS)) + "XRP Amounts may not exceed %s drops (100B XRP, denominated in Drops)", FORMATTER.format(MAX_XRP_IN_DROPS) + ) ); } diff --git a/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/client/fees/DecomposedFeesTest.java b/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/client/fees/DecomposedFeesTest.java new file mode 100644 index 000000000..5abc345f4 --- /dev/null +++ b/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/client/fees/DecomposedFeesTest.java @@ -0,0 +1,151 @@ +package org.xrpl.xrpl4j.model.client.fees; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.Mockito.when; +import static org.xrpl.xrpl4j.model.client.fees.FeeUtils.DecomposedFees.MAX_XRP_IN_DROPS_BIG_INT; +import static org.xrpl.xrpl4j.model.transactions.CurrencyAmount.MAX_XRP_IN_DROPS; + +import com.google.common.primitives.UnsignedInteger; +import com.google.common.primitives.UnsignedLong; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Optional; + +/** + * Unit tests for {@link FeeUtils.DecomposedFees}. + */ +public class DecomposedFeesTest { + + @Mock + FeeDrops feeDropsMock; + + @Mock + FeeResult feeResultMock; + + @BeforeEach + void setup() { + MockitoAnnotations.openMocks(this); + + when(feeResultMock.drops()).thenReturn(feeDropsMock); + when(feeResultMock.currentQueueSize()).thenReturn(UnsignedInteger.ZERO); + when(feeResultMock.maxQueueSize()).thenReturn(Optional.of(UnsignedInteger.ONE)); + + when(feeDropsMock.minimumFee()).thenReturn(XrpCurrencyAmount.builder().value(UnsignedLong.ZERO).build()); + when(feeDropsMock.medianFee()).thenReturn(XrpCurrencyAmount.builder().value(UnsignedLong.ZERO).build()); + when(feeDropsMock.openLedgerFee()).thenReturn(XrpCurrencyAmount.builder().value(UnsignedLong.ZERO).build()); + } + + @Test + void buildWithZeroFees() { + when(feeDropsMock.minimumFee()).thenReturn(XrpCurrencyAmount.builder().value(UnsignedLong.ZERO).build()); + when(feeDropsMock.medianFee()).thenReturn(XrpCurrencyAmount.builder().value(UnsignedLong.ZERO).build()); + when(feeDropsMock.openLedgerFee()).thenReturn(XrpCurrencyAmount.builder().value(UnsignedLong.ZERO).build()); + + FeeUtils.DecomposedFees decomposedFees = FeeUtils.DecomposedFees.builder(feeResultMock); + + assertThat(decomposedFees.adjustedMinimumFeeDrops()).isEqualTo(BigInteger.ZERO); + assertThat(decomposedFees.adjustedMinimumFeeDropsAsBigDecimal()).isEqualTo(BigDecimal.ZERO); + assertThat(decomposedFees.medianFeeDrops()).isEqualTo(BigInteger.ZERO); + assertThat(decomposedFees.medianFeeDropsAsBigDecimal()).isEqualTo(BigDecimal.ZERO); + assertThat(decomposedFees.openLedgerFeeDrops()).isEqualTo(BigInteger.ZERO); + assertThat(decomposedFees.openLedgerFeeDropsAsBigDecimal()).isEqualTo(BigDecimal.ZERO); + } + + @Test + void buildWithOneFees() { + when(feeDropsMock.minimumFee()).thenReturn(XrpCurrencyAmount.builder().value(UnsignedLong.ONE).build()); + when(feeDropsMock.medianFee()).thenReturn(XrpCurrencyAmount.builder().value(UnsignedLong.ONE).build()); + when(feeDropsMock.openLedgerFee()).thenReturn(XrpCurrencyAmount.builder().value(UnsignedLong.ONE).build()); + + FeeUtils.DecomposedFees decomposedFees = FeeUtils.DecomposedFees.builder(feeResultMock); + + assertThat(decomposedFees.adjustedMinimumFeeDrops()).isEqualTo(BigInteger.ONE); + assertThat(decomposedFees.adjustedMinimumFeeDropsAsBigDecimal()).isEqualTo(BigDecimal.ONE); + assertThat(decomposedFees.medianFeeDrops()).isEqualTo(BigInteger.ONE); + assertThat(decomposedFees.medianFeeDropsAsBigDecimal()).isEqualTo(BigDecimal.ONE); + assertThat(decomposedFees.openLedgerFeeDrops()).isEqualTo(BigInteger.ONE); + assertThat(decomposedFees.openLedgerFeeDropsAsBigDecimal()).isEqualTo(BigDecimal.ONE); + } + + @Test + void buildWithTenFees() { + when(feeDropsMock.minimumFee()).thenReturn(XrpCurrencyAmount.builder().value(UnsignedLong.valueOf(10L)).build()); + when(feeDropsMock.medianFee()).thenReturn(XrpCurrencyAmount.builder().value(UnsignedLong.valueOf(10L)).build()); + when(feeDropsMock.openLedgerFee()).thenReturn(XrpCurrencyAmount.builder().value(UnsignedLong.valueOf(10L)).build()); + + FeeUtils.DecomposedFees decomposedFees = FeeUtils.DecomposedFees.builder(feeResultMock); + + assertThat(decomposedFees.adjustedMinimumFeeDrops()).isEqualTo(BigInteger.valueOf(15L)); + assertThat(decomposedFees.adjustedMinimumFeeDropsAsBigDecimal()).isEqualTo(BigDecimal.valueOf(15L)); + assertThat(decomposedFees.medianFeeDrops()).isEqualTo(BigInteger.valueOf(10L)); + assertThat(decomposedFees.medianFeeDropsAsBigDecimal()).isEqualTo(BigDecimal.valueOf(10L)); + assertThat(decomposedFees.openLedgerFeeDrops()).isEqualTo(BigInteger.valueOf(10L)); + assertThat(decomposedFees.openLedgerFeeDropsAsBigDecimal()).isEqualTo(BigDecimal.valueOf(10L)); + } + + @Test + void buildWithMaxFees() { + when(feeResultMock.currentQueueSize()).thenReturn(UnsignedInteger.MAX_VALUE); + when(feeResultMock.maxQueueSize()).thenReturn(Optional.of(UnsignedInteger.MAX_VALUE)); + + final UnsignedLong maxFees = UnsignedLong.valueOf(MAX_XRP_IN_DROPS); + when(feeDropsMock.minimumFee()).thenReturn( + XrpCurrencyAmount.builder().value(maxFees).build()); + when(feeDropsMock.medianFee()).thenReturn( + XrpCurrencyAmount.builder().value(maxFees).build()); + when(feeDropsMock.openLedgerFee()).thenReturn( + XrpCurrencyAmount.builder().value(maxFees).build()); + + FeeUtils.DecomposedFees decomposedFees = FeeUtils.DecomposedFees.builder(feeResultMock); + + assertThat(decomposedFees.adjustedMinimumFeeDrops()).isEqualTo(MAX_XRP_IN_DROPS_BIG_INT); + assertThat(decomposedFees.adjustedMinimumFeeDropsAsBigDecimal()) + .isEqualTo(new BigDecimal(maxFees.bigIntegerValue())); + assertThat(decomposedFees.medianFeeDrops()).isEqualTo(MAX_XRP_IN_DROPS_BIG_INT); + assertThat(decomposedFees.medianFeeDropsAsBigDecimal()) + .isEqualTo(new BigDecimal(maxFees.bigIntegerValue())); + assertThat(decomposedFees.openLedgerFeeDrops()).isEqualTo(MAX_XRP_IN_DROPS_BIG_INT); + assertThat(decomposedFees.openLedgerFeeDropsAsBigDecimal()) + .isEqualTo(new BigDecimal(maxFees.bigIntegerValue())); + } + + @Test + void buildWithEmptyMaxQueueSize() { + when(feeResultMock.currentQueueSize()).thenReturn(UnsignedInteger.ONE); + when(feeResultMock.maxQueueSize()).thenReturn(Optional.empty()); + + when(feeDropsMock.minimumFee()).thenReturn(XrpCurrencyAmount.builder().value(UnsignedLong.ONE).build()); + when(feeDropsMock.medianFee()).thenReturn(XrpCurrencyAmount.builder().value(UnsignedLong.ONE).build()); + when(feeDropsMock.openLedgerFee()).thenReturn(XrpCurrencyAmount.builder().value(UnsignedLong.ONE).build()); + + FeeUtils.DecomposedFees decomposedFees = FeeUtils.DecomposedFees.builder(feeResultMock); + + assertThat(decomposedFees.adjustedMinimumFeeDrops()).isEqualTo(BigInteger.ONE); + assertThat(decomposedFees.adjustedMinimumFeeDropsAsBigDecimal()).isEqualTo(BigDecimal.ONE); + assertThat(decomposedFees.medianFeeDrops()).isEqualTo(BigInteger.ONE); + assertThat(decomposedFees.medianFeeDropsAsBigDecimal()).isEqualTo(BigDecimal.ONE); + assertThat(decomposedFees.openLedgerFeeDrops()).isEqualTo(BigInteger.ONE); + assertThat(decomposedFees.openLedgerFeeDropsAsBigDecimal()).isEqualTo(BigDecimal.ONE); + } + + @Test + void buildWithNegativeQueuePercentage() { + when(feeResultMock.currentQueueSize()).thenReturn(UnsignedInteger.ONE); + when(feeResultMock.maxQueueSize()).thenReturn(Optional.empty()); + + Assertions.assertThrows(IllegalArgumentException.class, () -> { + FeeUtils.DecomposedFees.builder(feeDropsMock, BigDecimal.valueOf(-1)); + }); + Assertions.assertThrows(IllegalArgumentException.class, () -> { + FeeUtils.DecomposedFees.builder(feeDropsMock, BigDecimal.valueOf(1.1)); + }); + } + +} \ No newline at end of file diff --git a/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/client/fees/FeeUtilsTest.java b/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/client/fees/FeeUtilsTest.java new file mode 100644 index 000000000..807de1f1c --- /dev/null +++ b/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/client/fees/FeeUtilsTest.java @@ -0,0 +1,574 @@ +package org.xrpl.xrpl4j.model.client.fees; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.xrpl.xrpl4j.model.client.fees.FeeUtils.computeMultisigNetworkFees; +import static org.xrpl.xrpl4j.model.client.fees.FeeUtils.computeNetworkFees; +import static org.xrpl.xrpl4j.model.transactions.CurrencyAmount.MAX_XRP; +import static org.xrpl.xrpl4j.model.transactions.CurrencyAmount.MAX_XRP_IN_DROPS; + +import com.google.common.primitives.UnsignedInteger; +import com.google.common.primitives.UnsignedLong; +import org.junit.jupiter.api.Test; +import org.xrpl.xrpl4j.model.client.common.LedgerIndex; +import org.xrpl.xrpl4j.model.flags.Flags; +import org.xrpl.xrpl4j.model.ledger.SignerEntry; +import org.xrpl.xrpl4j.model.ledger.SignerEntryWrapper; +import org.xrpl.xrpl4j.model.ledger.SignerListObject; +import org.xrpl.xrpl4j.model.transactions.Address; +import org.xrpl.xrpl4j.model.transactions.Hash256; +import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount; + +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * Unit tests for {@link FeeUtils}. + */ +public class FeeUtilsTest { + + @Test + public void nullInputForComputeMultiSigFee() { + assertThrows( + NullPointerException.class, + () -> computeMultisigNetworkFees(null, mock(SignerListObject.class)) + ); + + assertThrows( + NullPointerException.class, + () -> computeMultisigNetworkFees(mock(FeeResult.class), null) + ); + } + + @Test + public void simpleComputeMultiSigFee() { + FeeResult feeResult = feeResultBuilder().build(); + SignerListObject object = SignerListObject.builder() + .flags(Flags.SignerListFlags.UNSET) + .ownerNode("0000000000000000") + .previousTransactionId(Hash256.of("5904C0DC72C58A83AEFED2FFC5386356AA83FCA6A88C89D00646E51E687CDBE4")) + .previousTransactionLedgerSequence(UnsignedInteger.valueOf(16061435)) + .addSignerEntries( + SignerEntryWrapper.of( + SignerEntry.builder() + .account(Address.of("rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW")) + .signerWeight(UnsignedInteger.valueOf(2)) + .build() + ), + SignerEntryWrapper.of( + SignerEntry.builder() + .account(Address.of("raKEEVSGnKSD9Zyvxu4z6Pqpm4ABH8FS6n")) + .signerWeight(UnsignedInteger.valueOf(1)) + .build() + ) + ) + .signerListId(UnsignedInteger.ZERO) + .signerQuorum(UnsignedInteger.valueOf(3)) + .index(Hash256.of("A9C28A28B85CD533217F5C0A0C7767666B093FA58A0F2D80026FCC4CD932DDC7")) + .build(); + + assertThat(computeMultisigNetworkFees(feeResult, object).recommendedFee()) + .isEqualTo(XrpCurrencyAmount.ofDrops(15024)); + assertThat(computeMultisigNetworkFees(feeResult, object).feeLow()) + .isEqualTo(XrpCurrencyAmount.ofDrops(3000)); + assertThat(computeMultisigNetworkFees(feeResult, object).feeMedium()) + .isEqualTo(XrpCurrencyAmount.ofDrops(15024)); + assertThat(computeMultisigNetworkFees(feeResult, object).feeHigh()) + .isEqualTo(XrpCurrencyAmount.ofDrops(30000)); + } + + @Test + public void nullInputForCalculateFeeDynamically() { + assertThrows( + NullPointerException.class, + () -> computeNetworkFees(null) + ); + } + + @Test + public void computeNetworkFeesForAlmostEmptyQueue() { + FeeResult feeResult = FeeResult.builder() + .currentLedgerSize(UnsignedInteger.valueOf(56)) + .currentQueueSize(UnsignedInteger.valueOf(1)) + .drops( + FeeDrops.builder() + .baseFee(XrpCurrencyAmount.ofDrops(10)) + .medianFee(XrpCurrencyAmount.ofDrops(10000)) + .minimumFee(XrpCurrencyAmount.ofDrops(10)) + .openLedgerFee(XrpCurrencyAmount.ofDrops(2653937)) + .build() + ) + .expectedLedgerSize(UnsignedInteger.valueOf(55)) + .ledgerCurrentIndex(LedgerIndex.of(UnsignedInteger.valueOf(26575101))) + .levels( + FeeLevels.builder() + .medianLevel(XrpCurrencyAmount.ofDrops(256000)) + .minimumLevel(XrpCurrencyAmount.ofDrops(256)) + .openLedgerLevel(XrpCurrencyAmount.ofDrops(67940792)) + .referenceLevel(XrpCurrencyAmount.ofDrops(256)) + .build() + ) + .maxQueueSize(UnsignedInteger.valueOf(1100)) + .status("success") + .build(); + + ComputedNetworkFees computedNetworkFees = computeNetworkFees(feeResult); + assertThat(computedNetworkFees.feeLow()).isEqualTo(XrpCurrencyAmount.ofDrops(1000)); + assertThat(computedNetworkFees.feeMedium()).isEqualTo(XrpCurrencyAmount.ofDrops(5008)); + assertThat(computedNetworkFees.feeHigh()).isEqualTo(XrpCurrencyAmount.ofDrops(10000)); + assertThat(computedNetworkFees.recommendedFee()).isEqualTo(XrpCurrencyAmount.ofDrops(5008)); + } + + @Test + public void computeNetworkFeesForModeratelyFilledQueue() { + FeeResult feeResult = FeeResult.builder() + .currentLedgerSize(UnsignedInteger.valueOf(56)) + .currentQueueSize(UnsignedInteger.valueOf(220)) + .drops( + FeeDrops.builder() + .baseFee(XrpCurrencyAmount.ofDrops(10)) + .medianFee(XrpCurrencyAmount.ofDrops(10000)) + .minimumFee(XrpCurrencyAmount.ofDrops(10)) + .openLedgerFee(XrpCurrencyAmount.ofDrops(2653937)) + .build() + ) + .expectedLedgerSize(UnsignedInteger.valueOf(55)) + .ledgerCurrentIndex(LedgerIndex.of(UnsignedInteger.valueOf(26575101))) + .levels( + FeeLevels.builder() + .medianLevel(XrpCurrencyAmount.ofDrops(256000)) + .minimumLevel(XrpCurrencyAmount.ofDrops(256)) + .openLedgerLevel(XrpCurrencyAmount.ofDrops(67940792)) + .referenceLevel(XrpCurrencyAmount.ofDrops(256)) + .build() + ) + .maxQueueSize(UnsignedInteger.valueOf(1100)) + .status("success") + .build(); + + ComputedNetworkFees networkFeeResult = computeNetworkFees(feeResult); + assertThat(networkFeeResult.feeLow()).isEqualTo(XrpCurrencyAmount.ofDrops(1000)); + assertThat(networkFeeResult.feeMedium()).isEqualTo(XrpCurrencyAmount.ofDrops(10000)); + assertThat(networkFeeResult.feeHigh()).isEqualTo(XrpCurrencyAmount.ofDrops(10000)); + assertThat(networkFeeResult.recommendedFee()).isEqualTo(XrpCurrencyAmount.ofDrops(10000)); + } + + @Test + public void computeNetworkFeesForLessThanModerateTraffic() { + FeeResult feeResult = FeeResult.builder() + .currentLedgerSize(UnsignedInteger.valueOf(56)) + .currentQueueSize(UnsignedInteger.valueOf(100)) + .drops( + FeeDrops.builder() + .baseFee(XrpCurrencyAmount.ofDrops(10)) + .medianFee(XrpCurrencyAmount.ofDrops(10000)) + .minimumFee(XrpCurrencyAmount.ofDrops(10)) + .openLedgerFee(XrpCurrencyAmount.ofDrops(2653937)) + .build() + ) + .expectedLedgerSize(UnsignedInteger.valueOf(55)) + .ledgerCurrentIndex(LedgerIndex.of(UnsignedInteger.valueOf(26575101))) + .levels( + FeeLevels.builder() + .medianLevel(XrpCurrencyAmount.ofDrops(256000)) + .minimumLevel(XrpCurrencyAmount.ofDrops(256)) + .openLedgerLevel(XrpCurrencyAmount.ofDrops(67940792)) + .referenceLevel(XrpCurrencyAmount.ofDrops(256)) + .build() + ) + .maxQueueSize(UnsignedInteger.valueOf(1100)) + .status("success") + .build(); + + ComputedNetworkFees networkFeeResult = computeNetworkFees(feeResult); + assertThat(networkFeeResult.feeLow()).isEqualTo(XrpCurrencyAmount.ofDrops(1000)); + assertThat(networkFeeResult.feeMedium()).isEqualTo(XrpCurrencyAmount.ofDrops(5008)); + assertThat(networkFeeResult.feeHigh()).isEqualTo(XrpCurrencyAmount.ofDrops(10000)); + assertThat(networkFeeResult.recommendedFee()).isEqualTo(XrpCurrencyAmount.ofDrops(5008)); + } + + @Test + public void computeNetworkFeesForCompletelyFilledQueue() { + FeeResult feeResult = FeeResult.builder() + .currentLedgerSize(UnsignedInteger.valueOf(56)) + .currentQueueSize(UnsignedInteger.valueOf(110)) + .drops( + FeeDrops.builder() + .baseFee(XrpCurrencyAmount.ofDrops(10)) + .medianFee(XrpCurrencyAmount.ofDrops(100)) + .minimumFee(XrpCurrencyAmount.ofDrops(10)) + .openLedgerFee(XrpCurrencyAmount.ofDrops(2657)) + .build() + ) + .expectedLedgerSize(UnsignedInteger.valueOf(55)) + .ledgerCurrentIndex(LedgerIndex.of(UnsignedInteger.valueOf(26575101))) + .levels( + FeeLevels.builder() + .medianLevel(XrpCurrencyAmount.ofDrops(256000)) + .minimumLevel(XrpCurrencyAmount.ofDrops(256)) + .openLedgerLevel(XrpCurrencyAmount.ofDrops(67940792)) + .referenceLevel(XrpCurrencyAmount.ofDrops(256)) + .build() + ) + .maxQueueSize(UnsignedInteger.valueOf(110)) + .status("success") + .build(); + + ComputedNetworkFees networkFeeResult = computeNetworkFees(feeResult); + assertThat(networkFeeResult.feeLow()).isEqualTo(XrpCurrencyAmount.ofDrops(15)); + assertThat(networkFeeResult.feeMedium()).isEqualTo(XrpCurrencyAmount.ofDrops(225)); + assertThat(networkFeeResult.feeHigh()).isEqualTo(XrpCurrencyAmount.ofDrops(2923)); + assertThat(networkFeeResult.recommendedFee()).isEqualTo(XrpCurrencyAmount.ofDrops(2923)); + } + + @Test + public void computeNetworkFeesForEmptyQueue() { + FeeResult feeResult = FeeResult.builder() + .currentLedgerSize(UnsignedInteger.valueOf(56)) + .currentQueueSize(UnsignedInteger.valueOf(0)) + .drops( + FeeDrops.builder() + .baseFee(XrpCurrencyAmount.ofDrops(10)) + .medianFee(XrpCurrencyAmount.ofDrops(100)) + .minimumFee(XrpCurrencyAmount.ofDrops(10)) + .openLedgerFee(XrpCurrencyAmount.ofDrops(2657)) + .build() + ) + .expectedLedgerSize(UnsignedInteger.valueOf(55)) + .ledgerCurrentIndex(LedgerIndex.of(UnsignedInteger.valueOf(26575101))) + .levels( + FeeLevels.builder() + .medianLevel(XrpCurrencyAmount.ofDrops(256000)) + .minimumLevel(XrpCurrencyAmount.ofDrops(256)) + .openLedgerLevel(XrpCurrencyAmount.ofDrops(67940792)) + .referenceLevel(XrpCurrencyAmount.ofDrops(256)) + .build() + ) + .maxQueueSize(UnsignedInteger.valueOf(110)) + .status("success") + .build(); + + ComputedNetworkFees networkFeeResult = computeNetworkFees(feeResult); + assertThat(networkFeeResult.feeLow()).isEqualTo(XrpCurrencyAmount.ofDrops(15)); + assertThat(networkFeeResult.feeMedium()).isEqualTo(XrpCurrencyAmount.ofDrops(150)); + assertThat(networkFeeResult.feeHigh()).isEqualTo(XrpCurrencyAmount.ofDrops(2923)); + assertThat(networkFeeResult.recommendedFee()).isEqualTo(XrpCurrencyAmount.ofDrops(15)); + } + + @Test + public void calculateFeeUsingXummTestValuesForLow() { + FeeResult feeResult = FeeResult.builder() + .currentLedgerSize(UnsignedInteger.valueOf(1)) + .currentQueueSize(UnsignedInteger.valueOf(0)) + .drops( + FeeDrops.builder() + .baseFee(XrpCurrencyAmount.ofDrops(10)) + .medianFee(XrpCurrencyAmount.ofDrops(5000)) + .minimumFee(XrpCurrencyAmount.ofDrops(10)) + .openLedgerFee(XrpCurrencyAmount.ofDrops(5343)) + .build() + ) + .expectedLedgerSize(UnsignedInteger.valueOf(10)) + .ledgerCurrentIndex(LedgerIndex.of(UnsignedInteger.valueOf(26575101))) + .levels( + FeeLevels.builder() + .medianLevel(XrpCurrencyAmount.ofDrops(256000)) + .minimumLevel(XrpCurrencyAmount.ofDrops(10)) + .openLedgerLevel(XrpCurrencyAmount.ofDrops(67940792)) + .referenceLevel(XrpCurrencyAmount.ofDrops(256)) + .build() + ) + .maxQueueSize(UnsignedInteger.valueOf(2000)) + .status("success") + .build(); + + ComputedNetworkFees networkFeeResult = computeNetworkFees(feeResult); + assertThat(networkFeeResult.feeLow()).isEqualTo(XrpCurrencyAmount.ofDrops(15)); + assertThat(networkFeeResult.feeMedium()).isEqualTo(XrpCurrencyAmount.ofDrops(225)); + assertThat(networkFeeResult.feeHigh()).isEqualTo(XrpCurrencyAmount.ofDrops(5877)); + assertThat(networkFeeResult.recommendedFee()).isEqualTo(XrpCurrencyAmount.ofDrops(15)); + } + + @Test + public void calculateFeeUsingXummTestValuesForMedium() { + FeeResult feeResult = FeeResult.builder() + .currentLedgerSize(UnsignedInteger.valueOf(1)) + .currentQueueSize(UnsignedInteger.valueOf(1924)) + .drops( + FeeDrops.builder() + .baseFee(XrpCurrencyAmount.ofDrops(10)) + .medianFee(XrpCurrencyAmount.ofDrops(5000)) + .minimumFee(XrpCurrencyAmount.ofDrops(10)) + .openLedgerFee(XrpCurrencyAmount.ofDrops(5343)) + .build() + ) + .expectedLedgerSize(UnsignedInteger.valueOf(10)) + .ledgerCurrentIndex(LedgerIndex.of(UnsignedInteger.valueOf(26575101))) + .levels( + FeeLevels.builder() + .medianLevel(XrpCurrencyAmount.ofDrops(256000)) + .minimumLevel(XrpCurrencyAmount.ofDrops(10)) + .openLedgerLevel(XrpCurrencyAmount.ofDrops(67940792)) + .referenceLevel(XrpCurrencyAmount.ofDrops(256)) + .build() + ) + .maxQueueSize(UnsignedInteger.valueOf(2000)) + .status("success") + .build(); + + ComputedNetworkFees networkFeeResult = computeNetworkFees(feeResult); + assertThat(networkFeeResult.feeLow()).isEqualTo(XrpCurrencyAmount.ofDrops(15)); + assertThat(networkFeeResult.feeMedium()).isEqualTo(XrpCurrencyAmount.ofDrops(225)); + assertThat(networkFeeResult.feeHigh()).isEqualTo(XrpCurrencyAmount.ofDrops(5877)); + assertThat(networkFeeResult.recommendedFee()).isEqualTo(XrpCurrencyAmount.ofDrops(225)); + } + + @Test + public void calculateFeeUsingXummTestValuesForHigh() { + FeeResult feeResult = FeeResult.builder() + .currentLedgerSize(UnsignedInteger.valueOf(1)) + .currentQueueSize(UnsignedInteger.valueOf(2000)) + .drops( + FeeDrops.builder() + .baseFee(XrpCurrencyAmount.ofDrops(10)) + .medianFee(XrpCurrencyAmount.ofDrops(5000)) + .minimumFee(XrpCurrencyAmount.ofDrops(10)) + .openLedgerFee(XrpCurrencyAmount.ofDrops(5343)) + .build() + ) + .expectedLedgerSize(UnsignedInteger.valueOf(10)) + .ledgerCurrentIndex(LedgerIndex.of(UnsignedInteger.valueOf(26575101))) + .levels( + FeeLevels.builder() + .medianLevel(XrpCurrencyAmount.ofDrops(256000)) + .minimumLevel(XrpCurrencyAmount.ofDrops(10)) + .openLedgerLevel(XrpCurrencyAmount.ofDrops(67940792)) + .referenceLevel(XrpCurrencyAmount.ofDrops(256)) + .build() + ) + .maxQueueSize(UnsignedInteger.valueOf(2000)) + .status("success") + .build(); + + ComputedNetworkFees networkFeeResult = computeNetworkFees(feeResult); + assertThat(networkFeeResult.feeLow()).isEqualTo(XrpCurrencyAmount.ofDrops(15)); + assertThat(networkFeeResult.feeMedium()).isEqualTo(XrpCurrencyAmount.ofDrops(225)); + assertThat(networkFeeResult.feeHigh()).isEqualTo(XrpCurrencyAmount.ofDrops(5877)); + assertThat(networkFeeResult.recommendedFee()).isEqualTo(XrpCurrencyAmount.ofDrops(5877)); + } + + @Test + void testQueueIsEmpty() { + assertThrows( + NullPointerException.class, + () -> FeeUtils.queueIsEmpty(null) + ); + + assertThat(FeeUtils.queueIsEmpty(BigDecimal.valueOf(-1))).isTrue(); + assertThat(FeeUtils.queueIsEmpty(BigDecimal.ZERO)).isTrue(); + assertThat(FeeUtils.queueIsEmpty(BigDecimal.valueOf(0.1))).isFalse(); + assertThat(FeeUtils.queueIsEmpty(BigDecimal.valueOf(0.4))).isFalse(); + assertThat(FeeUtils.queueIsEmpty(BigDecimal.valueOf(0.5))).isFalse(); + assertThat(FeeUtils.queueIsEmpty(BigDecimal.valueOf(0.6))).isFalse(); + assertThat(FeeUtils.queueIsEmpty(BigDecimal.valueOf(0.999999))).isFalse(); + assertThat(FeeUtils.queueIsEmpty(BigDecimal.ONE)).isFalse(); + assertThat(FeeUtils.queueIsEmpty(BigDecimal.valueOf(2))).isFalse(); + } + + @Test + void testQueueIsNotEmptyAndNotFull() { + assertThrows( + NullPointerException.class, + () -> FeeUtils.queueIsNotEmptyAndNotFull(null) + ); + + assertThat(FeeUtils.queueIsNotEmptyAndNotFull(BigDecimal.valueOf(-1))).isFalse(); + assertThat(FeeUtils.queueIsNotEmptyAndNotFull(BigDecimal.ZERO)).isFalse(); + assertThat(FeeUtils.queueIsNotEmptyAndNotFull(BigDecimal.valueOf(0.1))).isTrue(); + assertThat(FeeUtils.queueIsNotEmptyAndNotFull(BigDecimal.valueOf(0.4))).isTrue(); + assertThat(FeeUtils.queueIsNotEmptyAndNotFull(BigDecimal.valueOf(0.5))).isTrue(); + assertThat(FeeUtils.queueIsNotEmptyAndNotFull(BigDecimal.valueOf(0.6))).isTrue(); + assertThat(FeeUtils.queueIsNotEmptyAndNotFull(BigDecimal.valueOf(0.999999))).isTrue(); + assertThat(FeeUtils.queueIsNotEmptyAndNotFull(BigDecimal.ONE)).isFalse(); + assertThat(FeeUtils.queueIsNotEmptyAndNotFull(BigDecimal.valueOf(2))).isFalse(); + } + + @Test + void testToUnsignedLongSafe() { + assertThrows( + NullPointerException.class, + () -> FeeUtils.toUnsignedLongSafe(null) + ); + + assertThrows( + IllegalArgumentException.class, + () -> FeeUtils.toUnsignedLongSafe(BigInteger.valueOf(-1)) + ); + + assertThat(FeeUtils.toUnsignedLongSafe(BigInteger.ZERO)).isEqualTo(UnsignedLong.ZERO); + assertThat(FeeUtils.toUnsignedLongSafe(BigInteger.ONE)).isEqualTo(UnsignedLong.ONE); + assertThat(FeeUtils.toUnsignedLongSafe(BigInteger.valueOf(MAX_XRP))).isEqualTo(UnsignedLong.valueOf(MAX_XRP)); + assertThat(FeeUtils.toUnsignedLongSafe(BigInteger.valueOf(MAX_XRP_IN_DROPS))) + .isEqualTo(UnsignedLong.valueOf(MAX_XRP_IN_DROPS)); + assertThat(FeeUtils.toUnsignedLongSafe(UnsignedLong.MAX_VALUE.bigIntegerValue())).isEqualTo(UnsignedLong.MAX_VALUE); + assertThat(FeeUtils.toUnsignedLongSafe(UnsignedLong.MAX_VALUE.bigIntegerValue().add(BigInteger.ONE))) + .isEqualTo(UnsignedLong.MAX_VALUE); + } + + @Test + void testMin() { + assertThrows( + NullPointerException.class, + () -> FeeUtils.min(null) + ); + + assertThrows( + NullPointerException.class, + () -> FeeUtils.min(BigInteger.ZERO, null) + ); + + assertThat(FeeUtils.min(BigInteger.valueOf(-1), BigInteger.ZERO)).isEqualTo(BigInteger.valueOf(-1)); + assertThat(FeeUtils.min(BigInteger.ZERO, BigInteger.ZERO)).isEqualTo(BigInteger.ZERO); + assertThat(FeeUtils.min(BigInteger.ZERO, BigInteger.ONE)).isEqualTo(BigInteger.ZERO); + assertThat(FeeUtils.min(BigInteger.ONE, BigInteger.ZERO)).isEqualTo(BigInteger.ZERO); + assertThat(FeeUtils.min(BigInteger.ONE, BigInteger.ONE)).isEqualTo(BigInteger.ONE); + assertThat(FeeUtils.min(BigInteger.ONE)).isEqualTo(BigInteger.ONE); + assertThat(FeeUtils.min(BigInteger.ZERO, BigInteger.valueOf(MAX_XRP_IN_DROPS))).isEqualTo(BigInteger.ZERO); + assertThat(FeeUtils.min(BigInteger.valueOf(MAX_XRP_IN_DROPS), BigInteger.ONE)).isEqualTo(BigInteger.ONE); + } + + @Test + void testMax() { + assertThrows( + NullPointerException.class, + () -> FeeUtils.max(null) + ); + + assertThrows( + NullPointerException.class, + () -> FeeUtils.max(BigInteger.ZERO, null) + ); + + assertThat(FeeUtils.max(BigInteger.valueOf(-1), BigInteger.ZERO)).isEqualTo(BigInteger.ZERO); + assertThat(FeeUtils.max(BigInteger.ZERO, BigInteger.ZERO)).isEqualTo(BigInteger.ZERO); + assertThat(FeeUtils.max(BigInteger.ZERO, BigInteger.ONE)).isEqualTo(BigInteger.ONE); + assertThat(FeeUtils.max(BigInteger.ONE, BigInteger.ZERO)).isEqualTo(BigInteger.ONE); + assertThat(FeeUtils.max(BigInteger.ONE, BigInteger.ONE)).isEqualTo(BigInteger.ONE); + assertThat(FeeUtils.max(BigInteger.ONE)).isEqualTo(BigInteger.ONE); + assertThat(FeeUtils.max(BigInteger.ZERO, BigInteger.valueOf(MAX_XRP_IN_DROPS))) + .isEqualTo(BigInteger.valueOf(MAX_XRP_IN_DROPS)); + assertThat(FeeUtils.max(BigInteger.valueOf(MAX_XRP_IN_DROPS), BigInteger.ONE)) + .isEqualTo(BigInteger.valueOf(MAX_XRP_IN_DROPS)); + } + + @Test + void testDivideBigDecimalsToBigInteger() { + final BigDecimal nullBigDecimal = null; + assertThrows( + NullPointerException.class, + () -> FeeUtils.divideToBigInteger(nullBigDecimal, BigDecimal.ONE) + ); + assertThrows( + NullPointerException.class, + () -> FeeUtils.divideToBigInteger(BigDecimal.ONE, nullBigDecimal) + ); + + assertThrows( + IllegalArgumentException.class, + () -> FeeUtils.divideToBigInteger(BigDecimal.ONE, BigDecimal.valueOf(-1)) + ); + + assertThat(FeeUtils.divideToBigInteger(BigDecimal.valueOf(-1), BigDecimal.ONE)).isEqualTo(BigInteger.valueOf(-1)); + assertThat(FeeUtils.divideToBigInteger(BigDecimal.ONE, BigDecimal.valueOf(2))).isEqualTo(BigInteger.ONE); + assertThat(FeeUtils.divideToBigInteger(BigDecimal.ONE, BigDecimal.valueOf(4))).isEqualTo(BigInteger.ZERO); + assertThat(FeeUtils.divideToBigInteger(BigDecimal.ONE, BigDecimal.TEN)).isEqualTo(BigInteger.ZERO); + assertThat(FeeUtils.divideToBigInteger(BigDecimal.TEN, BigDecimal.valueOf(2))).isEqualTo(BigInteger.valueOf(5)); + assertThat(FeeUtils.divideToBigInteger(new BigDecimal(UnsignedLong.MAX_VALUE.bigIntegerValue()), BigDecimal.ONE)) + .isEqualTo(UnsignedLong.MAX_VALUE.bigIntegerValue()); + assertThat(FeeUtils.divideToBigInteger( + new BigDecimal(UnsignedLong.MAX_VALUE.bigIntegerValue()), + new BigDecimal(UnsignedLong.MAX_VALUE.bigIntegerValue())) + ).isEqualTo(BigInteger.ONE); + } + + @Test + void testDivideBigIntegersToBigInteger() { + final BigInteger nullBigInteger = null; + assertThrows( + NullPointerException.class, + () -> FeeUtils.divideToBigInteger(nullBigInteger, BigInteger.ONE) + ); + assertThrows( + NullPointerException.class, + () -> FeeUtils.divideToBigInteger(BigInteger.ONE, nullBigInteger) + ); + + assertThrows( + IllegalArgumentException.class, + () -> FeeUtils.divideToBigInteger(BigInteger.ONE, BigInteger.valueOf(-1)) + ); + + assertThat(FeeUtils.divideToBigInteger(BigInteger.valueOf(-1), BigInteger.ONE)).isEqualTo(BigInteger.valueOf(-1)); + assertThat(FeeUtils.divideToBigInteger(BigInteger.ONE, BigInteger.valueOf(2))).isEqualTo(BigInteger.ONE); + assertThat(FeeUtils.divideToBigInteger(BigInteger.ONE, BigInteger.valueOf(4))).isEqualTo(BigInteger.ZERO); + assertThat(FeeUtils.divideToBigInteger(BigInteger.ONE, BigInteger.TEN)).isEqualTo(BigInteger.ZERO); + assertThat(FeeUtils.divideToBigInteger(BigInteger.TEN, BigInteger.valueOf(2))).isEqualTo(BigInteger.valueOf(5)); + assertThat(FeeUtils.divideToBigInteger(UnsignedLong.MAX_VALUE.bigIntegerValue(), BigInteger.ONE)) + .isEqualTo(UnsignedLong.MAX_VALUE.bigIntegerValue()); + assertThat(FeeUtils.divideToBigInteger( + UnsignedLong.MAX_VALUE.bigIntegerValue(), + UnsignedLong.MAX_VALUE.bigIntegerValue()) + ).isEqualTo(BigInteger.ONE); + } + + @Test + void testMultiplyToBigInteger() { + assertThrows( + NullPointerException.class, + () -> FeeUtils.multiplyToBigInteger(null, BigDecimal.ONE) + ); + assertThrows( + NullPointerException.class, + () -> FeeUtils.multiplyToBigInteger(BigInteger.ONE, null) + ); + + assertThat(FeeUtils.multiplyToBigInteger(BigInteger.valueOf(-1), BigDecimal.ONE)).isEqualTo(BigInteger.valueOf(-1)); + assertThat(FeeUtils.multiplyToBigInteger(BigInteger.ONE, BigDecimal.valueOf(2))).isEqualTo(BigInteger.valueOf(2)); + assertThat(FeeUtils.multiplyToBigInteger(BigInteger.ONE, BigDecimal.valueOf(4))).isEqualTo(BigInteger.valueOf(4)); + assertThat(FeeUtils.multiplyToBigInteger(BigInteger.ONE, BigDecimal.TEN)).isEqualTo(BigInteger.TEN); + assertThat(FeeUtils.multiplyToBigInteger(BigInteger.TEN, BigDecimal.valueOf(2))).isEqualTo(BigInteger.valueOf(20)); + assertThat(FeeUtils.multiplyToBigInteger(UnsignedLong.MAX_VALUE.bigIntegerValue(), BigDecimal.ONE)) + .isEqualTo(UnsignedLong.MAX_VALUE.bigIntegerValue()); + assertThat(FeeUtils.multiplyToBigInteger( + UnsignedLong.MAX_VALUE.bigIntegerValue(), + new BigDecimal(UnsignedLong.MAX_VALUE.bigIntegerValue()) + )).isEqualTo(new BigInteger("340282366920938463426481119284349108225")); + } + + private ImmutableFeeResult.Builder feeResultBuilder() { + return FeeResult.builder() + .currentLedgerSize(UnsignedInteger.valueOf(56)) + .currentQueueSize(UnsignedInteger.valueOf(1)) + .drops( + FeeDrops.builder() + .baseFee(XrpCurrencyAmount.ofDrops(10)) + .medianFee(XrpCurrencyAmount.ofDrops(10000)) + .minimumFee(XrpCurrencyAmount.ofDrops(10)) + .openLedgerFee(XrpCurrencyAmount.ofDrops(2653937)) + .build() + ) + .expectedLedgerSize(UnsignedInteger.valueOf(55)) + .ledgerCurrentIndex(LedgerIndex.of(UnsignedInteger.valueOf(26575101))) + .levels( + FeeLevels.builder() + .medianLevel(XrpCurrencyAmount.ofDrops(256000)) + .minimumLevel(XrpCurrencyAmount.ofDrops(256)) + .openLedgerLevel(XrpCurrencyAmount.ofDrops(67940792)) + .referenceLevel(XrpCurrencyAmount.ofDrops(256)) + .build() + ) + .maxQueueSize(UnsignedInteger.valueOf(1100)) + .status("success"); + } +} diff --git a/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/client/fees/NetworkFeeResultTests.java b/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/client/fees/NetworkFeeResultTests.java new file mode 100644 index 000000000..61ab19d88 --- /dev/null +++ b/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/client/fees/NetworkFeeResultTests.java @@ -0,0 +1,78 @@ +package org.xrpl.xrpl4j.model.client.fees; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.junit.jupiter.api.Test; +import org.xrpl.xrpl4j.model.transactions.XrpCurrencyAmount; + +import java.math.BigDecimal; + +/** + * Unit tests for {@link ComputedNetworkFees}. + */ +class NetworkFeeResultTests { + + @Test + void feeOptionsLow() { + ComputedNetworkFees feeOptions = ComputedNetworkFees.builder() + .feeLow(XrpCurrencyAmount.ofDrops(1L)) + .feeMedium(XrpCurrencyAmount.ofDrops(2L)) + .feeHigh(XrpCurrencyAmount.ofDrops(100L)) + .queuePercentage(BigDecimal.valueOf(-1)) + .build(); + + assertThat(feeOptions.recommendedFee()).isEqualTo(XrpCurrencyAmount.ofDrops(1L)); + assertThat(feeOptions.isTranactionQueueFull()).isFalse(); + assertThat(feeOptions.isTransactionQueueEmpty()).isTrue(); + + feeOptions = ComputedNetworkFees.builder() + .feeLow(XrpCurrencyAmount.ofDrops(1L)) + .feeMedium(XrpCurrencyAmount.ofDrops(2L)) + .feeHigh(XrpCurrencyAmount.ofDrops(100L)) + .queuePercentage(BigDecimal.ZERO) + .build(); + + assertThat(feeOptions.recommendedFee()).isEqualTo(XrpCurrencyAmount.ofDrops(1L)); + assertThat(feeOptions.isTranactionQueueFull()).isFalse(); + assertThat(feeOptions.isTransactionQueueEmpty()).isTrue(); + } + + @Test + void feeOptionsMedium() { + ComputedNetworkFees feeOptions = ComputedNetworkFees.builder() + .feeLow(XrpCurrencyAmount.ofDrops(1L)) + .feeMedium(XrpCurrencyAmount.ofDrops(2L)) + .feeHigh(XrpCurrencyAmount.ofDrops(100L)) + .queuePercentage(BigDecimal.valueOf(0.5)) + .build(); + + assertThat(feeOptions.recommendedFee()).isEqualTo(XrpCurrencyAmount.ofDrops(2L)); + assertThat(feeOptions.isTranactionQueueFull()).isFalse(); + assertThat(feeOptions.isTransactionQueueEmpty()).isFalse(); + } + + @Test + void isTranactionQueueHigh() { + ComputedNetworkFees feeOptions = ComputedNetworkFees.builder() + .feeLow(XrpCurrencyAmount.ofDrops(1L)) + .feeMedium(XrpCurrencyAmount.ofDrops(2L)) + .feeHigh(XrpCurrencyAmount.ofDrops(100L)) + .queuePercentage(BigDecimal.ONE) + .build(); + + assertThat(feeOptions.recommendedFee()).isEqualTo(XrpCurrencyAmount.ofDrops(100L)); + assertThat(feeOptions.isTranactionQueueFull()).isTrue(); + assertThat(feeOptions.isTransactionQueueEmpty()).isFalse(); + + feeOptions = ComputedNetworkFees.builder() + .feeLow(XrpCurrencyAmount.ofDrops(1L)) + .feeMedium(XrpCurrencyAmount.ofDrops(2L)) + .feeHigh(XrpCurrencyAmount.ofDrops(100L)) + .queuePercentage(BigDecimal.valueOf(1.1)) + .build(); + + assertThat(feeOptions.recommendedFee()).isEqualTo(XrpCurrencyAmount.ofDrops(100L)); + assertThat(feeOptions.isTranactionQueueFull()).isTrue(); + assertThat(feeOptions.isTransactionQueueEmpty()).isFalse(); + } +} \ No newline at end of file diff --git a/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/transactions/CurrencyAmountTest.java b/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/transactions/CurrencyAmountTest.java index 9e79ca818..f7129de5a 100644 --- a/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/transactions/CurrencyAmountTest.java +++ b/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/transactions/CurrencyAmountTest.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. @@ -133,4 +133,11 @@ public void mapIssuance() { issuedCurrencyAmount.map(($) -> new Object(), null) ); } + + @Test + void testConstants() { + assertThat(CurrencyAmount.ONE_XRP_IN_DROPS).isEqualTo(1_000_000L); + assertThat(CurrencyAmount.MAX_XRP).isEqualTo(100_000_000_000L); + assertThat(CurrencyAmount.MAX_XRP_IN_DROPS).isEqualTo(100_000_000_000_000_000L); + } } diff --git a/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/transactions/EscrowFinishTest.java b/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/transactions/EscrowFinishTest.java index 0cef3cd53..3d241145b 100644 --- a/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/transactions/EscrowFinishTest.java +++ b/xrpl4j-model/src/test/java/org/xrpl/xrpl4j/model/transactions/EscrowFinishTest.java @@ -90,7 +90,7 @@ public void testNormalizeWithNoFulfillmentAndCondition() { } @Test - public void testNormalizeWithFulfillmentAndConditionButLowFee() { + public void testNormalizeWithFulfillmentAndConditionButFeeLow() { // We expect the Fulfillment fulfillment = PreimageSha256Fulfillment.from("ssh".getBytes()); From e9aa7c491eb2d8589c207951a25a07518fe24d71 Mon Sep 17 00:00:00 2001 From: David Fuelling <323659+sappenin@users.noreply.github.com> Date: Thu, 6 Oct 2022 18:31:46 -0600 Subject: [PATCH 11/12] Fix IT to account for broken clio paging (#302) --- .../xrpl4j/tests/AccountTransactionsIT.java | 29 ++++++++++++------- .../tests/v3/AccountTransactionsIT.java | 22 ++++++++++---- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountTransactionsIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountTransactionsIT.java index 56d038fff..972f454df 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountTransactionsIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/AccountTransactionsIT.java @@ -24,6 +24,7 @@ import static org.awaitility.Awaitility.given; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.oneOf; import com.google.common.primitives.UnsignedInteger; import org.junit.jupiter.api.Test; @@ -58,20 +59,20 @@ public class AccountTransactionsIT { public void listTransactionsDefaultWithPagination() throws JsonRpcClientErrorException { AccountTransactionsResult results = mainnetClient.accountTransactions(MAINNET_ADDRESS); - assertThat(results.transactions()).hasSize(200); + // The default value for reporting mode is 200; but clio it's 50. However, the important things to test here is + // that a marker exists, so we only assert on that value. assertThat(results.marker()).isNotEmpty(); } @Test @Timeout(30) public void listTransactionsPagination() throws JsonRpcClientErrorException { - final int expectedTransactions = 284; + final int expectedTransactions = 748; // known ledger index range for this account that is known to have exactly 748 transactions LedgerIndexBound minLedger = LedgerIndexBound.of(61400000); - LedgerIndexBound maxLedger = LedgerIndexBound.of(61437000); + LedgerIndexBound maxLedger = LedgerIndexBound.of(61487000); AccountTransactionsResult results = mainnetClient.accountTransactions( - AccountTransactionsRequestParams - .builder(minLedger, maxLedger) + AccountTransactionsRequestParams.builder(minLedger, maxLedger) .account(MAINNET_ADDRESS) .build() ); @@ -79,20 +80,26 @@ public void listTransactionsPagination() throws JsonRpcClientErrorException { assertThat(results.marker()).isNotEmpty(); int transactionsFound = results.transactions().size(); - int pages = 1; - while (results.marker().isPresent()) { - results = mainnetClient.accountTransactions(AccountTransactionsRequestParams - .builder(minLedger, maxLedger) + int pages = 0; + while (results.marker().isPresent() && + // Needed because clio is broken. See https://github.com/XRPLF/clio/issues/195#issuecomment-1247412892 + results.transactions().size() > 0 + ) { + results = mainnetClient.accountTransactions(AccountTransactionsRequestParams.builder(minLedger, maxLedger) .account(MAINNET_ADDRESS) + .limit(UnsignedInteger.valueOf(200L)) .marker(results.marker().get()) .build()); - assertThat(results.transactions()).isNotEmpty(); transactionsFound += results.transactions().size(); pages++; + logger.info("Retrieved {} ledgers (marker={} page={})", + transactionsFound, + results.marker().map($ -> $.value()).orElseGet(() -> "n/a"), + pages + ); } assertThat(transactionsFound).isEqualTo(expectedTransactions); - assertThat(pages).isEqualTo(2); } @Test diff --git a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/AccountTransactionsIT.java b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/AccountTransactionsIT.java index a93eab75e..e58c2a538 100644 --- a/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/AccountTransactionsIT.java +++ b/xrpl4j-integration-tests/src/test/java/org/xrpl/xrpl4j/tests/v3/AccountTransactionsIT.java @@ -9,6 +9,8 @@ import org.awaitility.Durations; 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.accounts.AccountTransactionsRequestParams; @@ -29,6 +31,8 @@ */ public class AccountTransactionsIT { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + // an arbitrary address on xrpl mainnet that has a decent amount of transaction history public static final Address MAINNET_ADDRESS = Address.of("r9m6MwViR4GnUNqoGXGa8eroBrZ9FAPHFS"); @@ -38,7 +42,8 @@ public class AccountTransactionsIT { public void listTransactionsDefaultWithPagination() throws JsonRpcClientErrorException { AccountTransactionsResult results = mainnetClient.accountTransactions(MAINNET_ADDRESS); - assertThat(results.transactions()).hasSize(200); + // The default value for reporting mode is 200; but clio it's 50. However, the important things to test here is + // that a marker exists, so we only assert on that value. assertThat(results.marker()).isNotEmpty(); } @@ -58,19 +63,26 @@ public void listTransactionsPagination() throws JsonRpcClientErrorException { assertThat(results.marker()).isNotEmpty(); int transactionsFound = results.transactions().size(); - int pages = 1; - while (results.marker().isPresent()) { + int pages = 0; + while (results.marker().isPresent() && + // Needed because clio is broken. See https://github.com/XRPLF/clio/issues/195#issuecomment-1247412892 + results.transactions().size() > 0 + ) { results = mainnetClient.accountTransactions(AccountTransactionsRequestParams.builder(minLedger, maxLedger) .account(MAINNET_ADDRESS) + .limit(UnsignedInteger.valueOf(200L)) .marker(results.marker().get()) .build()); - assertThat(results.transactions()).isNotEmpty(); transactionsFound += results.transactions().size(); pages++; + logger.info("Retrieved {} ledgers (marker={} page={})", + transactionsFound, + results.marker().map($ -> $.value()).orElseGet(() -> "n/a"), + pages + ); } assertThat(transactionsFound).isEqualTo(expectedTransactions); - assertThat(pages).isEqualTo(4); } @Test From 3a48818652abaf7a904d66b7f4a142425d99d5fb Mon Sep 17 00:00:00 2001 From: Mukul Jangid <49061120+mukulljangid@users.noreply.github.com> Date: Fri, 7 Oct 2022 11:26:55 -0400 Subject: [PATCH 12/12] add serializer and deserializer for `Signature` (#305) * add serializer and deserializer for `Signature` * WrappedSignature example interface and test * fix checkstyle * add `as = ImmutableSignature.class` back Co-authored-by: David Fuelling <323659+sappenin@users.noreply.github.com> --- .../xrpl4j/crypto/core/signing/Signature.java | 4 +- .../core/signing/SignatureDeserializer.java | 22 +++++++++++ .../core/signing/SignatureSerializer.java | 17 ++++++++ .../UnsignedByteArrayDeserializer.java | 2 +- .../crypto/core/signing/SignatureTest.java | 39 ++++++++++++++++++- 5 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/SignatureDeserializer.java create mode 100644 xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/SignatureSerializer.java diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/Signature.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/Signature.java index 40054813b..62e7e3d9f 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/Signature.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/Signature.java @@ -12,8 +12,8 @@ * Represents a digital signature for a transaction that can be submitted to the XRP Ledger. */ @Value.Immutable -@JsonSerialize(as = ImmutableSignature.class) -@JsonDeserialize(as = ImmutableSignature.class) +@JsonSerialize(as = ImmutableSignature.class, using = SignatureSerializer.class) +@JsonDeserialize(as = ImmutableSignature.class, using = SignatureDeserializer.class) public interface Signature { /** diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/SignatureDeserializer.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/SignatureDeserializer.java new file mode 100644 index 000000000..27d47611d --- /dev/null +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/SignatureDeserializer.java @@ -0,0 +1,22 @@ +package org.xrpl.xrpl4j.crypto.core.signing; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import org.xrpl.xrpl4j.codec.addresses.UnsignedByteArray; + +import java.io.IOException; + +/** + * Deserializes signature string value to an object of type {@link Signature}. + */ +class SignatureDeserializer extends JsonDeserializer { + + @Override + public Signature deserialize(JsonParser jsonParser, DeserializationContext ctxt) + throws IOException { + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + return Signature.builder().value(UnsignedByteArray.fromHex(node.asText())).build(); + } +} \ No newline at end of file diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/SignatureSerializer.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/SignatureSerializer.java new file mode 100644 index 000000000..ba6521023 --- /dev/null +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/SignatureSerializer.java @@ -0,0 +1,17 @@ +package org.xrpl.xrpl4j.crypto.core.signing; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +/** + * Custom Jackson serializer for {@link Signature}es. + */ +class SignatureSerializer extends JsonSerializer { + @Override + public void serialize(Signature signature, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeString(signature.base16Value()); + } +} \ No newline at end of file diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/UnsignedByteArrayDeserializer.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/UnsignedByteArrayDeserializer.java index 4f33cb239..35704dcad 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/UnsignedByteArrayDeserializer.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/main/java/org/xrpl/xrpl4j/crypto/core/signing/UnsignedByteArrayDeserializer.java @@ -8,7 +8,7 @@ import java.io.IOException; /** - * A FasterXML serializer for {@link UnsignedByteArray}. + * A FasterXML deserializer for {@link UnsignedByteArray}. * * @deprecated This class will go away once {@link UnsignedByteArray} is moved into the core module. */ diff --git a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/test/java/org/xrpl/xrpl4j/crypto/core/signing/SignatureTest.java b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/test/java/org/xrpl/xrpl4j/crypto/core/signing/SignatureTest.java index ae1b316b3..6d50d2f22 100644 --- a/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/test/java/org/xrpl/xrpl4j/crypto/core/signing/SignatureTest.java +++ b/xrpl4j-crypto-parent/xrpl4j-crypto-core/src/test/java/org/xrpl/xrpl4j/crypto/core/signing/SignatureTest.java @@ -4,7 +4,11 @@ import static org.hamcrest.Matchers.is; 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.jayway.jsonassert.JsonAssert; +import org.immutables.value.Value; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.xrpl.xrpl4j.codec.addresses.UnsignedByteArray; @@ -46,7 +50,8 @@ void hexValue() { @Test void jsonSerializeAndDeserialize() throws JsonProcessingException { String json = ObjectMapperFactory.create().writeValueAsString(signature); - JsonAssert.with(json).assertThat("$.value", is(HEX_32_BYTES)); + JsonAssert.with(json).assertThat("$", is(HEX_32_BYTES)); + assertThat(json).isEqualTo("\"" + HEX_32_BYTES + "\""); Signature actual = ObjectMapperFactory.create().readValue(json, Signature.class); assertThat(actual).isEqualTo(signature); @@ -61,4 +66,36 @@ void of() { void fromBase16() { assertThat(Signature.fromBase16(HEX_32_BYTES)).isEqualTo(signature); } + + @Test + void serializeSignature() throws JsonProcessingException { + + ObjectMapper objectMapper = ObjectMapperFactory.create(); + + WrappedSignature wrappedSignature = WrappedSignature.builder() + .signature(signature) + .build(); + String serializedWrappedSignature = objectMapper.writeValueAsString(wrappedSignature); + + String serialized = "{\"signature\":\"" + HEX_32_BYTES + "\"}"; + assertThat(serializedWrappedSignature).isEqualTo(serialized); + + WrappedSignature deserializedWrappedSignature = objectMapper.readValue( + serializedWrappedSignature, WrappedSignature.class + ); + assertThat(deserializedWrappedSignature).isEqualTo(wrappedSignature); + + } + + @Value.Immutable + @JsonSerialize(as = ImmutableWrappedSignature.class) + @JsonDeserialize(as = ImmutableWrappedSignature.class) + interface WrappedSignature { + + static ImmutableWrappedSignature.Builder builder() { + return ImmutableWrappedSignature.builder(); + } + + Signature signature(); + } } \ No newline at end of file