The Solana API can represent unsigned and signed transactions, but signing must remain separate from transaction construction and blockchain submission.
Issue #29 constructs an unsigned Jupiter Perps transaction. Before it can be submitted through SolanaBlockChain.sendTransaction(...), a wallet must authorize it by adding the required signature.
The wallet implementation must be able to sign any valid serialized Solana transaction for which the configured wallet is a required signer. It must not contain Jupiter-specific transaction logic.
The initial wallet implementation should be deliberately small. Its primary responsibility is converting:
SolanaUnsignedTransaction
into:
SolanaSignedTransaction
The implementation may use an external command, temporary files, standard input/output, or another local mechanism. These details must remain hidden behind the wallet API.
Goal
Introduce a minimal public Solana wallet interface.
Suggested package:
com.r35157.libs.solana.wallet
Suggested API:
publicinterfaceSolanaWallet{/**
* Returns the public address controlled by this wallet.
*
* @return the wallet's public Solana address
*/@NotNullΩSolanaWalletIdΩgetAddress();/**
* Returns the native SOL balance of this wallet.
*
* @return the wallet's native SOL balance
* @throws IOException if the balance cannot be fetched
* @throws InterruptedException if the calling thread is interrupted
*/@NotNullΩSolanaAmountΩgetSolanaBalance()throwsIOException,InterruptedException;/**
* Signs a complete unsigned Solana transaction.
*
* <p>This method does not submit the transaction to the blockchain.</p>
*
* @param transaction the unsigned transaction to sign
* @return the complete signed transaction
* @throws IOException if the transaction cannot be signed or decoded
* @throws InterruptedException if the signing process is interrupted
*/@NotNullSolanaSignedTransactionsignTransaction(@NotNullSolanaUnsignedTransactiontransaction)throwsIOException,InterruptedException;}
Initial implementation
Add a minimal reference implementation backed by the locally installed Jupiter CLI signer.
Suggested package:
com.r35157.libs.solana.wallet.impl.jupcli
Suggested class name:
JupiterCliSolanaWalletImpl
The implementation may internally invoke a command equivalent to:
These capabilities may be introduced later through other implementations or extensions.
Definition of done
The public SolanaWallet interface exists.
The interface contains getAddress(), getSolanaBalance(), and signTransaction(...).
A minimal Jupiter CLI-backed implementation exists.
The implementation signs an unsigned transaction using the configured local wallet.
A valid SolanaSignedTransaction is returned.
The returned signer address is validated against the configured wallet address.
getSolanaBalance() delegates to the existing Solana API.
Signing does not submit the transaction.
No private-key material is exposed through the Java API or logs.
The complete repository compiles.
## Background
The Solana API can represent unsigned and signed transactions, but signing must remain separate from transaction construction and blockchain submission.
Issue #29 constructs an unsigned Jupiter Perps transaction. Before it can be submitted through `SolanaBlockChain.sendTransaction(...)`, a wallet must authorize it by adding the required signature.
The wallet implementation must be able to sign any valid serialized Solana transaction for which the configured wallet is a required signer. It must not contain Jupiter-specific transaction logic.
The initial wallet implementation should be deliberately small. Its primary responsibility is converting:
```text
SolanaUnsignedTransaction
```
into:
```text
SolanaSignedTransaction
```
The implementation may use an external command, temporary files, standard input/output, or another local mechanism. These details must remain hidden behind the wallet API.
## Goal
Introduce a minimal public Solana wallet interface.
Suggested package:
```java
com.r35157.libs.solana.wallet
```
Suggested API:
```java
public interface SolanaWallet {
/**
* Returns the public address controlled by this wallet.
*
* @return the wallet's public Solana address
*/
@NotNull
ΩSolanaWalletIdΩ getAddress();
/**
* Returns the native SOL balance of this wallet.
*
* @return the wallet's native SOL balance
* @throws IOException if the balance cannot be fetched
* @throws InterruptedException if the calling thread is interrupted
*/
@NotNull
ΩSolanaAmountΩ getSolanaBalance()
throws IOException, InterruptedException;
/**
* Signs a complete unsigned Solana transaction.
*
* <p>This method does not submit the transaction to the blockchain.</p>
*
* @param transaction the unsigned transaction to sign
* @return the complete signed transaction
* @throws IOException if the transaction cannot be signed or decoded
* @throws InterruptedException if the signing process is interrupted
*/
@NotNull
SolanaSignedTransaction signTransaction(
@NotNull SolanaUnsignedTransaction transaction
) throws IOException, InterruptedException;
}
```
## Initial implementation
Add a minimal reference implementation backed by the locally installed Jupiter CLI signer.
Suggested package:
```java
com.r35157.libs.solana.wallet.impl.jupcli
```
Suggested class name:
```java
JupiterCliSolanaWalletImpl
```
The implementation may internally invoke a command equivalent to:
```bash
jup sign -f json --key <key-name> --tx <unsigned-base64>
```
The public Java API must not expose this implementation detail.
## Requirements
### Signing
* Accept a `SolanaUnsignedTransaction`.
* Sign its serialized Base64 transaction using the configured local wallet.
* Parse the machine-readable signer output.
* Return a `SolanaSignedTransaction`.
* Validate that the signer address returned by the external signer matches the wallet address configured for the implementation.
* Reject missing or blank unsigned transaction data.
* Report process failures and malformed signer output as meaningful Java exceptions.
* Do not submit the transaction.
* Do not modify or rebuild the transaction.
### Address
* `getAddress()` returns the public address controlled by the configured signing key.
* The public address must be available without exposing private-key material.
* The address should use the existing `ΩSolanaWalletIdΩ` ValueTag.
### Balance
* `getSolanaBalance()` delegates to:
```java
SolanaBlockChain.getBalanceInSolana(getAddress())
```
* The wallet implementation must not duplicate Solana RPC balance logic.
### External process isolation
* Private-key bytes must never be passed into or returned from the public Java API.
* Private-key contents must never be logged.
* The implementation may refer to an external signer key by a local alias such as:
```text
evelyn-prod
```
* Whether the unsigned transaction is passed through command arguments, standard input, or temporary files is an implementation detail.
* Temporary files must be removed after use when temporary files are used.
* Standard output used for machine parsing must not contain decorative human-readable formatting.
* Standard error should be preserved or included in exceptions when the signer process fails.
### Architecture
* `SolanaWallet` must not contain Jupiter Perps domain logic.
* The implementation may depend on `SolanaBlockChain` for read operations.
* The implementation must not call `SolanaBlockChain.sendTransaction(...)` from `signTransaction(...)`.
* Signing and submission must remain two separate explicit operations.
* The complete repository must continue to compile.
## Verification
Using the local `evelyn-prod` signer:
1. Construct an unsigned transaction through issue #29.
2. Pass it to `SolanaWallet.signTransaction(...)`.
3. Verify that the returned `SolanaSignedTransaction` is non-empty.
4. Verify that the signer address matches the expected wallet address.
5. Submit the result separately through `SolanaBlockChain.sendTransaction(...)`.
6. Verify that the expected Jupiter Perps position was changed.
Also verify that:
```java
wallet.getSolanaBalance()
```
returns the same value as:
```java
solanaBlockChain.getBalanceInSolana(wallet.getAddress())
```
## Out of scope
The following are not part of this issue:
* Creating or importing private keys
* Seed phrases
* Key encryption
* Password prompts
* Hardware wallets
* Multiple signatures
* Transaction submission
* Confirmation polling
* Browser-wallet integration
* A graphical wallet interface
* General token-balance APIs
* SOL transfers
* SPL token transfers
* A pure-Java cryptographic wallet implementation
These capabilities may be introduced later through other implementations or extensions.
## Definition of done
* The public `SolanaWallet` interface exists.
* The interface contains `getAddress()`, `getSolanaBalance()`, and `signTransaction(...)`.
* A minimal Jupiter CLI-backed implementation exists.
* The implementation signs an unsigned transaction using the configured local wallet.
* A valid `SolanaSignedTransaction` is returned.
* The returned signer address is validated against the configured wallet address.
* `getSolanaBalance()` delegates to the existing Solana API.
* Signing does not submit the transaction.
* No private-key material is exposed through the Java API or logs.
* The complete repository compiles.
Implemented the minimal Solana wallet API and reference implementation.
The wallet now:
exposes its public Solana address,
delegates native SOL balance lookup to SolanaBlockChain,
signs arbitrary serialized Solana transactions through the configured local Jupiter CLI key,
validates that the returned signer matches the configured wallet address,
returns a SolanaSignedTransaction without submitting it.
The implementation was verified using a real unsigned Jupiter Perps transaction. The transaction was signed successfully and later executed on-chain through Jupiter's transaction relay.
Implemented the minimal Solana wallet API and reference implementation.
The wallet now:
- exposes its public Solana address,
- delegates native SOL balance lookup to SolanaBlockChain,
- signs arbitrary serialized Solana transactions through the configured local Jupiter CLI key,
- validates that the returned signer matches the configured wallet address,
- returns a SolanaSignedTransaction without submitting it.
The implementation was verified using a real unsigned Jupiter Perps transaction. The transaction was signed successfully and later executed on-chain through Jupiter's transaction relay.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Background
The Solana API can represent unsigned and signed transactions, but signing must remain separate from transaction construction and blockchain submission.
Issue #29 constructs an unsigned Jupiter Perps transaction. Before it can be submitted through
SolanaBlockChain.sendTransaction(...), a wallet must authorize it by adding the required signature.The wallet implementation must be able to sign any valid serialized Solana transaction for which the configured wallet is a required signer. It must not contain Jupiter-specific transaction logic.
The initial wallet implementation should be deliberately small. Its primary responsibility is converting:
into:
The implementation may use an external command, temporary files, standard input/output, or another local mechanism. These details must remain hidden behind the wallet API.
Goal
Introduce a minimal public Solana wallet interface.
Suggested package:
Suggested API:
Initial implementation
Add a minimal reference implementation backed by the locally installed Jupiter CLI signer.
Suggested package:
Suggested class name:
The implementation may internally invoke a command equivalent to:
The public Java API must not expose this implementation detail.
Requirements
Signing
SolanaUnsignedTransaction.SolanaSignedTransaction.Address
getAddress()returns the public address controlled by the configured signing key.ΩSolanaWalletIdΩValueTag.Balance
getSolanaBalance()delegates to:External process isolation
Architecture
SolanaWalletmust not contain Jupiter Perps domain logic.SolanaBlockChainfor read operations.SolanaBlockChain.sendTransaction(...)fromsignTransaction(...).Verification
Using the local
evelyn-prodsigner:SolanaWallet.signTransaction(...).SolanaSignedTransactionis non-empty.SolanaBlockChain.sendTransaction(...).Also verify that:
returns the same value as:
Out of scope
The following are not part of this issue:
These capabilities may be introduced later through other implementations or extensions.
Definition of done
SolanaWalletinterface exists.getAddress(),getSolanaBalance(), andsignTransaction(...).SolanaSignedTransactionis returned.getSolanaBalance()delegates to the existing Solana API.Implemented the minimal Solana wallet API and reference implementation.
The wallet now:
The implementation was verified using a real unsigned Jupiter Perps transaction. The transaction was signed successfully and later executed on-chain through Jupiter's transaction relay.