14: Add getProgramAccounts(...) API method
This commit is contained in:
@@ -331,6 +331,113 @@ public class SolanaBlockChainImpl implements SolanaBlockChain {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<SolanaAccountInfo> getProgramAccounts(
|
||||||
|
ΩSolanaProgramIdΩ programId,
|
||||||
|
Set<SolanaProgramAccountMemcmpFilter> filters
|
||||||
|
) throws IOException, InterruptedException {
|
||||||
|
String jsonBody = createGetProgramAccountsBody(programId, filters);
|
||||||
|
|
||||||
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
.uri(URI.create(RPC_URL))
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
HttpResponse<String> response = sendThrottled(request);
|
||||||
|
|
||||||
|
if (response.statusCode() != 200) {
|
||||||
|
throw new IOException("RPC call failed: HTTP " + response.statusCode() + "\n" + response.body());
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonNode root = objectMapper.readTree(response.body());
|
||||||
|
|
||||||
|
if (root.has("error")) {
|
||||||
|
throw new IOException("Solana RPC error: " + root.get("error").toPrettyString());
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonNode result = root.path("result");
|
||||||
|
|
||||||
|
if (!result.isArray()) {
|
||||||
|
throw new IOException("getProgramAccounts response did not contain result array!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<SolanaAccountInfo> accountInfos = new HashSet<>();
|
||||||
|
|
||||||
|
for (JsonNode accountNode : result) {
|
||||||
|
JsonNode pubkeyNode = accountNode.path("pubkey");
|
||||||
|
JsonNode account = accountNode.path("account");
|
||||||
|
|
||||||
|
if (!pubkeyNode.isTextual()) {
|
||||||
|
throw new IOException("getProgramAccounts response contained account without textual pubkey!");
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonNode ownerNode = account.path("owner");
|
||||||
|
if (!ownerNode.isTextual()) {
|
||||||
|
throw new IOException("getProgramAccounts response contained account without textual owner!");
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonNode dataNode = account.path("data");
|
||||||
|
if (!dataNode.isArray() || dataNode.isEmpty() || !dataNode.get(0).isTextual()) {
|
||||||
|
throw new IOException("getProgramAccounts response contained account without base64 data!");
|
||||||
|
}
|
||||||
|
|
||||||
|
accountInfos.add(new SolanaAccountInfo(
|
||||||
|
pubkeyNode.asText(),
|
||||||
|
ownerNode.asText(),
|
||||||
|
dataNode.get(0).asText()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Set.copyOf(accountInfos);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String createGetProgramAccountsBody(
|
||||||
|
ΩSolanaProgramIdΩ programId,
|
||||||
|
Set<SolanaProgramAccountMemcmpFilter> filters
|
||||||
|
) throws IOException {
|
||||||
|
StringBuilder filtersJson = new StringBuilder();
|
||||||
|
|
||||||
|
boolean first = true;
|
||||||
|
for (SolanaProgramAccountMemcmpFilter filter : filters) {
|
||||||
|
if (!first) {
|
||||||
|
filtersJson.append(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
filtersJson.append("""
|
||||||
|
{
|
||||||
|
"memcmp": {
|
||||||
|
"offset": %d,
|
||||||
|
"bytes": "%s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".formatted(
|
||||||
|
filter.offset(),
|
||||||
|
filter.bytes()
|
||||||
|
));
|
||||||
|
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return """
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"method": "getProgramAccounts",
|
||||||
|
"params": [
|
||||||
|
"%s",
|
||||||
|
{
|
||||||
|
"commitment": "finalized",
|
||||||
|
"encoding": "base64",
|
||||||
|
"filters": [
|
||||||
|
%s
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
""".formatted(programId, filtersJson);
|
||||||
|
}
|
||||||
|
|
||||||
private synchronized HttpResponse<String> sendThrottled(HttpRequest request) throws IOException, InterruptedException {
|
private synchronized HttpResponse<String> sendThrottled(HttpRequest request) throws IOException, InterruptedException {
|
||||||
waitBeforeRemoteCall();
|
waitBeforeRemoteCall();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user