Await CONFIRMED on-chain outcomes for Jupiter Swap and Perps operations #68

Closed
opened 2026-08-11 11:55:04 +02:00 by minimons · 0 comments
Owner

Background

Issue #67 added the generic:

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

to SolanaBlockChain.

The current Jupiter services submit signed transactions through Jupiter-managed execution endpoints, but they do not independently verify the resulting transaction through SolanaBlockChain.awaitTransaction():

  • JupiterSwapServiceImpl trusts a successful Jupiter Swap V2 /execute response.
  • AnchorIdlJupiterPerpsServiceImpl returns as soon as Jupiter's Perps execution endpoint returns a transaction signature.

The new blockchain functionality should now be reused at these high-level operation boundaries. A Jupiter Swap or Perps operation must not report normal success until the returned transaction signature has independently reached CONFIRMED on Solana.

The starting point for this task is the followg Gitea commit:

cb2d3e2a164c8fb489d3ed4dae780eb2380a5e26

Objective

Integrate SolanaBlockChain.awaitTransaction() into:

com.r35157.libs.jupiter.swap.impl.ref.JupiterSwapServiceImpl
com.r35157.libs.jupiter.perps.impl.anchoridl.AnchorIdlJupiterPerpsServiceImpl

Both services must:

  1. perform their existing build, validation, signing and single submission flow
  2. obtain and validate the Solana transaction signature returned by Jupiter
  3. call SolanaBlockChain.awaitTransaction() for that signature
  4. request SolanaCommitment.CONFIRMED
  5. return their existing normal success value only when the outcome is SUCCEEDED
  6. expose FAILED and TIMED_OUT without losing the transaction signature or conflating an unknown outcome with an on-chain failure

No transaction may be automatically resubmitted.

Why the integration belongs in the Jupiter services

JupiterSwapService.swap(), JupiterPerpsService.executePositionIncrease() and JupiterPerpsService.executePositionDecrease() represent complete high-level operations. Their implementations already own transaction construction, signing orchestration and submission through Jupiter.

Independent on-chain confirmation is therefore part of completing those operations and must remain inside the respective Jupiter service implementations. Alarm actions and future Evelyn services must not duplicate Jupiter transaction orchestration.

The lower-level SolanaWallet submission methods must remain unchanged. Methods such as sendSolana(), sendSPLToken(), sendTransaction() and signAndSendTransaction() continue to submit and return a signature without automatically waiting. Callers of those lower-level methods may explicitly invoke SolanaBlockChain.awaitTransaction() when their own domain policy requires it.

Public outcome exception

Add a checked public exception shared by the Jupiter Swap and Perps APIs:

com.r35157.libs.jupiter.JupiterTransactionOutcomeException

The exception must contain structured access to:

ΩSolanaTransactionSignatureΩ transactionSignature()
SolanaCommitment requestedCommitment()
SolanaTransactionOutcome outcome()

The exact implementation may use ordinary accessor methods rather than record-style methods if that better matches the project's exception conventions.

The constructor must enforce:

  • the transaction signature is not null or blank
  • the requested commitment is not null
  • the outcome is not null
  • the outcome status is either FAILED or TIMED_OUT
  • a SUCCEEDED outcome is rejected because it is not exceptional

The exception message must include:

  • the transaction signature
  • the requested commitment
  • whether the transaction definitively failed or remains unknown
  • the slot and compact Solana err JSON for FAILED
  • an explicit warning for TIMED_OUT that the transaction outcome is unknown and an equivalent transaction must not be automatically resubmitted

The exception and its accessors must have comprehensive Javadoc.

Add JupiterTransactionOutcomeException to the declared checked exceptions of:

JupiterSwapResult swap(...)

ΩSolanaTransactionSignatureΩ executePositionIncrease(...)

ΩSolanaTransactionSignatureΩ executePositionDecrease(...)

The normal return types remain unchanged. They are returned only after independently observed CONFIRMED success.

