Skip to content

Commit

Permalink
feat: Add Server.loadAccount to load the Account object used for …
Browse files Browse the repository at this point in the history
…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`.
  • Loading branch information
overcat authored Jul 9, 2024
1 parent ec72d95 commit 734dd74
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 8 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions examples/src/main/java/network/lightsail/Payment.java
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand All @@ -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 =
Expand Down
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/org/stellar/sdk/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 <code>Timeout
* </code> 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.
*/
Expand Down
11 changes: 6 additions & 5 deletions src/main/java/org/stellar/sdk/SorobanServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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();
Expand All @@ -118,7 +119,7 @@ public TransactionBuilderAccount getAccount(String accountId) {
List<GetLedgerEntriesResponse.LedgerEntryResult> entries =
getLedgerEntriesResponse.getEntries();
if (entries == null || entries.isEmpty()) {
throw new AccountNotFoundException(accountId);
throw new AccountNotFoundException(muxedAccount.getAccountId());
}
LedgerEntry.LedgerEntryData ledgerEntryData;
try {
Expand All @@ -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);
}

/**
Expand Down
55 changes: 55 additions & 0 deletions src/test/java/org/stellar/sdk/SorobanServerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<GetLedgerEntriesRequest> sorobanRpcRequest =
gson.fromJson(
recordedRequest.getBody().readUtf8(),
new TypeToken<SorobanRpcRequest<GetLedgerEntriesRequest>>() {}.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 {
Expand Down

0 comments on commit 734dd74

Please sign in to comment.