Update tutorial for xrpl4j v3

This PR updates all Java examples to utilize xrpl4j v3, which has slight different contracts as compared to v2.
This commit is contained in:
David Fuelling
2023-05-09 16:54:40 -06:00
parent 022f24c1e6
commit a18fe56cdf
7 changed files with 189 additions and 220 deletions

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@ __pycache__
out/ out/
package-lock.json package-lock.json
yarn-error.log yarn-error.log
/.idea

View File

@@ -1,28 +1,26 @@
// Construct a network client // Construct a network client
HttpUrl rippledUrl = HttpUrl HttpUrl rippledUrl = HttpUrl.get("https://s.altnet.rippletest.net:51234/");
.get("https://s.altnet.rippletest.net:51234/"); System.out.println("Constructing an XrplClient connected to " + rippledUrl);
XrplClient xrplClient = new XrplClient(rippledUrl); XrplClient xrplClient = new XrplClient(rippledUrl);
// Create a Wallet using a WalletFactory // Create a random KeyPair
WalletFactory walletFactory = DefaultWalletFactory.getInstance(); KeyPair randomKeyPair = Seed.ed25519Seed().deriveKeyPair();
Wallet testWallet = walletFactory.randomWallet(true).wallet(); System.out.println("Generated KeyPair: " + randomKeyPair);
// Get the Classic and X-Addresses from testWallet // Derive the Classic and X-Addresses from testWallet
Address classicAddress = testWallet.classicAddress(); Address classicAddress = randomKeyPair.publicKey().deriveAddress();
XAddress xAddress = testWallet.xAddress(); XAddress xAddress = AddressCodec.getInstance().classicAddressToXAddress(classicAddress, true);
System.out.println("Classic Address: " + classicAddress); System.out.println("Classic Address: " + classicAddress);
System.out.println("X-Address: " + xAddress); System.out.println("X-Address: " + xAddress);
// Fund the account using the testnet Faucet // Fund the account using the testnet Faucet
FaucetClient faucetClient = FaucetClient FaucetClient faucetClient = FaucetClient.construct(HttpUrl.get("https://faucet.altnet.rippletest.net"));
.construct(HttpUrl.get("https://faucet.altnet.rippletest.net"));
faucetClient.fundAccount(FundAccountRequest.of(classicAddress)); faucetClient.fundAccount(FundAccountRequest.of(classicAddress));
System.out.println("Funded the account using the Testnet faucet.");
// Look up your Account Info // Look up your Account Info
AccountInfoRequestParams requestParams = AccountInfoRequestParams requestParams = AccountInfoRequestParams.of(classicAddress);
AccountInfoRequestParams.of(classicAddress); AccountInfoResult accountInfoResult = xrplClient.accountInfo(requestParams);
AccountInfoResult accountInfoResult =
xrplClient.accountInfo(requestParams);
// Print the result // Print the result
System.out.println(accountInfoResult); System.out.println(accountInfoResult);

View File