Confirmation policy and timeout

Both Jupiter service implementations must use:

SolanaCommitment.CONFIRMED

The confirmation timeout is an operation policy belonging to the high-level service, not to SolanaWallet and not to the alarm action.

Add a constructor overload to each implementation that accepts a Duration transactionConfirmationTimeout:

JupiterSwapServiceImpl
AnchorIdlJupiterPerpsServiceImpl

Requirements:

  • the timeout must not be null
  • the timeout must be strictly greater than zero
  • invalid values must fail during construction
  • existing constructors must remain available and delegate to the new overload
  • the default timeout must be two minutes
  • no configuration-file format change is part of this task

The timeout passed to awaitTransaction() starts when the service begins awaiting the already submitted signature. Time spent obtaining the Jupiter order or position transaction, signing it and submitting it is not part of this confirmation timeout.

Jupiter Swap integration

Preserve the complete existing Jupiter Swap V2 flow and validation.

After /execute has returned a response that passes the existing validation for:

  • success status
  • result code 0
  • non-blank transaction signature
  • positive actual input amount
  • positive actual output amount

the implementation must call:

solanaBlockChain.awaitTransaction(
        result.transactionSignature(),
        SolanaCommitment.CONFIRMED,
        transactionConfirmationTimeout
);

Handle the outcome as follows:

Outcome Required behavior
SUCCEEDED Return the already validated JupiterSwapResult.
FAILED Throw JupiterTransactionOutcomeException containing the signature, CONFIRMED and the complete outcome.
TIMED_OUT Throw JupiterTransactionOutcomeException containing the signature, CONFIRMED and the complete outcome.

The independent Solana check supplements rather than replaces Jupiter's existing /execute response validation. Existing validation of Jupiter's reported actual token amounts must remain unchanged.

If Jupiter's execution request fails before a valid transaction signature is received, the existing unknown-outcome behavior remains unchanged because there is no verified signature that can be passed to awaitTransaction().

Jupiter Perps integration

Preserve the existing increase and decrease flows through Jupiter's Perps transaction execution endpoint.

After the execution response has produced a valid non-blank txid, both:

executePositionIncrease(...)
executePositionDecrease(...)

must await that signature at CONFIRMED using the configured transaction confirmation timeout.

Handle the outcome as follows:

Outcome Required behavior
SUCCEEDED Return the transaction signature as today.
FAILED Throw JupiterTransactionOutcomeException containing the signature, CONFIRMED and the complete outcome.
TIMED_OUT Throw JupiterTransactionOutcomeException containing the signature, CONFIRMED and the complete outcome.

The increase and decrease paths should share one small private helper for awaiting and enforcing the common Jupiter transaction outcome policy. Do not duplicate the outcome switch in both paths.

The helper must not submit, rebuild, re-sign or retry a transaction.

Alarm actions

Update:

JupiterPerpsPositionIncreaseAlarmAction
JupiterPerpsPositionDecreaseAlarmAction

so JupiterTransactionOutcomeException is handled distinctly from generic communication and runtime errors.

For FAILED, report at least:

  • the operation type
  • transaction signature
  • confirmation slot
  • compact Solana failure details

For TIMED_OUT, report at least:

  • the operation type
  • transaction signature
  • that the outcome remains unknown
  • that no equivalent transaction was automatically resubmitted

An alarm action must not call awaitTransaction() directly. The Perps service owns the confirmation step.

This task does not introduce persistent tracking or reconciliation of unknown alarm transactions. It only guarantees that the current invocation performs one submission and never automatically resubmits it.

Communication errors and interruption

If awaitTransaction() throws an IOException after a Jupiter service has obtained the signature:

  • propagate an IOException
  • add operation and transaction-signature context to the message
  • state that the on-chain outcome is unknown
  • preserve the original exception as the cause
  • do not invoke awaitTransaction() again automatically
  • do not submit another transaction

