Add generic awaitTransaction() support to SolanaBlockChain #67

Closed
opened 2026-08-11 10:32:50 +02:00 by minimons · 0 comments
Owner

Background

SolanaBlockChain.sendTransaction() currently submits a signed transaction and returns the signature accepted by the Solana RPC server.

However, a successful response from sendTransaction() only means that the RPC server has received the transaction. It does not necessarily mean that the transaction has been processed, confirmed, or finalized on-chain.

We therefore need a generic blocking operation that can subsequently wait for the outcome of an already submitted transaction.

This functionality will be needed by, among others:

  • the upcoming SPL token burn functionality, which must be able to wait for FINALIZED
  • the upcoming EvelynBurnerService, which must not send a Discord notification until the transaction is FINALIZED
  • JupiterSwapService, which may later use CONFIRMED as an independent on-chain confirmation
  • Jupiter Perps increase/decrease operations, which may later wait for CONFIRMED inside the Perps service
  • other callers that choose to wait after an ordinary wallet transfer

The starting point for this task is commit:

e7f551b8dd4cfe364a15a13956336cfea98ad7c8

Objective

Extend:

com.r35157.libs.solana.SolanaBlockChain

with a generic method that polls Solana's getSignatureStatuses RPC method until the transaction:

  • succeeds at the requested commitment level
  • fails at the requested commitment level
  • or the specified timeout expires

sendTransaction() and awaitTransaction() must remain separate operations:

sendTransaction()
    -> submits the transaction and returns its signature

awaitTransaction()
    -> subsequently waits for the requested on-chain outcome

A combined sendAndAwaitTransaction() method must not be added as part of this task.

Public API

Add the following method to SolanaBlockChain:

SolanaTransactionOutcome awaitTransaction(
        ΩSolanaTransactionSignatureΩ signature,
        SolanaCommitment commitment,
        Duration timeout
) throws IOException, InterruptedException;

The method and the new public types must include comprehensive Javadoc, including the important meaning of TIMED_OUT: the transaction outcome remains unknown, and the transaction must not automatically be resubmitted.

SolanaCommitment

Add the following public enum in:

com.r35157.libs.solana
public enum SolanaCommitment {
    PROCESSED,
    CONFIRMED,
    FINALIZED
}

The commitment levels are ordered as follows:

PROCESSED < CONFIRMED < FINALIZED

An observed commitment level therefore also satisfies any lower requested commitment level.

SolanaTransactionOutcome

Add the following public record in:

com.r35157.libs.solana
public record SolanaTransactionOutcome(
        Status status,
        ΩSolanaSlotΩ slot,
        String failureDetails
) {
    public enum Status {
        SUCCEEDED,
        FAILED,
        TIMED_OUT
    }
}

The record constructor must enforce the following invariants:

Status slot failureDetails
SUCCEEDED Must contain the transaction slot and must not be null Must be null
FAILED Must contain the transaction slot and must not be null Must contain Solana's err value as compact JSON and must not be blank
TIMED_OUT Must be null Must be null

If the method times out, it must not return a slot that may have been observed earlier at a lower commitment level. The result describes the outcome at the commitment level requested by the caller.

New ValueTag

Add the following ValueTag below Long in conf/detag.conf:

Long
    SolanaSlot

This generates:

ΩSolanaSlotΩ

The backing type must be Long, allowing slot to be null for a TIMED_OUT outcome.

SolanaSlot is a distinct domain concept and must not be replaced with another existing Long-based ValueTag.

Commitment and outcome semantics

SUCCEEDED means:

  • the transaction was found
  • it reached or exceeded the requested commitment level
  • Solana returned err: null

FAILED means:

  • the transaction was found
  • it reached or exceeded the requested commitment level
  • Solana returned an execution error in err

TIMED_OUT means:

  • the requested definitive outcome was not known before the deadline
  • the transaction may still later be confirmed, finalized, or dropped
  • a timeout must not be interpreted as a rejected or failed transaction
  • the caller must therefore not automatically submit an equivalent transaction again

Both successful and failed transactions must reach the commitment level requested by the caller before a definitive outcome is returned.

