Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.lmax.solana4j.client.jsonrpc;

import com.lmax.solana4j.client.api.VoteAccount;
import com.lmax.solana4j.client.api.VoteAccounts;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

// https://solana.com/docs/rpc/http/getvoteaccounts
public class GetVoteAccountsContractTest extends SolanaClientIntegrationTestBase
{
@Test
void shouldGetVoteAccountsDefaultOptionalParams() throws SolanaJsonRpcClientException
{
final VoteAccounts voteAccounts = SOLANA_API.getVoteAccounts().getResponse();
assertThat(voteAccounts).isNotNull();

final List<VoteAccount> currentVoteAccounts = voteAccounts.getCurrent();
assertThat(currentVoteAccounts).isNotNull();
assertThat(currentVoteAccounts).isNotEmpty();

final VoteAccount firstVoteAccount = currentVoteAccounts.get(0);
assertThat(firstVoteAccount.getVotePubkey()).isNotNull();
assertThat(firstVoteAccount.getNodePubkey()).isNotNull();
}

@Test
void shouldGetVoteAccountsVotePubKeyOptionalParam() throws SolanaJsonRpcClientException
{
final VoteAccounts voteAccounts = SOLANA_API.getVoteAccounts().getResponse();
assertThat(voteAccounts).isNotNull();

final List<VoteAccount> currentVoteAccounts = voteAccounts.getCurrent();
assertThat(currentVoteAccounts).isNotNull();
assertThat(currentVoteAccounts).isNotEmpty();

final VoteAccount firstVoteAccount = currentVoteAccounts.get(0);

final SolanaJsonRpcClientOptionalParams optionalParams = new SolanaJsonRpcClientOptionalParams();
optionalParams.addParam("votePubkey", firstVoteAccount.getVotePubkey());
final VoteAccounts filteredVoteAccounts = SOLANA_API.getVoteAccounts(optionalParams).getResponse();

final List<VoteAccount> currentFilteredVoteAccounts = filteredVoteAccounts.getCurrent();
assertThat(currentFilteredVoteAccounts).isNotNull();
assertThat(currentFilteredVoteAccounts).isNotEmpty();
final VoteAccount firstFitleredVoteAccount = currentFilteredVoteAccounts.get(0);

assertThat(firstFitleredVoteAccount.getVotePubkey()).isEqualTo(firstVoteAccount.getVotePubkey());
Comment thread
RyanHealey marked this conversation as resolved.
Outdated
}

@Test
void shouldGetVoteAccountsKeepUnstakedDelinquentsOptionalParam() throws SolanaJsonRpcClientException
{
final SolanaJsonRpcClientOptionalParams optionalParams = new SolanaJsonRpcClientOptionalParams();
optionalParams.addParam("keepUnstakedDelinquents", true);
final VoteAccounts voteAccounts = SOLANA_API.getVoteAccounts(optionalParams).getResponse();
assertThat(voteAccounts).isNotNull();

final List<VoteAccount> currentVoteAccounts = voteAccounts.getCurrent();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this check that some zero-stake delinquents were returned? Presumably there won't always be any. I guess it is possible that there won't be any delinquents at all.

assertThat(currentVoteAccounts).isNotNull();
assertThat(currentVoteAccounts).isNotEmpty();
}

@Test
void shouldGetVoteAccountsDelinquentSlotDistanceOptionalParam() throws SolanaJsonRpcClientException
{
final SolanaJsonRpcClientOptionalParams optionalParams = new SolanaJsonRpcClientOptionalParams();
optionalParams.addParam("delinquentSlotDistance", 100000L);
final VoteAccounts voteAccounts = SOLANA_API.getVoteAccounts(optionalParams).getResponse();
assertThat(voteAccounts).isNotNull();

final List<VoteAccount> currentVoteAccounts = voteAccounts.getCurrent();
assertThat(currentVoteAccounts).isNotNull();
assertThat(currentVoteAccounts).isNotEmpty();
}
}
17 changes: 17 additions & 0 deletions client/src/main/java/com/lmax/solana4j/client/api/SolanaApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -335,4 +335,21 @@ SolanaClientResponse<List<TokenAccount>> getTokenAccountsByOwner(
* @throws SolanaJsonRpcClientException if there is an error with the JSON-RPC request
*/
SolanaClientResponse<List<ClusterNode>> getClusterNodes() throws SolanaJsonRpcClientException;

/**
* Returns the account info and associated stake for all the voting accounts in the current bank with default optional parameters.
*
* @return a {@link SolanaClientResponse} containing a {@link VoteAccounts} object with current and delinquent vote accounts
* @throws SolanaJsonRpcClientException if there is an error with the JSON-RPC request
*/
SolanaClientResponse<VoteAccounts> getVoteAccounts() throws SolanaJsonRpcClientException;

/**
* Returns the account info and associated stake for all the voting accounts in the current bank with optional parameters.
*
* @param optionalParams a map of optional parameters to customize the request, such as `commitment`, `votePubkey`, `keepUnstakedDelinquents`, and `delinquentSlotDistance`
* @return a {@link SolanaClientResponse} containing a {@link VoteAccounts} object with current and delinquent vote accounts
* @throws SolanaJsonRpcClientException if there is an error with the JSON-RPC request
*/
SolanaClientResponse<VoteAccounts> getVoteAccounts(SolanaClientOptionalParams optionalParams) throws SolanaJsonRpcClientException;
}
64 changes: 64 additions & 0 deletions client/src/main/java/com/lmax/solana4j/client/api/VoteAccount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.lmax.solana4j.client.api;