If awaitTransaction() throws InterruptedException:

  • propagate InterruptedException directly
  • do not convert it to TIMED_OUT
  • do not wrap it in IOException or JupiterTransactionOutcomeException
  • do not perform any subsequent RPC or Jupiter request

Javadoc and specifications

Update the public Javadocs for the affected Jupiter APIs so they state:

  • normal return means independent Solana CONFIRMED success
  • JupiterTransactionOutcomeException distinguishes definitive on-chain failure from an unknown timeout
  • TIMED_OUT must never cause automatic resubmission
  • IOException after submission can also leave the outcome unknown
  • interruption propagates as InterruptedException

Update the OpenSpec material for Jupiter Swap and add the necessary Perps behavior specification in the new change. Preserve the distinction between Jupiter provider validation and independent Solana confirmation.

Verification

Do not add new unit tests or other automated tests as part of this task.

Verify at least:

  • the normal Gradle compilation succeeds
  • existing relevant tests are run if the repository test runtime permits it
  • strict OpenSpec validation succeeds
  • both Jupiter services invoke awaitTransaction() with CONFIRMED
  • the configured timeout is forwarded unchanged
  • normal results are returned only for SUCCEEDED
  • both FAILED and TIMED_OUT preserve the signature through JupiterTransactionOutcomeException
  • IOException includes the submitted signature and unknown-outcome warning
  • InterruptedException still propagates directly
  • no execution or submission request is retried
  • no live Jupiter or Solana financial transaction is performed as part of verification

Scope

This task includes:

  • JupiterTransactionOutcomeException
  • constructor-level confirmation timeout policy for the two Jupiter implementations
  • independent CONFIRMED awaiting in Jupiter Swap
  • independent CONFIRMED awaiting in Jupiter Perps increase and decrease
  • focused handling in the two existing Perps alarm actions
  • required Javadoc and OpenSpec updates

This task does not change:

  • the implementation or public contract of SolanaBlockChain.awaitTransaction()
  • SolanaBlockChain.sendTransaction()
  • automatic behavior of SolanaWallet transfer or submission methods
  • transaction construction, signing or provider submission semantics
  • Jupiter Swap order pacing
  • Jupiter execution retry behavior
  • Jupiter Perps financial validation or reserve rules
  • SPL-token burn functionality
  • EvelynBurnerService
  • Discord integration
  • persistent reconciliation of unknown transaction outcomes

The future SPL-token burn operation will separately use FINALIZED. The future EvelynBurnerService must also wait for FINALIZED before sending a burn notification.

Acceptance criteria

  • Jupiter Swap independently awaits the returned signature at CONFIRMED.
  • Jupiter Perps increase independently awaits the returned signature at CONFIRMED.
  • Jupiter Perps decrease independently awaits the returned signature at CONFIRMED.
  • Both implementations support a validated constructor-injected timeout and retain a two-minute default.
  • Existing normal return types are returned only for SUCCEEDED.
  • FAILED and TIMED_OUT are exposed through structured JupiterTransactionOutcomeException values containing the signature, requested commitment and outcome.
  • A timeout is documented and reported as an unknown outcome, not as an on-chain failure.
  • Communication errors after a signature is known include that signature and unknown-outcome context.
  • InterruptedException propagates directly.
  • Perps alarm actions report failed and unknown outcomes distinctly.
  • Neither service nor alarm action automatically resubmits a transaction.
  • Existing Jupiter provider-response validation remains intact.
  • Lower-level SolanaWallet methods remain submission-only.
  • Burn, Evelyn and Discord behavior remains outside this task.
  • No new automated tests are added.
  • Gradle compilation and strict OpenSpec validation succeed.