Examples:

Observed status Requested commitment err Action
processed PROCESSED null Return SUCCEEDED
confirmed PROCESSED null Return SUCCEEDED
processed CONFIRMED null Continue polling
confirmed CONFIRMED null Return SUCCEEDED
confirmed FINALIZED null Continue polling
finalized FINALIZED null Return SUCCEEDED
processed FINALIZED execution error Continue polling
finalized FINALIZED execution error Return FAILED

An execution error observed at PROCESSED must therefore not be returned as FAILED if the caller requested FINALIZED. The block containing the failed transaction must first reach the requested commitment level.

RPC implementation

The first version must use polling through Solana's HTTP JSON-RPC method:

getSignatureStatuses

WebSocket support and signatureSubscribe are explicitly outside the scope of this task.

Each status request must be equivalent to:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "getSignatureStatuses",
  "params": [
    [
      "<signature>"
    ],
    {
      "searchTransactionHistory": true
    }
  ]
}

searchTransactionHistory must be true because awaitTransaction() accepts an existing signature and must also be able to locate transactions that are no longer available in the RPC server's recent status cache.

The following fields must be read from:

result.value[0].slot
result.value[0].err
result.value[0].confirmationStatus

result.context and the confirmations and status fields must not be used.

Interpreting the RPC response

If:

result.value[0] == null

the signature has not yet been found. Polling must continue until the deadline expires.

If a status object is present:

  1. slot must be a valid integer representable as a Java Long.

  2. confirmationStatus must be a string containing one of the following values:

    processed
    confirmed
    finalized
    
  3. The observed commitment level must be compared with the requested commitment level.

  4. Polling must continue if the requested commitment level has not yet been reached.

  5. Once the requested commitment level has been reached or exceeded:

    • err == null results in SUCCEEDED
    • err != null results in FAILED

For FAILED, the complete err value must be stored as compact JSON in failureDetails. This generic blockchain class must not attempt to interpret domain-specific error codes from individual Solana programs.

A missing or unknown confirmationStatus must be treated as an invalid RPC response and result in an IOException.

Polling and existing RPC throttling

SolanaBlockChainImpl already contains a shared, synchronized sendThrottled() mechanism that ensures at least five seconds between Solana RPC calls.

awaitTransaction() must use the existing shared throttling mechanism. It must not introduce:

  • a separate polling interval
  • a separate rate limiter
  • an additional Thread.sleep() after each status request

The first status request must be performed as soon as the shared throttling mechanism permits. After an inconclusive response, the next iteration will naturally be limited by the existing five-second throttling.

This also means that other concurrent RPC operations using the same SolanaBlockChainImpl may delay the status checks. This waiting time is part of the awaitTransaction() timeout.

Timeout and deadline

The timeout must define one overall deadline for the entire method call. It includes:

  • waiting for access to the shared RPC gate
  • any remaining throttling delay
  • the HTTP request itself
  • all subsequent status checks

The deadline must be measured using a monotonic time source, such as System.nanoTime(), so changes to the system clock cannot affect the timeout.

The implementation must not start a new RPC request after the deadline has been reached.

If the timeout expires before the next status request can be performed, return:

new SolanaTransactionOutcome(
        SolanaTransactionOutcome.Status.TIMED_OUT,
        null,
        null
)

If the remaining time is shorter than the normal throttling delay, the implementation must wait only until the deadline and must not subsequently perform the RPC request.

An ongoing HTTP request must, as far as reasonably possible, be limited by the remaining time before the overall deadline. If this deadline expires during the status request, the result is TIMED_OUT, not an ordinary communication failure.

Interruptible and deadline-aware RPC gate

The current synchronized sendThrottled() implementation makes monitor acquisition non-interruptible and may block beyond the awaitTransaction() deadline if another thread is performing an RPC request.

The internal throttling mechanism or RPC gate must therefore be adjusted so awaitTransaction() can:

  • be interrupted while waiting for access to the RPC gate
  • stop waiting when its deadline is reached
  • avoid starting an RPC request after the deadline