/**
* Represents a vote account in the Solana network.
*/
public interface VoteAccount
{
/**
* Returns the validator identity public key as a base58-encoded string.
*
* @return the validator identity public key
*/
String getNodePubkey();

/**
* Returns the vote account public key as a base58-encoded string.
*
* @return the vote account public key
*/
String getVotePubkey();

/**
* Returns the stake activated for this vote account in lamports.
*
* @return the activated stake in lamports
*/
long getActivatedStake();

/**
* Returns whether the vote account is a commission-earning account.
*
* @return true if this is a commission-earning account
*/
boolean isEpochVoteAccount();

/**
* Returns the current commission percentage for the vote account (0-100).
*
* @return the commission percentage
*/
int getCommission();

/**
* Returns the most recent slot voted on by this validator.
*
* @return the last vote slot
*/
long getLastVote();

/**
* Returns the current root slot for this validator.
*
* @return the root slot
*/
long getRootSlot();

/**
* Returns the epoch credits history for this vote account.
* Each entry is a three-element array containing [epoch, credits, previousCredits].
*
* @return the epoch credits history
*/
java.util.List<java.util.List<Long>> getEpochCredits();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to have an object to represent the array entries; as in an object with fields for epoch, credits, and previousCredits?

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.lmax.solana4j.client.api;

import java.util.List;

/**
* Represents the response from the getVoteAccounts RPC method.
*/
public interface VoteAccounts
{
/**
* Returns the list of current vote accounts.
*
* @return the list of current vote accounts
*/
List<VoteAccount> getCurrent();

/**
* Returns the list of delinquent vote accounts.
*
* @return the list of delinquent vote accounts
*/
List<VoteAccount> getDelinquent();
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.lmax.solana4j.client.api.TokenAccount;
import com.lmax.solana4j.client.api.TokenAmount;
import com.lmax.solana4j.client.api.TransactionResponse;
import com.lmax.solana4j.client.api.VoteAccounts;

import java.io.IOException;
import java.net.SocketTimeoutException;
Expand Down Expand Up @@ -455,6 +456,26 @@ public SolanaClientResponse<List<ClusterNode>> getClusterNodes() throws SolanaJs
ArrayList::new, "getClusterNodes");
}

@Override
public SolanaClientResponse<VoteAccounts> getVoteAccounts() throws SolanaJsonRpcClientException
{
return queryForObject(new TypeReference<RpcWrapperDTO<VoteAccountsDTO>>()
{
},
dto -> dto, "getVoteAccounts",
defaultOptionalParams());
}

@Override
public SolanaClientResponse<VoteAccounts> getVoteAccounts(final SolanaClientOptionalParams optionalParams) throws SolanaJsonRpcClientException
{
return queryForObject(new TypeReference<RpcWrapperDTO<VoteAccountsDTO>>()
{
},
dto -> dto, "getVoteAccounts",
optionalParams.getParams());
}