## Background Issue #67 added the generic: ```java SolanaTransactionOutcome awaitTransaction( ΩSolanaTransactionSignatureΩ signature, SolanaCommitment commitment, Duration timeout ) throws IOException, InterruptedException; ``` to `SolanaBlockChain`. The current Jupiter services submit signed transactions through Jupiter-managed execution endpoints, but they do not independently verify the resulting transaction through `SolanaBlockChain.awaitTransaction()`: - `JupiterSwapServiceImpl` trusts a successful Jupiter Swap V2 `/execute` response. - `AnchorIdlJupiterPerpsServiceImpl` returns as soon as Jupiter's Perps execution endpoint returns a transaction signature. The new blockchain functionality should now be reused at these high-level operation boundaries. A Jupiter Swap or Perps operation must not report normal success until the returned transaction signature has independently reached `CONFIRMED` on Solana. The starting point for this task is the followg Gitea commit: ```text cb2d3e2a164c8fb489d3ed4dae780eb2380a5e26 ``` ## Objective Integrate `SolanaBlockChain.awaitTransaction()` into: ```text com.r35157.libs.jupiter.swap.impl.ref.JupiterSwapServiceImpl com.r35157.libs.jupiter.perps.impl.anchoridl.AnchorIdlJupiterPerpsServiceImpl ``` Both services must: 1. perform their existing build, validation, signing and single submission flow 2. obtain and validate the Solana transaction signature returned by Jupiter 3. call `SolanaBlockChain.awaitTransaction()` for that signature 4. request `SolanaCommitment.CONFIRMED` 5. return their existing normal success value only when the outcome is `SUCCEEDED` 6. expose `FAILED` and `TIMED_OUT` without losing the transaction signature or conflating an unknown outcome with an on-chain failure No transaction may be automatically resubmitted. ## Why the integration belongs in the Jupiter services `JupiterSwapService.swap()`, `JupiterPerpsService.executePositionIncrease()` and `JupiterPerpsService.executePositionDecrease()` represent complete high-level operations. Their implementations already own transaction construction, signing orchestration and submission through Jupiter. Independent on-chain confirmation is therefore part of completing those operations and must remain inside the respective Jupiter service implementations. Alarm actions and future Evelyn services must not duplicate Jupiter transaction orchestration. The lower-level `SolanaWallet` submission methods must remain unchanged. Methods such as `sendSolana()`, `sendSPLToken()`, `sendTransaction()` and `signAndSendTransaction()` continue to submit and return a signature without automatically waiting. Callers of those lower-level methods may explicitly invoke `SolanaBlockChain.awaitTransaction()` when their own domain policy requires it. ## Public outcome exception Add a checked public exception shared by the Jupiter Swap and Perps APIs: ```text com.r35157.libs.jupiter.JupiterTransactionOutcomeException ``` The exception must contain structured access to: ```java ΩSolanaTransactionSignatureΩ transactionSignature() SolanaCommitment requestedCommitment() SolanaTransactionOutcome outcome() ``` The exact implementation may use ordinary accessor methods rather than record-style methods if that better matches the project's exception conventions. The constructor must enforce: - the transaction signature is not `null` or blank - the requested commitment is not `null` - the outcome is not `null` - the outcome status is either `FAILED` or `TIMED_OUT` - a `SUCCEEDED` outcome is rejected because it is not exceptional The exception message must include: - the transaction signature - the requested commitment - whether the transaction definitively failed or remains unknown - the slot and compact Solana `err` JSON for `FAILED` - an explicit warning for `TIMED_OUT` that the transaction outcome is unknown and an equivalent transaction must not be automatically resubmitted The exception and its accessors must have comprehensive Javadoc. Add `JupiterTransactionOutcomeException` to the declared checked exceptions of: ```java JupiterSwapResult swap(...) ΩSolanaTransactionSignatureΩ executePositionIncrease(...) ΩSolanaTransactionSignatureΩ executePositionDecrease(...) ``` The normal return types remain unchanged. They are returned only after independently observed `CONFIRMED` success. ## Confirmation policy and timeout Both Jupiter service implementations must use: ```java SolanaCommitment.CONFIRMED ``` The confirmation timeout is an operation policy belonging to the high-level service, not to `SolanaWallet` and not to the alarm action. Add a constructor overload to each implementation that accepts a `Duration transactionConfirmationTimeout`: ```text JupiterSwapServiceImpl AnchorIdlJupiterPerpsServiceImpl ``` Requirements: - the timeout must not be `null` - the timeout must be strictly greater than zero - invalid values must fail during construction - existing constructors must remain available and delegate to the new overload - the default timeout must be two minutes - no configuration-file format change is part of this task The timeout passed to `awaitTransaction()` starts when the service begins awaiting the already submitted signature. Time spent obtaining the Jupiter order or position transaction, signing it and submitting it is not part of this confirmation timeout. ## Jupiter Swap integration Preserve the complete existing Jupiter Swap V2 flow and validation. After `/execute` has returned a response that passes the existing validation for: - success status - result code `0` - non-blank transaction signature - positive actual input amount - positive actual output amount the implementation must call: ```java solanaBlockChain.awaitTransaction( result.transactionSignature(), SolanaCommitment.CONFIRMED, transactionConfirmationTimeout ); ``` Handle the outcome as follows: | Outcome | Required behavior | |---|---| | `SUCCEEDED` | Return the already validated `JupiterSwapResult`. | | `FAILED` | Throw `JupiterTransactionOutcomeException` containing the signature, `CONFIRMED` and the complete outcome. | | `TIMED_OUT` | Throw `JupiterTransactionOutcomeException` containing the signature, `CONFIRMED` and the complete outcome. | The independent Solana check supplements rather than replaces Jupiter's existing `/execute` response validation. Existing validation of Jupiter's reported actual token amounts must remain unchanged. If Jupiter's execution request fails before a valid transaction signature is received, the existing unknown-outcome behavior remains unchanged because there is no verified signature that can be passed to `awaitTransaction()`. ## Jupiter Perps integration Preserve the existing increase and decrease flows through Jupiter's Perps transaction execution endpoint. After the execution response has produced a valid non-blank `txid`, both: ```java executePositionIncrease(...) executePositionDecrease(...) ``` must await that signature at `CONFIRMED` using the configured transaction confirmation timeout. Handle the outcome as follows: | Outcome | Required behavior | |---|---| | `SUCCEEDED` | Return the transaction signature as today. | | `FAILED` | Throw `JupiterTransactionOutcomeException` containing the signature, `CONFIRMED` and the complete outcome. | | `TIMED_OUT` | Throw `JupiterTransactionOutcomeException` containing the signature, `CONFIRMED` and the complete outcome. | The increase and decrease paths should share one small private helper for awaiting and enforcing the common Jupiter transaction outcome policy. Do not duplicate the outcome switch in both paths. The helper must not submit, rebuild, re-sign or retry a transaction. ## Alarm actions Update: ```text JupiterPerpsPositionIncreaseAlarmAction JupiterPerpsPositionDecreaseAlarmAction ``` so `JupiterTransactionOutcomeException` is handled distinctly from generic communication and runtime errors. For `FAILED`, report at least: - the operation type - transaction signature - confirmation slot - compact Solana failure details For `TIMED_OUT`, report at least: - the operation type - transaction signature - that the outcome remains unknown - that no equivalent transaction was automatically resubmitted An alarm action must not call `awaitTransaction()` directly. The Perps service owns the confirmation step. This task does not introduce persistent tracking or reconciliation of unknown alarm transactions. It only guarantees that the current invocation performs one submission and never automatically resubmits it. ## Communication errors and interruption If `awaitTransaction()` throws an `IOException` after a Jupiter service has obtained the signature: - propagate an `IOException` - add operation and transaction-signature context to the message - state that the on-chain outcome is unknown - preserve the original exception as the cause - do not invoke `awaitTransaction()` again automatically - do not submit another transaction If `awaitTransaction()` throws `InterruptedException`: - propagate `InterruptedException` directly - do not convert it to `TIMED_OUT` - do not wrap it in `IOException` or `JupiterTransactionOutcomeException` - do not perform any subsequent RPC or Jupiter request ## Javadoc and specifications Update the public Javadocs for the affected Jupiter APIs so they state: - normal return means independent Solana `CONFIRMED` success - `JupiterTransactionOutcomeException` distinguishes definitive on-chain failure from an unknown timeout - `TIMED_OUT` must never cause automatic resubmission - `IOException` after submission can also leave the outcome unknown - interruption propagates as `InterruptedException` Update the OpenSpec material for Jupiter Swap and add the necessary Perps behavior specification in the new change. Preserve the distinction between Jupiter provider validation and independent Solana confirmation. ## Verification Do not add new unit tests or other automated tests as part of this task. Verify at least: - the normal Gradle compilation succeeds - existing relevant tests are run if the repository test runtime permits it - strict OpenSpec validation succeeds - both Jupiter services invoke `awaitTransaction()` with `CONFIRMED` - the configured timeout is forwarded unchanged - normal results are returned only for `SUCCEEDED` - both `FAILED` and `TIMED_OUT` preserve the signature through `JupiterTransactionOutcomeException` - `IOException` includes the submitted signature and unknown-outcome warning - `InterruptedException` still propagates directly - no execution or submission request is retried - no live Jupiter or Solana financial transaction is performed as part of verification ## Scope This task includes: - `JupiterTransactionOutcomeException` - constructor-level confirmation timeout policy for the two Jupiter implementations - independent `CONFIRMED` awaiting in Jupiter Swap - independent `CONFIRMED` awaiting in Jupiter Perps increase and decrease - focused handling in the two existing Perps alarm actions - required Javadoc and OpenSpec updates This task does not change: - the implementation or public contract of `SolanaBlockChain.awaitTransaction()` - `SolanaBlockChain.sendTransaction()` - automatic behavior of `SolanaWallet` transfer or submission methods - transaction construction, signing or provider submission semantics - Jupiter Swap order pacing - Jupiter execution retry behavior - Jupiter Perps financial validation or reserve rules - SPL-token burn functionality - `EvelynBurnerService` - Discord integration - persistent reconciliation of unknown transaction outcomes The future SPL-token burn operation will separately use `FINALIZED`. The future `EvelynBurnerService` must also wait for `FINALIZED` before sending a burn notification. ## Acceptance criteria - [ ] Jupiter Swap independently awaits the returned signature at `CONFIRMED`. - [ ] Jupiter Perps increase independently awaits the returned signature at `CONFIRMED`. - [ ] Jupiter Perps decrease independently awaits the returned signature at `CONFIRMED`. - [ ] Both implementations support a validated constructor-injected timeout and retain a two-minute default. - [ ] Existing normal return types are returned only for `SUCCEEDED`. - [ ] `FAILED` and `TIMED_OUT` are exposed through structured `JupiterTransactionOutcomeException` values containing the signature, requested commitment and outcome. - [ ] A timeout is documented and reported as an unknown outcome, not as an on-chain failure. - [ ] Communication errors after a signature is known include that signature and unknown-outcome context. - [ ] `InterruptedException` propagates directly. - [ ] Perps alarm actions report failed and unknown outcomes distinctly. - [ ] Neither service nor alarm action automatically resubmits a transaction. - [ ] Existing Jupiter provider-response validation remains intact. - [ ] Lower-level `SolanaWallet` methods remain submission-only. - [ ] Burn, Evelyn and Discord behavior remains outside this task. - [ ] No new automated tests are added. - [ ] Gradle compilation and strict OpenSpec validation succeed.
minimons added the enhancement label 2026-08-11 11:55:04 +02:00
minimons self-assigned this 2026-08-11 11:55:04 +02:00
minimons added this to the AssetAZ project 2026-08-11 11:55:04 +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#68