The implementation may, for example, use an interruptible and time-bounded locking mechanism, but the specific internal solution is not part of the public API.

Existing Solana RPC operations must preserve their current five-second throttling and public behavior.

Argument validation

The following must be validated before the first RPC request:

  • signature must not be null or blank
  • the signature must be valid Base58
  • the decoded signature must be exactly 64 bytes
  • commitment must not be null
  • timeout must not be null
  • timeout must be strictly greater than zero

Invalid arguments must be rejected locally and must not cause an RPC request.

The project's existing conventions may be used when choosing between NullPointerException and IllegalArgumentException, but the behavior must be documented consistently in the API Javadoc.

Error model

FAILED must only represent an on-chain execution error reported in:

result.value[0].err

The following conditions must result in an IOException:

  • a non-success HTTP status
  • a JSON-RPC error
  • invalid JSON
  • missing or invalid result.value
  • a result.value array containing anything other than exactly one element
  • a non-null status object without a valid slot
  • a missing or unknown confirmationStatus
  • any other network or communication failure

Error messages must mention getSignatureStatuses and include relevant HTTP, RPC, or response context.

The method must not automatically retry after an IOException. A caller can safely invoke awaitTransaction() again with the same signature because doing so does not resubmit the transaction.

The following conditions are not FAILED:

  • a signature that the RPC server cannot find yet
  • a dropped transaction
  • a transaction that may have expired
  • a transaction that does not reach the requested commitment level before the timeout
  • a communication failure

Using only the transaction signature, the method cannot prove whether an unseen transaction never existed, may still land, was dropped, or expired because of its blockhash. If no communication failure occurs, these situations must therefore result in TIMED_OUT.

InterruptedException

If the calling Java thread is interrupted while:

  • waiting for access to the RPC gate
  • waiting for throttling
  • performing the HTTP request
  • performing any other internal wait

the InterruptedException must propagate directly.

An interruption must not:

  • be converted into TIMED_OUT
  • be wrapped in an IOException
  • be ignored
  • result in a subsequent RPC request

CachedSolanaBlockChain

Add the same method to:

com.r35157.libs.solana.impl.cached.CachedSolanaBlockChain

The implementation must delegate directly:

@Override
public SolanaTransactionOutcome awaitTransaction(
        ΩSolanaTransactionSignatureΩ signature,
        SolanaCommitment commitment,
        Duration timeout
) throws IOException, InterruptedException {
    return delegate.awaitTransaction(signature, commitment, timeout);
}

The outcome must not be:

  • cached
  • reused between calls
  • deduplicated
  • determined from previously cached blockchain data

Every call must be delegated directly to the underlying implementation.

Tests and verification

Add focused automated tests covering at least the following cases:

  • PROCESSED succeeds when PROCESSED is requested
  • CONFIRMED satisfies a requested PROCESSED
  • PROCESSED continues polling when CONFIRMED is requested
  • CONFIRMED continues polling when FINALIZED is requested
  • FINALIZED succeeds when FINALIZED is requested
  • an execution error returns FAILED with the slot and compact err JSON
  • an execution error at a lower commitment level continues polling until the requested commitment level is reached
  • result.value[0] == null continues polling
  • a timeout returns the required null fields
  • the deadline can expire while waiting for RPC throttling
  • interruption propagates without a subsequent RPC request
  • a JSON-RPC error results in IOException
  • a non-success HTTP status results in IOException
  • a malformed or incomplete response results in IOException
  • an unknown confirmationStatus results in IOException
  • an invalid signature or timeout is rejected without an RPC request
  • CachedSolanaBlockChain delegates every call without caching

The tests must not contact a real Solana RPC server or wait for five actual seconds. If necessary, introduce a narrow internal test seam for transport, time, and throttling without expanding the public API.

The normal Gradle build and all relevant tests must pass.

Scope

This task includes only:

  • SolanaCommitment
  • SolanaTransactionOutcome
  • the ΩSolanaSlotΩ ValueTag
  • SolanaBlockChain.awaitTransaction()
  • the polling implementation in SolanaBlockChainImpl
  • the necessary deadline-aware adjustment of the existing internal RPC throttling
  • direct delegation in CachedSolanaBlockChain
  • relevant tests and documentation

