Skip to content

Commit

Permalink
Add ability to deserialize both account indexes and account addresses…
Browse files Browse the repository at this point in the history
… from the same "account" field in the json (the fact you have to do this is just bad api design in my opinion)
  • Loading branch information
ml-james committed Jan 31, 2025
1 parent 113949d commit 49331b3
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,10 @@ void shouldGetTransactionJsonEncodingOptionalParam() throws SolanaJsonRpcClientE

final var instruction = message.getInstructions().get(0);
assertThat(instruction.getData()).isEqualTo("3Bxs3zzLZLuLQEYX");
assertThat(instruction.getAccounts()).hasSize(2);
assertThat(instruction.getAccounts().get(0)).isEqualTo(0);
assertThat(instruction.getAccounts().get(1)).isEqualTo(1);
assertThat(instruction.getAccounts().getIndexes()).hasSize(2);
assertThat(instruction.getAccounts().getAddresses()).isNull();
assertThat(instruction.getAccounts().getIndexes().get(0)).isEqualTo(0);
assertThat(instruction.getAccounts().getIndexes().get(1)).isEqualTo(1);
assertThat(instruction.getProgramIdIndex()).isEqualTo(2);
assertThat(instruction.getStackHeight()).isEqualTo(null);

Expand Down Expand Up @@ -164,6 +165,7 @@ void shouldGetTransactionJsonParsedEncodingOptionalParam() throws SolanaJsonRpcC

// would have been present if encoding json not jsonParsed
assertThat(instruction.getData()).isNull();
// jsonParsed so getAccounts().getIndexes() is null (as expected) but i _would_ expect getAccounts().getAddresses() to NOT be null ...
assertThat(instruction.getAccounts()).isNull();
assertThat(instruction.getProgramIdIndex()).isNull();
assertThat(instruction.getStackHeight()).isEqualTo(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,31 @@ interface Header
}
}

/**
* Represents an inner instruction in a Solana transaction.
* Inner instructions are instructions that are executed within a broader transaction and can involve program invocations.
* This interface provides access to the index of the inner instruction and the list of associated instructions.
*/
interface InnerInstruction
{

/**
* Returns the index of the inner instruction within the transaction.
* The index indicates the position of this inner instruction in the list of instructions in the transaction.
*
* @return the index of the inner instruction
*/
long getIndex();

/**
* Returns the list of instructions that are part of this inner instruction.
* Each instruction represents an action or a program invocation that is part of the inner transaction.
*
* @return a list of {@link Instruction} objects representing the inner instructions
*/
List<Instruction> getInstructions();
}

/**
* Represents an instruction in a Solana transaction.
* Instructions define actions to be executed on the Solana blockchain, specifying accounts involved,
Expand All @@ -499,12 +524,15 @@ interface Header
interface Instruction
{
/**
* Returns a list of account indices involved in the instruction.
* Each index refers to an account in the transaction's account list that is affected by this instruction.
* Retrieves information about the accounts involved in this instruction.
* Each account is referenced by its index within the transaction's account list
* and is associated with its corresponding Solana address.
*
* @return a list of integers representing account indices
* @return an {@link InstructionAccounts} instance containing the index and address
* of the affected account.
*/
List<Integer> getAccounts();
InstructionAccounts getAccounts();


/**
* Returns the base58-encoded data associated with the instruction.
Expand Down Expand Up @@ -563,31 +591,30 @@ interface Instruction
* @return the stack height as an {@link Integer}, or null if not specified
*/
Integer getStackHeight();
}


/**
* Represents an inner instruction in a Solana transaction.
* Inner instructions are instructions that are executed within a broader transaction and can involve program invocations.
* This interface provides access to the index of the inner instruction and the list of associated instructions.
*/
interface InnerInstruction
{

/**
* Returns the index of the inner instruction within the transaction.
* The index indicates the position of this inner instruction in the list of instructions in the transaction.
*
* @return the index of the inner instruction
* Represents an account involved in an instruction within a Solana transaction.
* This interface provides access to both the account index within the transaction's
* account list and its corresponding address.
*/
long getIndex();
interface InstructionAccounts
{
/**
* Retrieves the index of the account within the transaction's account list.
* This index is used to reference the account in the transaction's account array.
*
* @return the zero-based index of the account in the transaction's account list.
*/
List<Integer> getIndexes();

/**
* Retrieves the address of the account involved in the instruction.
* This is a Base58-encoded string representing the Solana account.
*
* @return the Solana account address as a string.
*/
List<String> getAddresses();
}

/**
* Returns the list of instructions that are part of this inner instruction.
* Each instruction represents an action or a program invocation that is part of the inner transaction.
*
* @return a list of {@link Instruction} objects representing the inner instructions
*/
List<Instruction> getInstructions();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,7 @@ public String toString()

static final class InstructionDTO implements Instruction
{
private final List<Integer> accounts;
private final InstructionAccountsDTO accounts;
private final Map<String, Object> instructionParsed;
private final String data;
private final String program;
Expand All @@ -701,7 +701,7 @@ static final class InstructionDTO implements Instruction

@JsonCreator
InstructionDTO(
final @JsonProperty("accounts") List<Integer> accounts,
final @JsonProperty("accounts") InstructionAccountsDTO accounts,
final @JsonProperty("parsed") Map<String, Object> instructionParsed,
final @JsonProperty("data") String data,
final @JsonProperty("program") String program,
Expand All @@ -719,7 +719,7 @@ static final class InstructionDTO implements Instruction
}

@Override
public List<Integer> getAccounts()
public InstructionAccounts getAccounts()
{
return accounts;
}
Expand Down Expand Up @@ -774,6 +774,59 @@ public String toString()
'}';
}
}


@JsonDeserialize(using = InstructionAccountsDTO.InstructionAccountsDeserializer.class)
static final class InstructionAccountsDTO implements Instruction.InstructionAccounts
{
private final List<Integer> indexes;
private final List<String> addresses;

InstructionAccountsDTO(final List<Integer> indexes, final List<String> addresses)
{
this.indexes = indexes;
this.addresses = addresses;
}

@Override
public List<Integer> getIndexes()
{
return indexes;
}

@Override
public List<String> getAddresses()
{
return addresses;
}

static class InstructionAccountsDeserializer extends JsonDeserializer<InstructionAccountsDTO>
{
@Override
public InstructionAccountsDTO deserialize(final JsonParser parser, final DeserializationContext context) throws IOException
{
final ObjectMapper mapper = (ObjectMapper) parser.getCodec();
final JsonNode node = mapper.readTree(parser);

if (node.isArray())
{
if (!node.isEmpty() && node.get(0).isTextual())
{
final List<String> addresses = mapper.convertValue(node, mapper.getTypeFactory().constructCollectionType(List.class, String.class));
return new InstructionAccountsDTO(null, addresses);
}
else
{
final List<Integer> indexes = mapper.convertValue(node, mapper.getTypeFactory().constructCollectionType(List.class, Integer.class));
return new InstructionAccountsDTO(indexes, null);
}
}

throw new IOException("Unable to deserialize instruction accounts.");
}
}
}

}

static final class LoadedAddressesDTO implements TransactionMetadata.LoadedAddresses
Expand Down

0 comments on commit 49331b3

Please sign in to comment.