From 734dd74587cbd7d7c89c0207e865a9614e4f7bbf Mon Sep 17 00:00:00 2001
From: Jun Luo <4catcode@gmail.com>
Date: Tue, 9 Jul 2024 19:23:57 +0800
Subject: [PATCH] feat: Add `Server.loadAccount` to load the `Account` object
used for building transactions, supporting `MuxedAccount`. (#616)
* feat: Add `Server.loadAccount` to load the `Account` object used for building transactions, supporting `MuxedAccount`.
* feat: Add support for `MuxedAccount` to `SorobanServer.getAccount`.
---
CHANGELOG.md | 2 +
.../main/java/network/lightsail/Payment.java | 3 +-
readme.md | 2 +-
src/main/java/org/stellar/sdk/Server.java | 27 +++++++++
.../java/org/stellar/sdk/SorobanServer.java | 11 ++--
.../org/stellar/sdk/SorobanServerTest.java | 55 +++++++++++++++++++
6 files changed, 92 insertions(+), 8 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 29d1dcbe0..f6ec7a121 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -46,6 +46,8 @@ As this project is pre 1.0, breaking changes may happen for minor version bumps.
- Removed all functions that return `Optional` value.
- refactor!: remove `rateLimitLimit`, `rateLimitRemaining`, and `rateLimitReset` from the `Response`.
- feat: add `MuxedAccount` class to represent a multiplexed account on Stellar's network.
+- feat: Add `Server.loadAccount` to load the `Account` object used for building transactions, supporting `MuxedAccount`.
+- feat: Add support for `MuxedAccount` to `SorobanServer.getAccount`.
## 0.44.0
### Update
diff --git a/examples/src/main/java/network/lightsail/Payment.java b/examples/src/main/java/network/lightsail/Payment.java
index 40fb6864e..6678240da 100644
--- a/examples/src/main/java/network/lightsail/Payment.java
+++ b/examples/src/main/java/network/lightsail/Payment.java
@@ -13,7 +13,6 @@
import org.stellar.sdk.exception.BadResponseException;
import org.stellar.sdk.exception.NetworkException;
import org.stellar.sdk.operations.PaymentOperation;
-import org.stellar.sdk.responses.AccountResponse;
import org.stellar.sdk.responses.TransactionResponse;
/** Create, sign, and submit a transaction using Java Stellar SDK. */
@@ -33,7 +32,7 @@ public static void main(String[] args) {
// Transactions require a valid sequence number that is specific to this account.
// We can fetch the current sequence number for the source account from Horizon.
- AccountResponse sourceAccount = server.accounts().account(sourceKeypair.getAccountId());
+ TransactionBuilderAccount sourceAccount = server.loadAccount(sourceKeypair.getAccountId());
// Build a payment operation, send 350.1234567 XLM to receiver
PaymentOperation paymentOperation =
diff --git a/readme.md b/readme.md
index fc6667306..6892f5205 100644
--- a/readme.md
+++ b/readme.md
@@ -36,7 +36,7 @@ For some examples on how to use this library, take a look at the [Get Started do
Javadoc is available at https://javadoc.io/doc/network.lightsail/stellar-sdk
## Examples
-This repository contains a number of helpful examples. You can find them in the [examples](./examples) directory.
+This repository contains a number of helpful examples. You can find them in the [examples](./examples/src/main/java/network/lightsail) directory.
## Integrate into Android project
If you want to integrate this SDK on Android platforms with API level 28 and above, you don't need any additional configuration.
diff --git a/src/main/java/org/stellar/sdk/Server.java b/src/main/java/org/stellar/sdk/Server.java
index 7ff1fd623..0209caf6a 100644
--- a/src/main/java/org/stellar/sdk/Server.java
+++ b/src/main/java/org/stellar/sdk/Server.java
@@ -83,6 +83,33 @@ public Server(String serverURI, OkHttpClient httpClient, OkHttpClient submitHttp
this.submitHttpClient = submitHttpClient;
}
+ /**
+ * Fetches an account's most current state in the ledger, then creates and returns an {@link
+ * Account} object.
+ *
+ * @param address The address of the account to load, muxed accounts are supported.
+ * @return {@link Account} object
+ * @throws org.stellar.sdk.exception.NetworkException All the exceptions below are subclasses of
+ * NetworkError
+ * @throws org.stellar.sdk.exception.BadRequestException if the request fails due to a bad request
+ * (4xx)
+ * @throws org.stellar.sdk.exception.BadResponseException if the request fails due to a bad
+ * response from the server (5xx)
+ * @throws TooManyRequestsException if the request fails due to too many requests sent to the
+ * server
+ * @throws org.stellar.sdk.exception.RequestTimeoutException When Horizon returns a Timeout
+ *
or connection timeout occurred
+ * @throws org.stellar.sdk.exception.UnknownResponseException if the server returns an unknown
+ * status code
+ * @throws org.stellar.sdk.exception.ConnectionErrorException When the request cannot be executed
+ * due to cancellation or connectivity problems, etc.
+ */
+ public TransactionBuilderAccount loadAccount(String address) {
+ MuxedAccount muxedAccount = new MuxedAccount(address);
+ AccountResponse accountResponse = this.accounts().account(muxedAccount.getAccountId());
+ return new Account(address, accountResponse.getSequenceNumber());
+ }
+
/**
* @return {@link RootRequestBuilder} instance.
*/
diff --git a/src/main/java/org/stellar/sdk/SorobanServer.java b/src/main/java/org/stellar/sdk/SorobanServer.java
index 44edc5095..2fabce527 100644
--- a/src/main/java/org/stellar/sdk/SorobanServer.java
+++ b/src/main/java/org/stellar/sdk/SorobanServer.java
@@ -94,7 +94,7 @@ public SorobanServer(String serverURI, OkHttpClient httpClient) {
* number for the account, so you can build a successful transaction with {@link
* TransactionBuilder}.
*
- * @param accountId The public address of the account to load.
+ * @param address The address of the account to load, muxed accounts are supported.
* @return An {@link Account} object containing the sequence number and current state of the
* account.
* @throws org.stellar.sdk.exception.NetworkException All the exceptions below are subclasses of
@@ -106,10 +106,11 @@ public SorobanServer(String serverURI, OkHttpClient httpClient) {
* @throws ConnectionErrorException When the request cannot be executed due to cancellation or
* connectivity problems, etc.
*/
- public TransactionBuilderAccount getAccount(String accountId) {
+ public TransactionBuilderAccount getAccount(String address) {
+ MuxedAccount muxedAccount = new MuxedAccount(address);
LedgerKey.LedgerKeyAccount ledgerKeyAccount =
LedgerKey.LedgerKeyAccount.builder()
- .accountID(KeyPair.fromAccountId(accountId).getXdrAccountId())
+ .accountID(KeyPair.fromAccountId(muxedAccount.getAccountId()).getXdrAccountId())
.build();
LedgerKey ledgerKey =
LedgerKey.builder().account(ledgerKeyAccount).discriminant(LedgerEntryType.ACCOUNT).build();
@@ -118,7 +119,7 @@ public TransactionBuilderAccount getAccount(String accountId) {
List entries =
getLedgerEntriesResponse.getEntries();
if (entries == null || entries.isEmpty()) {
- throw new AccountNotFoundException(accountId);
+ throw new AccountNotFoundException(muxedAccount.getAccountId());
}
LedgerEntry.LedgerEntryData ledgerEntryData;
try {
@@ -127,7 +128,7 @@ public TransactionBuilderAccount getAccount(String accountId) {
throw new IllegalArgumentException("Invalid ledgerEntryData: " + entries.get(0).getXdr(), e);
}
long sequence = ledgerEntryData.getAccount().getSeqNum().getSequenceNumber().getInt64();
- return new Account(accountId, sequence);
+ return new Account(address, sequence);
}
/**
diff --git a/src/test/java/org/stellar/sdk/SorobanServerTest.java b/src/test/java/org/stellar/sdk/SorobanServerTest.java
index 07ad1eee8..dacc732aa 100644
--- a/src/test/java/org/stellar/sdk/SorobanServerTest.java
+++ b/src/test/java/org/stellar/sdk/SorobanServerTest.java
@@ -129,6 +129,61 @@ public MockResponse dispatch(@NotNull RecordedRequest recordedRequest)
mockWebServer.close();
}
+ @Test
+ public void testGetAccountMuxed()
+ throws IOException, SorobanRpcException, AccountNotFoundException {
+ String accountId = "MDAT5HWTGIU4TSSZ4752OUC4SABDLTLZFRPZUJ3D6LKBNEPA7V2CIAAAAAAAAAPCIBOR2";
+ String json =
+ "{\n"
+ + " \"jsonrpc\": \"2.0\",\n"
+ + " \"id\": \"ecb18f82ec12484190673502d0486b98\",\n"
+ + " \"result\": {\n"
+ + " \"entries\": [\n"
+ + " {\n"
+ + " \"key\": \"AAAAAAAAAADBPp7TMinJylnn+6dQXJACNc15LF+aJ2Py1BaR4P10JA==\",\n"
+ + " \"xdr\": \"AAAAAAAAAADBPp7TMinJylnn+6dQXJACNc15LF+aJ2Py1BaR4P10JAAAABdIcDhpAAADHAAAAAwAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAABfI8AAAAAZMK3qQ==\",\n"
+ + " \"lastModifiedLedgerSeq\": \"97423\",\n"
+ + " \"liveUntilLedgerSeq\": \"97673\"\n"
+ + " }\n"
+ + " ],\n"
+ + " \"latestLedger\": \"108023\"\n"
+ + " }\n"
+ + "}";
+
+ MockWebServer mockWebServer = new MockWebServer();
+ Dispatcher dispatcher =
+ new Dispatcher() {
+ @NotNull
+ @Override
+ public MockResponse dispatch(@NotNull RecordedRequest recordedRequest)
+ throws InterruptedException {
+ GetLedgerEntriesRequest expectedRequest =
+ new GetLedgerEntriesRequest(
+ singletonList("AAAAAAAAAADBPp7TMinJylnn+6dQXJACNc15LF+aJ2Py1BaR4P10JA=="));
+ SorobanRpcRequest sorobanRpcRequest =
+ gson.fromJson(
+ recordedRequest.getBody().readUtf8(),
+ new TypeToken>() {}.getType());
+ if ("POST".equals(recordedRequest.getMethod())
+ && sorobanRpcRequest.getMethod().equals("getLedgerEntries")
+ && sorobanRpcRequest.getParams().equals(expectedRequest)) {
+ return new MockResponse().setResponseCode(200).setBody(json);
+ }
+ return new MockResponse().setResponseCode(404);
+ }
+ };
+ mockWebServer.setDispatcher(dispatcher);
+ mockWebServer.start();
+
+ HttpUrl baseUrl = mockWebServer.url("");
+ SorobanServer server = new SorobanServer(baseUrl.toString());
+ TransactionBuilderAccount resp = server.getAccount(accountId);
+ assertEquals(resp.getAccountId(), accountId);
+ assertEquals(resp.getSequenceNumber().longValue(), 3418793967628L);
+ server.close();
+ mockWebServer.close();
+ }
+
@Test(expected = AccountNotFoundException.class)
public void testGetAccountNotFoundThrows()
throws IOException, SorobanRpcException, AccountNotFoundException {