This task must not modify:

  • SolanaBlockChain.sendTransaction()
  • SolanaWallet or wallet transfers
  • JupiterSwapService
  • Jupiter Perps increase/decrease operations
  • SPL token burn functionality
  • EvelynBurnerService
  • Discord integration
  • existing domain-specific validation of Jupiter responses or actual token amounts

These components will become separate consumers of the new API in subsequent tasks.

Acceptance criteria

  • SolanaBlockChain exposes the described awaitTransaction() API.
  • All three commitment levels are supported.
  • Both successful transactions and execution failures must reach the requested commitment level.
  • Polling uses getSignatureStatuses.
  • searchTransactionHistory is always true.
  • The existing five-second Solana RPC throttling is reused.
  • The overall timeout includes the RPC gate, throttling, and HTTP requests.
  • Waiting for the RPC gate can both be interrupted and terminated by the deadline.
  • SUCCEEDED, FAILED, and TIMED_OUT follow the described invariants.
  • FAILED is used only for Solana's on-chain err.
  • TIMED_OUT represents an unknown outcome and does not cause resubmission.
  • Communication and protocol errors result in IOException.
  • Interruption propagates as InterruptedException.
  • CachedSolanaBlockChain delegates without caching.
  • No existing caller is changed to wait automatically.
  • No WebSocket or automatic retry mechanism is added.
  • The Gradle build and relevant tests pass.

References