private <S, T> SolanaClientResponse<S> queryForObject(
final TypeReference<RpcWrapperDTO<T>> type,
final Function<T, S> dtoMapper,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package com.lmax.solana4j.client.jsonrpc;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.lmax.solana4j.client.api.VoteAccount;

import java.util.List;
import java.util.stream.Collectors;

Comment thread
RyanHealey marked this conversation as resolved.
Outdated
/**
* Data Transfer Object for vote account information from the getVoteAccounts RPC call.
*/
public class VoteAccountDTO implements VoteAccount
{
private final String nodePubkey;
private final String votePubkey;
private final long activatedStake;
private final boolean epochVoteAccount;
private final int commission;
private final long lastVote;
private final long rootSlot;
private final List<List<Long>> epochCredits;

@JsonCreator
VoteAccountDTO(
final @JsonProperty("nodePubkey") String nodePubkey,
final @JsonProperty("votePubkey") String votePubkey,
final @JsonProperty("activatedStake") long activatedStake,
final @JsonProperty("epochVoteAccount") boolean epochVoteAccount,
final @JsonProperty("commission") int commission,
final @JsonProperty("lastVote") long lastVote,
final @JsonProperty("rootSlot") long rootSlot,
final @JsonProperty("epochCredits") List<List<Long>> epochCredits)
{
this.nodePubkey = nodePubkey;
this.votePubkey = votePubkey;
this.activatedStake = activatedStake;
this.epochVoteAccount = epochVoteAccount;
this.commission = commission;
this.lastVote = lastVote;
this.rootSlot = rootSlot;
this.epochCredits = epochCredits;
}

@Override
@JsonProperty("nodePubkey")
public String getNodePubkey()
{
return nodePubkey;
}

@Override
@JsonProperty("votePubkey")
public String getVotePubkey()
{
return votePubkey;
}

@Override
@JsonProperty("activatedStake")
public long getActivatedStake()
{
return activatedStake;
}

@Override
@JsonProperty("epochVoteAccount")
public boolean isEpochVoteAccount()
{
return epochVoteAccount;
}

@Override
@JsonProperty("commission")
public int getCommission()
{
return commission;
}

@Override
@JsonProperty("lastVote")
public long getLastVote()
{
return lastVote;
}

@Override
@JsonProperty("rootSlot")
public long getRootSlot()
{
return rootSlot;
}

@Override
@JsonProperty("epochCredits")
public List<List<Long>> getEpochCredits()
{
return epochCredits;
}

@Override
public String toString()
{
return "VoteAccountDTO{" +
"nodePubkey='" + nodePubkey + '\'' +
", votePubkey='" + votePubkey + '\'' +
", activatedStake=" + activatedStake +
", epochVoteAccount=" + epochVoteAccount +
", commission=" + commission +
", lastVote=" + lastVote +
", rootSlot=" + rootSlot +
", epochCredits=" + epochCredits +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.lmax.solana4j.client.jsonrpc;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.lmax.solana4j.client.api.VoteAccount;
import com.lmax.solana4j.client.api.VoteAccounts;

import java.util.List;

/**
* Data Transfer Object for the response from the getVoteAccounts RPC call.
*/
public class VoteAccountsDTO implements VoteAccounts
{
private final List<VoteAccount> current;
private final List<VoteAccount> delinquent;

@JsonCreator
VoteAccountsDTO(
final @JsonProperty("current") List<VoteAccountDTO> current,
final @JsonProperty("delinquent") List<VoteAccountDTO> delinquent)
{
this.current = List.copyOf(current);
this.delinquent = List.copyOf(delinquent);
}

@Override
@JsonProperty("current")
public List<VoteAccount> getCurrent()
{
return current;
}

@Override
@JsonProperty("delinquent")
public List<VoteAccount> getDelinquent()
{
return delinquent;
}

@Override
public String toString()
{
return "VoteAccountsDTO{" +
"current=" + current +
", delinquent=" + delinquent +
'}';
}
}
Loading
Loading