@@ -4,21 +4,22 @@
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.primitives.UnsignedInteger; import com.google.common.primitives.UnsignedInteger;
import com.google.common.primitives.UnsignedLong;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
import org.xrpl.xrpl4j.client.JsonRpcClientErrorException; import org.xrpl.xrpl4j.client.JsonRpcClientErrorException;
import org.xrpl.xrpl4j.client.XrplClient; import org.xrpl.xrpl4j.client.XrplClient;
import org.xrpl.xrpl4j.client.faucet.FaucetClient; import org.xrpl.xrpl4j.client.faucet.FaucetClient;
import org.xrpl.xrpl4j.client.faucet.FundAccountRequest; import org.xrpl.xrpl4j.client.faucet.FundAccountRequest;
import org.xrpl.xrpl4j.crypto.KeyMetadata; import org.xrpl.xrpl4j.crypto.keys.KeyPair;
import org.xrpl.xrpl4j.crypto.PrivateKey; import org.xrpl.xrpl4j.crypto.keys.PrivateKey;
import org.xrpl.xrpl4j.crypto.keys.Seed;
import org.xrpl.xrpl4j.crypto.signing.SignatureService; import org.xrpl.xrpl4j.crypto.signing.SignatureService;
import org.xrpl.xrpl4j.crypto.signing.SignedTransaction; import org.xrpl.xrpl4j.crypto.signing.SingleSignedTransaction;
import org.xrpl.xrpl4j.crypto.signing.SingleKeySignatureService; import org.xrpl.xrpl4j.crypto.signing.bc.BcSignatureService;
import org.xrpl.xrpl4j.model.client.accounts.AccountInfoRequestParams; import org.xrpl.xrpl4j.model.client.accounts.AccountInfoRequestParams;
import org.xrpl.xrpl4j.model.client.accounts.AccountLinesRequestParams; import org.xrpl.xrpl4j.model.client.accounts.AccountLinesRequestParams;
import org.xrpl.xrpl4j.model.client.accounts.TrustLine; import org.xrpl.xrpl4j.model.client.accounts.TrustLine;
import org.xrpl.xrpl4j.model.client.common.LedgerIndex; 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.FeeResult;
import org.xrpl.xrpl4j.model.client.ledger.LedgerRequestParams; import org.xrpl.xrpl4j.model.client.ledger.LedgerRequestParams;
import org.xrpl.xrpl4j.model.client.transactions.TransactionRequestParams; import org.xrpl.xrpl4j.model.client.transactions.TransactionRequestParams;
@@ -29,9 +30,6 @@ import org.xrpl.xrpl4j.model.transactions.ImmutableTrustSet;
import org.xrpl.xrpl4j.model.transactions.IssuedCurrencyAmount; import org.xrpl.xrpl4j.model.transactions.IssuedCurrencyAmount;
import org.xrpl.xrpl4j.model.transactions.Payment; import org.xrpl.xrpl4j.model.transactions.Payment;
import org.xrpl.xrpl4j.model.transactions.TrustSet; import org.xrpl.xrpl4j.model.transactions.TrustSet;
import org.xrpl.xrpl4j.wallet.DefaultWalletFactory;
import org.xrpl.xrpl4j.wallet.Wallet;
import org.xrpl.xrpl4j.wallet.WalletFactory;
import java.util.List; import java.util.List;
@@ -47,16 +45,15 @@ public class IssueToken {
FeeResult feeResult = xrplClient.fee(); FeeResult feeResult = xrplClient.fee();
// Create cold and hot Wallets using a WalletFactory ----------------------- // Create cold and hot KeyPairs -----------------------
WalletFactory walletFactory = DefaultWalletFactory.getInstance(); KeyPair coldWalletKeyPair = Seed.ed25519Seed().deriveKeyPair();
Wallet coldWallet = walletFactory.randomWallet(true).wallet(); KeyPair hotWalletKeyPair = Seed.ed25519Seed().deriveKeyPair();
Wallet hotWallet = walletFactory.randomWallet(true).wallet();
// Fund the account using the testnet Faucet ------------------------------- // Fund the account using the testnet Faucet -------------------------------
FaucetClient faucetClient = FaucetClient FaucetClient faucetClient = FaucetClient
.construct(HttpUrl.get("https://faucet.altnet.rippletest.net")); .construct(HttpUrl.get("https://faucet.altnet.rippletest.net"));
faucetClient.fundAccount(FundAccountRequest.of(coldWallet.classicAddress())); faucetClient.fundAccount(FundAccountRequest.of(coldWalletKeyPair.publicKey().deriveAddress()));
faucetClient.fundAccount(FundAccountRequest.of(hotWallet.classicAddress())); faucetClient.fundAccount(FundAccountRequest.of(hotWalletKeyPair.publicKey().deriveAddress()));
// If you go too soon, the funding transaction might slip back a ledger and // If you go too soon, the funding transaction might slip back a ledger and
// then your starting Sequence number will be off. This is mostly relevant // then your starting Sequence number will be off. This is mostly relevant
@@ -67,15 +64,15 @@ public class IssueToken {
try { try {
xrplClient.accountInfo( xrplClient.accountInfo(
AccountInfoRequestParams.builder() AccountInfoRequestParams.builder()
.ledgerIndex(LedgerIndex.VALIDATED) .ledgerSpecifier(LedgerSpecifier.VALIDATED)
.account(coldWallet.classicAddress()) .account(coldWalletKeyPair.publicKey().deriveAddress())
.build() .build()
); );
xrplClient.accountInfo( xrplClient.accountInfo(
AccountInfoRequestParams.builder() AccountInfoRequestParams.builder()
.ledgerIndex(LedgerIndex.VALIDATED) .ledgerSpecifier(LedgerSpecifier.VALIDATED)
.account(hotWallet.classicAddress()) .account(hotWalletKeyPair.publicKey().deriveAddress())
.build() .build()
); );
@@ -91,99 +88,85 @@ public class IssueToken {
// Configure issuer settings ----------------------------------------------- // Configure issuer settings -----------------------------------------------
UnsignedInteger coldWalletSequence = xrplClient.accountInfo( UnsignedInteger coldWalletSequence = xrplClient.accountInfo(
AccountInfoRequestParams.builder() AccountInfoRequestParams.builder()
.ledgerIndex(LedgerIndex.CURRENT) .ledgerSpecifier(LedgerSpecifier.CURRENT)
.account(coldWallet.classicAddress()) .account(coldWalletKeyPair.publicKey().deriveAddress())
.build() .build()
).accountData().sequence(); ).accountData().sequence();
AccountSet setDefaultRipple = AccountSet.builder() AccountSet setDefaultRipple = AccountSet.builder()
.account(coldWallet.classicAddress()) .account(coldWalletKeyPair.publicKey().deriveAddress())
.fee(feeResult.drops().minimumFee()) .fee(feeResult.drops().minimumFee())
.sequence(coldWalletSequence) .sequence(coldWalletSequence)
.signingPublicKey(coldWallet.publicKey()) .signingPublicKey(coldWalletKeyPair.publicKey())
.setFlag(AccountSet.AccountSetFlag.DEFAULT_RIPPLE) .setFlag(AccountSet.AccountSetFlag.DEFAULT_RIPPLE)
.lastLedgerSequence(computeLastLedgerSequence(xrplClient)) .lastLedgerSequence(computeLastLedgerSequence(xrplClient))
.build(); .build();
PrivateKey coldWalletPrivateKey = PrivateKey.fromBase16EncodedPrivateKey( SignatureService<PrivateKey> signatureService = new BcSignatureService();
coldWallet.privateKey().get() SingleSignedTransaction<AccountSet> signedAccountSet = signatureService.sign(
); coldWalletKeyPair.privateKey(), setDefaultRipple
SignatureService coldWalletSignatureService = new SingleKeySignatureService(coldWalletPrivateKey);
SignedTransaction<AccountSet> signedSetDefaultRipple = coldWalletSignatureService.sign(
KeyMetadata.EMPTY,
setDefaultRipple
); );
submitAndWaitForValidation(signedSetDefaultRipple, xrplClient); submitAndWaitForValidation(signedAccountSet, xrplClient);
// Configure hot address settings ------------------------------------------ // Configure hot address settings ------------------------------------------
UnsignedInteger hotWalletSequence = xrplClient.accountInfo( UnsignedInteger hotWalletSequence = xrplClient.accountInfo(
AccountInfoRequestParams.builder() AccountInfoRequestParams.builder()
.ledgerIndex(LedgerIndex.CURRENT) .ledgerSpecifier(LedgerSpecifier.CURRENT)
.account(hotWallet.classicAddress()) .account(hotWalletKeyPair.publicKey().deriveAddress())
.build() .build()
).accountData().sequence(); ).accountData().sequence();
AccountSet setRequireAuth = AccountSet.builder() AccountSet setRequireAuth = AccountSet.builder()
.account(hotWallet.classicAddress()) .account(hotWalletKeyPair.publicKey().deriveAddress())
.fee(feeResult.drops().minimumFee()) .fee(feeResult.drops().minimumFee())
.sequence(hotWalletSequence) .sequence(hotWalletSequence)
.signingPublicKey(hotWallet.publicKey()) .signingPublicKey(hotWalletKeyPair.publicKey())
.setFlag(AccountSet.AccountSetFlag.REQUIRE_AUTH) .setFlag(AccountSet.AccountSetFlag.REQUIRE_AUTH)
.lastLedgerSequence(computeLastLedgerSequence(xrplClient)) .lastLedgerSequence(computeLastLedgerSequence(xrplClient))
.build(); .build();
PrivateKey hotWalletPrivateKey = PrivateKey.fromBase16EncodedPrivateKey( SingleSignedTransaction<AccountSet> signedSetRequireAuth = signatureService.sign(
hotWallet.privateKey().get() hotWalletKeyPair.privateKey(), setRequireAuth
); );
SignatureService hotWalletSignatureService = new SingleKeySignatureService(hotWalletPrivateKey);
SignedTransaction<AccountSet> signedSetRequireAuth = hotWalletSignatureService.sign(
KeyMetadata.EMPTY,
setRequireAuth
);
submitAndWaitForValidation(signedSetRequireAuth, xrplClient); submitAndWaitForValidation(signedSetRequireAuth, xrplClient);
// Create trust line ------------------------------------------------------- // Create trust line -------------------------------------------------------
String currencyCode = "FOO"; String currencyCode = "FOO";
ImmutableTrustSet trustSet = TrustSet.builder() ImmutableTrustSet trustSet = TrustSet.builder()
.account(hotWallet.classicAddress()) .account(hotWalletKeyPair.publicKey().deriveAddress())
.fee(feeResult.drops().openLedgerFee()) .fee(feeResult.drops().openLedgerFee())
.sequence(hotWalletSequence.plus(UnsignedInteger.ONE)) .sequence(hotWalletSequence.plus(UnsignedInteger.ONE))
.limitAmount(IssuedCurrencyAmount.builder() .limitAmount(IssuedCurrencyAmount.builder()
.currency(currencyCode) .currency(currencyCode)
.issuer(coldWallet.classicAddress()) .issuer(coldWalletKeyPair.publicKey().deriveAddress())
.value("10000000000") .value("10000000000")
.build()) .build())
.signingPublicKey(hotWallet.publicKey()) .signingPublicKey(hotWalletKeyPair.publicKey())
.build(); .build();
SignedTransaction<TrustSet> signedTrustSet = hotWalletSignatureService.sign( SingleSignedTransaction<TrustSet> signedTrustSet = signatureService.sign(
KeyMetadata.EMPTY, hotWalletKeyPair.privateKey(), trustSet
trustSet
); );
submitAndWaitForValidation(signedTrustSet, xrplClient); submitAndWaitForValidation(signedTrustSet, xrplClient);
// Send token -------------------------------------------------------------- // Send token --------------------------------------------------------------
Payment payment = Payment.builder() Payment payment = Payment.builder()
.account(coldWallet.classicAddress()) .account(coldWalletKeyPair.publicKey().deriveAddress())
.fee(feeResult.drops().openLedgerFee()) .fee(feeResult.drops().openLedgerFee())
.sequence(coldWalletSequence.plus(UnsignedInteger.ONE)) .sequence(coldWalletSequence.plus(UnsignedInteger.ONE))
.destination(hotWallet.classicAddress()) .destination(hotWalletKeyPair.publicKey().deriveAddress())
.amount(IssuedCurrencyAmount.builder() .amount(IssuedCurrencyAmount.builder()
.issuer(coldWallet.classicAddress()) .issuer(coldWalletKeyPair.publicKey().deriveAddress())
.currency(currencyCode) .currency(currencyCode)
.value("3840") .value("3840")
.build()) .build())
.signingPublicKey(coldWallet.publicKey()) .signingPublicKey(coldWalletKeyPair.publicKey())
.build(); .build();
SignedTransaction<Payment> signedPayment = coldWalletSignatureService.sign( SingleSignedTransaction<Payment> signedPayment = signatureService.sign(
KeyMetadata.EMPTY, coldWalletKeyPair.privateKey(), payment
payment
); );
submitAndWaitForValidation(signedPayment, xrplClient); submitAndWaitForValidation(signedPayment, xrplClient);
@@ -191,8 +174,8 @@ public class IssueToken {
// Check balances ---------------------------------------------------------- // Check balances ----------------------------------------------------------
List<TrustLine> lines = xrplClient.accountLines( List<TrustLine> lines = xrplClient.accountLines(
AccountLinesRequestParams.builder() AccountLinesRequestParams.builder()
.account(hotWallet.classicAddress()) .account(hotWalletKeyPair.publicKey().deriveAddress())
.ledgerIndex(LedgerIndex.VALIDATED) .ledgerSpecifier(LedgerSpecifier.VALIDATED)
.build() .build()
).lines(); ).lines();
System.out.println("Hot wallet TrustLines: " + lines); System.out.println("Hot wallet TrustLines: " + lines);
@@ -204,7 +187,7 @@ public class IssueToken {
// Get the latest validated ledger index // Get the latest validated ledger index
LedgerIndex validatedLedger = xrplClient.ledger( LedgerIndex validatedLedger = xrplClient.ledger(
LedgerRequestParams.builder() LedgerRequestParams.builder()
.ledgerIndex(LedgerIndex.VALIDATED) .ledgerSpecifier(LedgerSpecifier.VALIDATED)
.build() .build()
) )
.ledgerIndex() .ledgerIndex()
@@ -212,11 +195,11 @@ public class IssueToken {
// Workaround for https://github.com/XRPLF/xrpl4j/issues/84 // Workaround for https://github.com/XRPLF/xrpl4j/issues/84
return UnsignedInteger.valueOf( return UnsignedInteger.valueOf(
validatedLedger.plus(UnsignedLong.valueOf(4)).unsignedLongValue().intValue() validatedLedger.plus(UnsignedInteger.valueOf(4)).unsignedIntegerValue().intValue()
); );
} }
private static void submitAndWaitForValidation(SignedTransaction<?> signedTransaction, XrplClient xrplClient) private static void submitAndWaitForValidation(SingleSignedTransaction<?> signedTransaction, XrplClient xrplClient)
throws InterruptedException, JsonRpcClientErrorException, JsonProcessingException { throws InterruptedException, JsonRpcClientErrorException, JsonProcessingException {
xrplClient.submit(signedTransaction); xrplClient.submit(signedTransaction);
@@ -224,9 +207,9 @@ public class IssueToken {
boolean transactionValidated = false; boolean transactionValidated = false;
boolean transactionExpired = false; boolean transactionExpired = false;
while (!transactionValidated && !transactionExpired) { while (!transactionValidated && !transactionExpired) {
Thread.sleep(4 * 1000); Thread.sleep(1000);
LedgerIndex latestValidatedLedgerIndex = xrplClient.ledger( LedgerIndex latestValidatedLedgerIndex = xrplClient.ledger(
LedgerRequestParams.builder().ledgerIndex(LedgerIndex.VALIDATED).build() LedgerRequestParams.builder().ledgerSpecifier(LedgerSpecifier.VALIDATED).build()
) )
.ledgerIndex() .ledgerIndex()
.orElseThrow(() -> .orElseThrow(() ->
@@ -243,12 +226,14 @@ public class IssueToken {
transactionResult.metadata().get().transactionResult()); transactionResult.metadata().get().transactionResult());
transactionValidated = true; transactionValidated = true;
} else { } else {
boolean lastLedgerSequenceHasPassed = FluentCompareTo.
is(latestValidatedLedgerIndex.unsignedLongValue()) boolean lastLedgerSequenceHasPassed = signedTransaction.signedTransaction().lastLedgerSequence()
.greaterThan(UnsignedLong.valueOf( .map((signedTransactionLastLedgerSeq) ->
signedTransaction.signedTransaction().lastLedgerSequence().get().intValue() FluentCompareTo.is(latestValidatedLedgerIndex.unsignedIntegerValue())
.greaterThan(signedTransactionLastLedgerSeq)
) )
); .orElse(false);
if (lastLedgerSequenceHasPassed) { if (lastLedgerSequenceHasPassed) {
System.out.println("LastLedgerSequence has passed. Last tx response: " + System.out.println("LastLedgerSequence has passed. Last tx response: " +
transactionResult); transactionResult);

View File

@@ -1,35 +1,29 @@
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Sign using a SingleKeySignatureService: // Sign using a SingleKeySignatureService:
// This implementation of SignatureService simply holds a PrivateKey in // This implementation of SignatureService signs Transactions using a
// memory and signs Transactions using that PrivateKey. This may be // supplied PrivateKey. This may be suitable for some applications, but is
// suitable for some applications, but is likely not secure enough for // likely not secure enough for server side applications, as keys should not
// server side applications, as keys must be stored and kept in memory. // be stored in memory whenever possible.
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Create a random wallet // Create a random wallet
WalletFactory walletFactory = DefaultWalletFactory.getInstance(); KeyPair randomKeyPair = Seed.ed25519Seed().deriveKeyPair();
Wallet wallet = walletFactory.randomWallet(true).wallet(); PublicKey publicKey = randomKeyPair.publicKey();
PrivateKey privateKey = randomKeyPair.privateKey()
// Construct a SingleKeySignatureService from the Wallet private key // Construct a SignatureService
PrivateKey privateKey = PrivateKey.fromBase16EncodedPrivateKey( SignatureService<PrivateKey> signatureService = new BcSignatureService();
wallet.privateKey().get()
);
SingleKeySignatureService signatureService =
new SingleKeySignatureService(privateKey);
// Construct and sign the Payment // Construct and sign the Payment
Payment payment = Payment.builder() Payment payment = Payment.builder()
.account(wallet.classicAddress()) .account(publicKey.deriveAddress())
.destination(Address.of("rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe")) .destination(Address.of("rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe"))
.amount(XrpCurrencyAmount.ofDrops(1000)) .amount(XrpCurrencyAmount.ofDrops(1000))
.fee(XrpCurrencyAmount.ofDrops(10)) .fee(XrpCurrencyAmount.ofDrops(10))
.sequence(UnsignedInteger.valueOf(16126889)) .sequence(UnsignedInteger.valueOf(16126889))
.signingPublicKey(signatureService.getPublicKey(KeyMetadata.EMPTY)) .signingPublicKey(publicKey)
.build(); .build();
SignedTransaction<Payment> signedPayment = signatureService.sign( SingleSignedTransaction<Payment> signedPayment = signatureService.sign(privateKey, payment);
KeyMetadata.EMPTY,
payment
);
System.out.println("Signed Payment: " + signedPayment.signedTransaction()); System.out.println("Signed Payment: " + signedPayment.signedTransaction());
@@ -47,25 +41,26 @@ System.out.println("Signed Payment: " + signedPayment.signedTransaction());
// implementation. // implementation.
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Construct a DerivedKeysSignatureService with a server secret // Construct a DerivedKeysSignatureService with a server secret (in this case "shh")
// (in this case "shh") SignatureService<PrivateKeyReference> derivedKeySignatureService = new BcDerivedKeySignatureService(
DerivedKeysSignatureService signatureService = () -> ServerSecret.of("shh".getBytes())
new DerivedKeysSignatureService("shh"::getBytes, VersionType.ED25519); );
// Choose a walletId. This can be anything as long as it is unique to your system. PrivateKeyReference privateKeyReference = new PrivateKeyReference() {
String walletId = "sample-wallet"; @Override
KeyMetadata keyMetadata = KeyMetadata.builder() public KeyType keyType() {
.platformIdentifier("jks") return KeyType.ED25519;
.keyringIdentifier("n/a") }
.keyIdentifier(walletId)
.keyVersion("1") @Override
.keyPassword("password") public String keyIdentifier() {
.build(); return "sample-keypair";
}
};
// Get the public key and classic address for the given walletId // Get the public key and classic address for the given walletId
PublicKey publicKey = signatureService.getPublicKey(keyMetadata); PublicKey publicKey = derivedKeySignatureService.derivePublicKey(privateKeyReference);
Address classicAddress = DefaultKeyPairService.getInstance() Address classicAddress = publicKey.deriveAddress();
.deriveAddress(publicKey.value());
// Construct and sign the Payment // Construct and sign the Payment
Payment payment = Payment.builder() Payment payment = Payment.builder()
@@ -74,9 +69,8 @@ Payment payment = Payment.builder()
.amount(XrpCurrencyAmount.ofDrops(1000)) .amount(XrpCurrencyAmount.ofDrops(1000))
.fee(XrpCurrencyAmount.ofDrops(10)) .fee(XrpCurrencyAmount.ofDrops(10))
.sequence(UnsignedInteger.valueOf(16126889)) .sequence(UnsignedInteger.valueOf(16126889))
.signingPublicKey(publicKey.base16Encoded()) .signingPublicKey(publicKey)
.build(); .build();
SignedTransaction<Payment> signedPayment = signatureService SingleSignedTransaction<Payment> signedPayment = derivedKeySignatureService.sign(privateKeyReference, payment);
.sign(keyMetadata, payment);
System.out.println("Signed Payment: " + signedPayment.signedTransaction()); System.out.println("Signed Payment: " + signedPayment.signedTransaction());

View File

@@ -1,12 +1,8 @@
// Example Credentials -------------------------------------------------------- // Create a KeyPair
WalletFactory walletFactory = DefaultWalletFactory.getInstance(); KeyPair randomKeyPair = Seed.ed25519Seed().deriveKeyPair();
Wallet testWallet = walletFactory
.fromSeed("sn3nxiW7v8KXzPzAqzyHXbSSKNuN9", true)
.wallet();
// Get the Classic address from testWallet // Get the Classic address from testWallet
Address classicAddress = testWallet.classicAddress(); Address classicAddress = randomKeyPair.publicKey().deriveAddress();
System.out.println(classicAddress); // "rMCcNuTcajgw7YTgBy1sys3b89QqjUrMpH"
// Connect -------------------------------------------------------------------- // Connect --------------------------------------------------------------------
HttpUrl rippledUrl = HttpUrl.get("https://s.altnet.rippletest.net:51234/"); HttpUrl rippledUrl = HttpUrl.get("https://s.altnet.rippletest.net:51234/");
@@ -15,8 +11,8 @@ XrplClient xrplClient = new XrplClient(rippledUrl);
// Prepare transaction -------------------------------------------------------- // Prepare transaction --------------------------------------------------------
// Look up your Account Info // Look up your Account Info
AccountInfoRequestParams requestParams = AccountInfoRequestParams.builder() AccountInfoRequestParams requestParams = AccountInfoRequestParams.builder()
.ledgerIndex(LedgerIndex.VALIDATED)
.account(classicAddress) .account(classicAddress)
.ledgerSpecifier(LedgerSpecifier.VALIDATED)
.build(); .build();
AccountInfoResult accountInfoResult = xrplClient.accountInfo(requestParams); AccountInfoResult accountInfoResult = xrplClient.accountInfo(requestParams);
UnsignedInteger sequence = accountInfoResult.accountData().sequence(); UnsignedInteger sequence = accountInfoResult.accountData().sequence();
@@ -28,16 +24,14 @@ XrpCurrencyAmount openLedgerFee = feeResult.drops().openLedgerFee();
// Get the latest validated ledger index // Get the latest validated ledger index
LedgerIndex validatedLedger = xrplClient.ledger( LedgerIndex validatedLedger = xrplClient.ledger(
LedgerRequestParams.builder() LedgerRequestParams.builder()
.ledgerIndex(LedgerIndex.VALIDATED) .ledgerSpecifier(LedgerSpecifier.VALIDATED)
.build() .build()
) )
.ledgerIndex() .ledgerIndex()
.orElseThrow(() -> new RuntimeException("LedgerIndex not available.")); .orElseThrow(() -> new RuntimeException("LedgerIndex not available."));
// Workaround for https://github.com/XRPLF/xrpl4j/issues/84 // LastLedgerSequence is the current ledger index + 4
UnsignedInteger lastLedgerSequence = UnsignedInteger.valueOf( UnsignedInteger lastLedgerSequence = validatedLedger.plus(UnsignedInteger.valueOf(4)).unsignedIntegerValue();
validatedLedger.plus(UnsignedLong.valueOf(4)).unsignedLongValue().intValue()
);
// Construct a Payment // Construct a Payment
Payment payment = Payment.builder() Payment payment = Payment.builder()
@@ -46,75 +40,61 @@ Payment payment = Payment.builder()
.destination(Address.of("rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe")) .destination(Address.of("rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe"))
.sequence(sequence) .sequence(sequence)
.fee(openLedgerFee) .fee(openLedgerFee)
.signingPublicKey(testWallet.publicKey()) .signingPublicKey(randomKeyPair.publicKey())
.lastLedgerSequence(lastLedgerSequence) .lastLedgerSequence(lastLedgerSequence)
.build(); .build();
System.out.println("Constructed Payment: " + payment); System.out.println("Constructed Payment: " + payment);
// Sign transaction ----------------------------------------------------------- // Sign transaction -----------------------------------------------------------
// Construct a SignatureService to sign the Payment // Construct a SignatureService to sign the Payment
PrivateKey privateKey = PrivateKey.fromBase16EncodedPrivateKey( SignatureService<PrivateKey> signatureService = new BcSignatureService();
testWallet.privateKey().get()
);
SignatureService signatureService = new SingleKeySignatureService(privateKey);
// Sign the Payment // Sign the Payment
SignedTransaction<Payment> signedPayment = signatureService.sign( SingleSignedTransaction<Payment> signedPayment = signatureService.sign(randomKeyPair.privateKey(), payment);
KeyMetadata.EMPTY,
payment
);
System.out.println("Signed Payment: " + signedPayment.signedTransaction()); System.out.println("Signed Payment: " + signedPayment.signedTransaction());
// Submit transaction --------------------------------------------------------- // Submit transaction ---------------------------------------------------------
SubmitResult<Transaction> prelimResult = xrplClient.submit(signedPayment); SubmitResult<Payment> paymentSubmitResult = xrplClient.submit(signedPayment);
System.out.println(prelimResult); System.out.println(paymentSubmitResult);
// Wait for validation -------------------------------------------------------- // Wait for validation --------------------------------------------------------
TransactionResult<Payment> transactionResult = null;
boolean transactionValidated = false; boolean transactionValidated = false;
boolean transactionExpired = false; boolean transactionExpired = false;
while (!transactionValidated && !transactionExpired) { while (!transactionValidated && !transactionExpired) {
Thread.sleep(4 * 1000); Thread.sleep(4 * 1000);
LedgerIndex latestValidatedLedgerIndex = xrplClient.ledger( LedgerIndex latestValidatedLedgerIndex = xrplClient.ledger(
LedgerRequestParams.builder().ledgerIndex(LedgerIndex.VALIDATED).build() LedgerRequestParams.builder()
.ledgerSpecifier(LedgerSpecifier.VALIDATED)
.build()
) )
.ledgerIndex() .ledgerIndex()
.orElseThrow(() -> .orElseThrow(() -> new RuntimeException("Ledger response did not contain a LedgerIndex."));
new RuntimeException("Ledger response did not contain a LedgerIndex.")
);
TransactionResult<Payment> transactionResult = xrplClient.transaction( transactionResult = xrplClient.transaction(TransactionRequestParams.of(signedPayment.hash()), Payment.class);
TransactionRequestParams.of(signedPayment.hash()),
Payment.class
);
if (transactionResult.validated()) { if (transactionResult.validated()) {
System.out.println("Payment was validated with result code " + System.out.println("Payment was validated with result code " + transactionResult.metadata().get().transactionResult());
transactionResult.metadata().get().transactionResult());
transactionValidated = true; transactionValidated = true;
} else { } else {
boolean lastLedgerSequenceHasPassed = FluentCompareTo. boolean lastLedgerSequenceHasPassed = FluentCompareTo.is(latestValidatedLedgerIndex.unsignedIntegerValue())
is(latestValidatedLedgerIndex.unsignedLongValue()) .greaterThan(UnsignedInteger.valueOf(lastLedgerSequence.intValue()));
.greaterThan(UnsignedLong.valueOf(lastLedgerSequence.intValue()));
if (lastLedgerSequenceHasPassed) { if (lastLedgerSequenceHasPassed) {
System.out.println("LastLedgerSequence has passed. Last tx response: " System.out.println("LastLedgerSequence has passed. Last tx response: " + transactionResult);
transactionResult);
);
transactionExpired = true; transactionExpired = true;
} else { } else {
System.out.println("Payment not yet validated."); System.out.println("Payment not yet validated.");
} }
} }
}
// Check transaction results -------------------------------------------------- // Check transaction results
System.out.println(transactionResult); System.out.println(transactionResult);
System.out.println("Explorer link: https://testnet.xrpl.org/transactions/" + System.out.println("Explorer link: https://testnet.xrpl.org/transactions/" + signedPayment.hash());
signedPayment.hash());
transactionResult.metadata().ifPresent(metadata -> { transactionResult.metadata().ifPresent(metadata -> {
System.out.println("Result code: " + metadata.transactionResult()); System.out.println("Result code: " + metadata.transactionResult());
metadata.deliveredAmount().ifPresent(deliveredAmount -> metadata.deliveredAmount().ifPresent(deliveredAmount ->
System.out.println("XRP Delivered: " + System.out.println("XRP Delivered: " + ((XrpCurrencyAmount) deliveredAmount).toXrp()));
((XrpCurrencyAmount) deliveredAmount).toXrp()) }
); );
});

View File

@@ -45,26 +45,30 @@ In this tutorial, you need the [xrpl4j-client](https://javadoc.io/doc/org.xrpl/x
To install with Maven, add the following to your project's `pom.xml` file and then run `mvn install`: To install with Maven, add the following to your project's `pom.xml` file and then run `mvn install`:
```xml ```xml
<dependencyManagement>
<dependencies> <dependencies>
<dependency>
<groupId>org.xrpl</groupId>
<artifactId>xrpl4j-bom</artifactId>
<version>3.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
```
```xml
<dependencies>
<dependency>
<groupId>org.xrpl</groupId>
<artifactId>xrpl4j-core</artifactId>
<version>3.0.1</version>
</dependency>
<dependency> <dependency>
<groupId>org.xrpl</groupId> <groupId>org.xrpl</groupId>
<artifactId>xrpl4j-client</artifactId> <artifactId>xrpl4j-client</artifactId>
<version>2.0.0</version> <version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.xrpl</groupId>
<artifactId>xrpl4j-address-codec</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.xrpl</groupId>
<artifactId>xrpl4j-keypairs</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.xrpl</groupId>
<artifactId>xrpl4j-model</artifactId>
<version>2.0.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
``` ```
@@ -86,9 +90,9 @@ Here are the basic steps you'll need to cover for almost any XRP Ledger project:
### {{n.next()}}. Connect to the XRP Ledger ### {{n.next()}}. Connect to the XRP Ledger
To make queries and submit transactions, you need to connect to the XRP Ledger. To do this with `xrpl4j`, To make queries and submit transactions, you need to connect to the XRP Ledger. To do this with `xrpl4j`,
you can use an [`XrplClient`](https://javadoc.io/doc/org.xrpl/xrpl4j-client/latest/org/xrpl/xrpl4j/client/XrplClient.html): you can use an [`XrplClient`](https://javadoc.io/doc/org.xrpl/xrpl4j-client/3.0.1/org/xrpl/xrpl4j/client/XrplClient.html):
{{ include_code("_code-samples/get-started/java/GetAccountInfo.java", start_with="// Construct a network client", end_before="// Create a Wallet using a WalletFactory", language="java") }} {{ include_code("_code-samples/get-started/java/GetAccountInfo.java", start_with="// Construct a network client", end_before="// Create a random KeyPair", language="java") }}
#### Connect to the production XRP Ledger #### Connect to the production XRP Ledger
@@ -112,7 +116,7 @@ To store value and execute transactions on the XRP Ledger, you need to get an ac
To generate a new account, `xrpl4j` provides the [`DefaultWalletFactory`](https://javadoc.io/doc/org.xrpl/xrpl4j-keypairs/latest/org/xrpl/xrpl4j/wallet/DefaultWalletFactory.html). To generate a new account, `xrpl4j` provides the [`DefaultWalletFactory`](https://javadoc.io/doc/org.xrpl/xrpl4j-keypairs/latest/org/xrpl/xrpl4j/wallet/DefaultWalletFactory.html).
{{ include_code("_code-samples/get-started/java/GetAccountInfo.java", start_with="// Create a Wallet using a WalletFactory", end_before="// Get the Classic and X-Addresses from testWallet", language="java") }} {{ include_code("_code-samples/get-started/java/GetAccountInfo.java", start_with="// Create a random KeyPair", end_before="// Derive the Classic and X-Addresses from testWallet", language="java") }}
The result of a call to `walletFactory.randomWallet(true).wallet()` is a [`Wallet` instance](https://javadoc.io/doc/org.xrpl/xrpl4j-keypairs/latest/org/xrpl/xrpl4j/wallet/Wallet.html): The result of a call to `walletFactory.randomWallet(true).wallet()` is a [`Wallet` instance](https://javadoc.io/doc/org.xrpl/xrpl4j-keypairs/latest/org/xrpl/xrpl4j/wallet/Wallet.html):
@@ -170,7 +174,14 @@ You should see output similar to this example:
```sh ```sh
Running the GetAccountInfo sample... Running the GetAccountInfo sample...
Constructing an XrplClient connected to https://s.altnet.rippletest.net:51234/ Constructing an XrplClient connected to https://s.altnet.rippletest.net:51234/
Generated a wallet with the following public key: ED015D922B5EACF09DF01168141FF27FA6229B0FAB9B4CD88D2B6DA036090EFAA4 Generated KeyPair: KeyPair{
privateKey=PrivateKey{value=[redacted], destroyed=false},
publicKey=PublicKey{value=UnsignedByteArray{unsignedBytes=List(size=33)},
base58Value=aKGgrZL2WTc85HJSkQGuKfinem5oMH1uCJankSWFATGUhqvygxir,
base16Value=EDFB1073327CCBDA342AD685AF1C04530294866B9CB10C21126DC004BFDBA287D1,
keyType=ED25519
}
}
Classic Address: rBXHGshqXu3Smy9FUsQTmo49bGpQUQEm3X Classic Address: rBXHGshqXu3Smy9FUsQTmo49bGpQUQEm3X
X-Address: T7yMiiJJCmgY2yg5WB2davUedDeBFAG5B8r9KHjKCxDdvv3 X-Address: T7yMiiJJCmgY2yg5WB2davUedDeBFAG5B8r9KHjKCxDdvv3
Funded the account using the Testnet faucet. Funded the account using the Testnet faucet.
@@ -178,17 +189,17 @@ AccountInfoResult{
status=success, status=success,
accountData=AccountRootObject{ accountData=AccountRootObject{
ledgerEntryType=ACCOUNT_ROOT, ledgerEntryType=ACCOUNT_ROOT,
account=rBXHGshqXu3Smy9FUsQTmo49bGpQUQEm3X, account=rDNwS2t4afhBogKqSFFmsDi1k7gmeGhz4p,
balance=1000000000, balance=10000000000,
flags=0, flags=0,
ownerCount=0, ownerCount=0,
previousTransactionId=0000000000000000000000000000000000000000000000000000000000000000, previousTransactionId=0000000000000000000000000000000000000000000000000000000000000000,
previousTransactionLedgerSequence=0, previousTransactionLedgerSequence=0,
sequence=17178149, sequence=37649083,
signerLists=[], signerLists=[],
index=0DC1B13C73A7F3D2D82446526D0C5D08E88F89BA442D54291117F1A08E447685 index=F607809578C2A413774B9A240480B8B7B10C3E296CA609337D2F41813F566B92
}, },
ledgerCurrentIndex=17178149, ledgerCurrentIndex=37649083,
validated=false validated=false
} }
``` ```

View File

@@ -95,7 +95,7 @@ Here are examples of how to sign transaction instructions locally using the foll
* **Python** - [`xrpl-py`](https://github.com/XRPLF/xrpl-py) * **Python** - [`xrpl-py`](https://github.com/XRPLF/xrpl-py)
* **Java** - [`xrpl4j-crypto-bouncycastle`](https://javadoc.io/doc/org.xrpl/xrpl4j-crypto-bouncycastle/latest/index.html) * **Java** - [`xrpl4j`](https://github.com/XRPLF/xrpl4j)
<!-- MULTICODE_BLOCK_START --> <!-- MULTICODE_BLOCK_START -->