## Background `SolanaBlockChain.sendTransaction()` currently submits a signed transaction and returns the signature accepted by the Solana RPC server. However, a successful response from `sendTransaction()` only means that the RPC server has received the transaction. It does not necessarily mean that the transaction has been processed, confirmed, or finalized on-chain. We therefore need a generic blocking operation that can subsequently wait for the outcome of an already submitted transaction. This functionality will be needed by, among others: - the upcoming SPL token burn functionality, which must be able to wait for `FINALIZED` - the upcoming `EvelynBurnerService`, which must not send a Discord notification until the transaction is `FINALIZED` - `JupiterSwapService`, which may later use `CONFIRMED` as an independent on-chain confirmation - Jupiter Perps increase/decrease operations, which may later wait for `CONFIRMED` inside the Perps service - other callers that choose to wait after an ordinary wallet transfer The starting point for this task is commit: ```text e7f551b8dd4cfe364a15a13956336cfea98ad7c8 ``` ## Objective Extend: ```text com.r35157.libs.solana.SolanaBlockChain ``` with a generic method that polls Solana's `getSignatureStatuses` RPC method until the transaction: - succeeds at the requested commitment level - fails at the requested commitment level - or the specified timeout expires `sendTransaction()` and `awaitTransaction()` must remain separate operations: ```text sendTransaction() -> submits the transaction and returns its signature awaitTransaction() -> subsequently waits for the requested on-chain outcome ``` A combined `sendAndAwaitTransaction()` method must not be added as part of this task. ## Public API Add the following method to `SolanaBlockChain`: ```java SolanaTransactionOutcome awaitTransaction( ΩSolanaTransactionSignatureΩ signature, SolanaCommitment commitment, Duration timeout ) throws IOException, InterruptedException; ``` The method and the new public types must include comprehensive Javadoc, including the important meaning of `TIMED_OUT`: the transaction outcome remains unknown, and the transaction must not automatically be resubmitted. ### `SolanaCommitment` Add the following public enum in: ```text com.r35157.libs.solana ``` ```java public enum SolanaCommitment { PROCESSED, CONFIRMED, FINALIZED } ``` The commitment levels are ordered as follows: ```text PROCESSED < CONFIRMED < FINALIZED ``` An observed commitment level therefore also satisfies any lower requested commitment level. ### `SolanaTransactionOutcome` Add the following public record in: ```text com.r35157.libs.solana ``` ```java public record SolanaTransactionOutcome( Status status, ΩSolanaSlotΩ slot, String failureDetails ) { public enum Status { SUCCEEDED, FAILED, TIMED_OUT } } ``` The record constructor must enforce the following invariants: | Status | `slot` | `failureDetails` | |---|---|---| | `SUCCEEDED` | Must contain the transaction slot and must not be `null` | Must be `null` | | `FAILED` | Must contain the transaction slot and must not be `null` | Must contain Solana's `err` value as compact JSON and must not be blank | | `TIMED_OUT` | Must be `null` | Must be `null` | If the method times out, it must not return a slot that may have been observed earlier at a lower commitment level. The result describes the outcome at the commitment level requested by the caller. ## New ValueTag Add the following ValueTag below `Long` in `conf/detag.conf`: ```text Long SolanaSlot ``` This generates: ```java ΩSolanaSlotΩ ``` The backing type must be `Long`, allowing `slot` to be `null` for a `TIMED_OUT` outcome. `SolanaSlot` is a distinct domain concept and must not be replaced with another existing `Long`-based ValueTag. ## Commitment and outcome semantics `SUCCEEDED` means: - the transaction was found - it reached or exceeded the requested commitment level - Solana returned `err: null` `FAILED` means: - the transaction was found - it reached or exceeded the requested commitment level - Solana returned an execution error in `err` `TIMED_OUT` means: - the requested definitive outcome was not known before the deadline - the transaction may still later be confirmed, finalized, or dropped - a timeout must not be interpreted as a rejected or failed transaction - the caller must therefore not automatically submit an equivalent transaction again Both successful and failed transactions must reach the commitment level requested by the caller before a definitive outcome is returned. Examples: | Observed status | Requested commitment | `err` | Action | |---|---|---|---| | `processed` | `PROCESSED` | `null` | Return `SUCCEEDED` | | `confirmed` | `PROCESSED` | `null` | Return `SUCCEEDED` | | `processed` | `CONFIRMED` | `null` | Continue polling | | `confirmed` | `CONFIRMED` | `null` | Return `SUCCEEDED` | | `confirmed` | `FINALIZED` | `null` | Continue polling | | `finalized` | `FINALIZED` | `null` | Return `SUCCEEDED` | | `processed` | `FINALIZED` | execution error | Continue polling | | `finalized` | `FINALIZED` | execution error | Return `FAILED` | An execution error observed at `PROCESSED` must therefore not be returned as `FAILED` if the caller requested `FINALIZED`. The block containing the failed transaction must first reach the requested commitment level. ## RPC implementation The first version must use polling through Solana's HTTP JSON-RPC method: ```text getSignatureStatuses ``` WebSocket support and `signatureSubscribe` are explicitly outside the scope of this task. Each status request must be equivalent to: ```json { "jsonrpc": "2.0", "id": 1, "method": "getSignatureStatuses", "params": [ [ "<signature>" ], { "searchTransactionHistory": true } ] } ``` `searchTransactionHistory` must be `true` because `awaitTransaction()` accepts an existing signature and must also be able to locate transactions that are no longer available in the RPC server's recent status cache. The following fields must be read from: ```text result.value[0].slot result.value[0].err result.value[0].confirmationStatus ``` `result.context` and the `confirmations` and `status` fields must not be used. ### Interpreting the RPC response If: ```text result.value[0] == null ``` the signature has not yet been found. Polling must continue until the deadline expires. If a status object is present: 1. `slot` must be a valid integer representable as a Java `Long`. 2. `confirmationStatus` must be a string containing one of the following values: ```text processed confirmed finalized ``` 3. The observed commitment level must be compared with the requested commitment level. 4. Polling must continue if the requested commitment level has not yet been reached. 5. Once the requested commitment level has been reached or exceeded: - `err == null` results in `SUCCEEDED` - `err != null` results in `FAILED` For `FAILED`, the complete `err` value must be stored as compact JSON in `failureDetails`. This generic blockchain class must not attempt to interpret domain-specific error codes from individual Solana programs. A missing or unknown `confirmationStatus` must be treated as an invalid RPC response and result in an `IOException`. ## Polling and existing RPC throttling `SolanaBlockChainImpl` already contains a shared, synchronized `sendThrottled()` mechanism that ensures at least five seconds between Solana RPC calls. `awaitTransaction()` must use the existing shared throttling mechanism. It must not introduce: - a separate polling interval - a separate rate limiter - an additional `Thread.sleep()` after each status request The first status request must be performed as soon as the shared throttling mechanism permits. After an inconclusive response, the next iteration will naturally be limited by the existing five-second throttling. This also means that other concurrent RPC operations using the same `SolanaBlockChainImpl` may delay the status checks. This waiting time is part of the `awaitTransaction()` timeout. ## Timeout and deadline The timeout must define one overall deadline for the entire method call. It includes: - waiting for access to the shared RPC gate - any remaining throttling delay - the HTTP request itself - all subsequent status checks The deadline must be measured using a monotonic time source, such as `System.nanoTime()`, so changes to the system clock cannot affect the timeout. The implementation must not start a new RPC request after the deadline has been reached. If the timeout expires before the next status request can be performed, return: ```java new SolanaTransactionOutcome( SolanaTransactionOutcome.Status.TIMED_OUT, null, null ) ``` If the remaining time is shorter than the normal throttling delay, the implementation must wait only until the deadline and must not subsequently perform the RPC request. An ongoing HTTP request must, as far as reasonably possible, be limited by the remaining time before the overall deadline. If this deadline expires during the status request, the result is `TIMED_OUT`, not an ordinary communication failure. ### Interruptible and deadline-aware RPC gate The current synchronized `sendThrottled()` implementation makes monitor acquisition non-interruptible and may block beyond the `awaitTransaction()` deadline if another thread is performing an RPC request. The internal throttling mechanism or RPC gate must therefore be adjusted so `awaitTransaction()` can: - be interrupted while waiting for access to the RPC gate - stop waiting when its deadline is reached - avoid starting an RPC request after the deadline The implementation may, for example, use an interruptible and time-bounded locking mechanism, but the specific internal solution is not part of the public API. Existing Solana RPC operations must preserve their current five-second throttling and public behavior. ## Argument validation The following must be validated before the first RPC request: - `signature` must not be `null` or blank - the signature must be valid Base58 - the decoded signature must be exactly 64 bytes - `commitment` must not be `null` - `timeout` must not be `null` - `timeout` must be strictly greater than zero Invalid arguments must be rejected locally and must not cause an RPC request. The project's existing conventions may be used when choosing between `NullPointerException` and `IllegalArgumentException`, but the behavior must be documented consistently in the API Javadoc. ## Error model `FAILED` must only represent an on-chain execution error reported in: ```text result.value[0].err ``` The following conditions must result in an `IOException`: - a non-success HTTP status - a JSON-RPC `error` - invalid JSON - missing or invalid `result.value` - a `result.value` array containing anything other than exactly one element - a non-null status object without a valid `slot` - a missing or unknown `confirmationStatus` - any other network or communication failure Error messages must mention `getSignatureStatuses` and include relevant HTTP, RPC, or response context. The method must not automatically retry after an `IOException`. A caller can safely invoke `awaitTransaction()` again with the same signature because doing so does not resubmit the transaction. The following conditions are not `FAILED`: - a signature that the RPC server cannot find yet - a dropped transaction - a transaction that may have expired - a transaction that does not reach the requested commitment level before the timeout - a communication failure Using only the transaction signature, the method cannot prove whether an unseen transaction never existed, may still land, was dropped, or expired because of its blockhash. If no communication failure occurs, these situations must therefore result in `TIMED_OUT`. ## `InterruptedException` If the calling Java thread is interrupted while: - waiting for access to the RPC gate - waiting for throttling - performing the HTTP request - performing any other internal wait the `InterruptedException` must propagate directly. An interruption must not: - be converted into `TIMED_OUT` - be wrapped in an `IOException` - be ignored - result in a subsequent RPC request ## `CachedSolanaBlockChain` Add the same method to: ```text com.r35157.libs.solana.impl.cached.CachedSolanaBlockChain ``` The implementation must delegate directly: ```java @Override public SolanaTransactionOutcome awaitTransaction( ΩSolanaTransactionSignatureΩ signature, SolanaCommitment commitment, Duration timeout ) throws IOException, InterruptedException { return delegate.awaitTransaction(signature, commitment, timeout); } ``` The outcome must not be: - cached - reused between calls - deduplicated - determined from previously cached blockchain data Every call must be delegated directly to the underlying implementation. ## Tests and verification Add focused automated tests covering at least the following cases: - `PROCESSED` succeeds when `PROCESSED` is requested - `CONFIRMED` satisfies a requested `PROCESSED` - `PROCESSED` continues polling when `CONFIRMED` is requested - `CONFIRMED` continues polling when `FINALIZED` is requested - `FINALIZED` succeeds when `FINALIZED` is requested - an execution error returns `FAILED` with the slot and compact `err` JSON - an execution error at a lower commitment level continues polling until the requested commitment level is reached - `result.value[0] == null` continues polling - a timeout returns the required `null` fields - the deadline can expire while waiting for RPC throttling - interruption propagates without a subsequent RPC request - a JSON-RPC error results in `IOException` - a non-success HTTP status results in `IOException` - a malformed or incomplete response results in `IOException` - an unknown `confirmationStatus` results in `IOException` - an invalid signature or timeout is rejected without an RPC request - `CachedSolanaBlockChain` delegates every call without caching The tests must not contact a real Solana RPC server or wait for five actual seconds. If necessary, introduce a narrow internal test seam for transport, time, and throttling without expanding the public API. The normal Gradle build and all relevant tests must pass. ## Scope This task includes only: - `SolanaCommitment` - `SolanaTransactionOutcome` - the `ΩSolanaSlotΩ` ValueTag - `SolanaBlockChain.awaitTransaction()` - the polling implementation in `SolanaBlockChainImpl` - the necessary deadline-aware adjustment of the existing internal RPC throttling - direct delegation in `CachedSolanaBlockChain` - relevant tests and documentation This task must not modify: - `SolanaBlockChain.sendTransaction()` - `SolanaWallet` or wallet transfers - `JupiterSwapService` - Jupiter Perps increase/decrease operations - SPL token burn functionality - `EvelynBurnerService` - Discord integration - existing domain-specific validation of Jupiter responses or actual token amounts These components will become separate consumers of the new API in subsequent tasks. ## Acceptance criteria - [ ] `SolanaBlockChain` exposes the described `awaitTransaction()` API. - [ ] All three commitment levels are supported. - [ ] Both successful transactions and execution failures must reach the requested commitment level. - [ ] Polling uses `getSignatureStatuses`. - [ ] `searchTransactionHistory` is always `true`. - [ ] The existing five-second Solana RPC throttling is reused. - [ ] The overall timeout includes the RPC gate, throttling, and HTTP requests. - [ ] Waiting for the RPC gate can both be interrupted and terminated by the deadline. - [ ] `SUCCEEDED`, `FAILED`, and `TIMED_OUT` follow the described invariants. - [ ] `FAILED` is used only for Solana's on-chain `err`. - [ ] `TIMED_OUT` represents an unknown outcome and does not cause resubmission. - [ ] Communication and protocol errors result in `IOException`. - [ ] Interruption propagates as `InterruptedException`. - [ ] `CachedSolanaBlockChain` delegates without caching. - [ ] No existing caller is changed to wait automatically. - [ ] No WebSocket or automatic retry mechanism is added. - [ ] The Gradle build and relevant tests pass. ## References - Solana `sendTransaction`: https://solana.com/docs/rpc/http/sendtransaction - Solana `getSignatureStatuses`: https://solana.com/docs/rpc/http/getsignaturestatuses - Solana commitment levels: https://docs.anza.xyz/consensus/commitments - Solana transaction confirmation and expiration: https://solana.com/developers/cookbook/transactions/confirmation
minimons added the enhancement label 2026-08-11 10:32:50 +02:00
minimons self-assigned this 2026-08-11 10:32:50 +02:00
minimons added this to the AssetAZ project 2026-08-11 10:32:50 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: r35157/com_r35157_nenjim-hubd-impl_ref#67