12 KiB
jupiter-swap-service Specification
Purpose
Defines safe exact-input SPL-token swaps through Jupiter Swap V2 using human-readable amounts and an existing Solana wallet.
Requirements
Requirement: Public exact-input swap contract
The service SHALL accept a positive human-readable exact input amount, a maximum slippage in basis points, a supported output SPL-token mint, and a distinct input representation that is either a supported SPL-token mint or native SOL expressed as the canonical SOL/WSOL mint So11111111111111111111111111111111111111112. It SHALL pass that canonical native-SOL representation unchanged to Jupiter as inputMint, preserve the existing swap(...) signature, and return the confirmed Solana transaction signature together with the actual human-readable input amount spent and output amount received. Callers that select native SOL SHALL remain responsible for identifying the AssetAZ SOL UUID, resolving the canonical mint through CIS, reading native wallet balance, and applying any reserve policy; the swap service SHALL NOT introduce a separate native-SOL operation.
Scenario: Successful exact-input swap
- WHEN a caller requests a valid exact-input swap from a supported legacy SPL or Token-2022 mint that Jupiter executes successfully
- THEN the result contains the confirmed signature and the actual spent and received amounts converted with their respective mint decimal precision
Scenario: Successful native SOL input swap
- WHEN a caller requests a valid exact-input swap using
So11111111111111111111111111111111111111112as input and Jupiter executes it from the taker's native SOL balance - THEN Jupiter receives that same canonical input mint and the service returns the confirmed signature and actual human-readable spent and received amounts through the existing result contract
Scenario: Existing token-program behavior is preserved
- WHEN a caller uses a supported legacy SPL-token or Token-2022 input
- THEN mint validation, decimal conversion, signing, single managed execution, and independent
CONFIRMEDhandling remain unchanged
Scenario: Invalid caller input
- WHEN either mint representation is blank, both mints are equal, the amount is null or non-positive, or the maximum slippage is outside 0 through 10000 basis points
- THEN the service rejects the request before requesting a Jupiter order
Requirement: On-chain mint validation and exact amount conversion
Before requesting an order, the service SHALL load both mint accounts through the Solana blockchain, require each mint to exist and be owned by either the legacy SPL Token Program or Token-2022, and resolve each mint's decimal precision. The service SHALL convert the input amount exactly to a positive raw integer and SHALL reject values that cannot be represented with the input mint's precision.
Scenario: Supported legacy and Token-2022 mints
- WHEN the input and output mint accounts are owned by either supported SPL token program and their supplies provide valid decimal precision
- THEN the service uses those independently resolved precisions for raw request and human-readable result amounts
Scenario: Missing or unsupported mint
- WHEN either mint account is absent or is owned by an unsupported program
- THEN the service fails before requesting an order or signing a transaction
Scenario: Fraction smaller than the mint unit
- WHEN the requested input amount has a non-zero fraction beyond the input mint's decimal precision
- THEN the service rejects the amount rather than rounding it
Requirement: Safe Jupiter order acquisition
The service SHALL use Jupiter's keyless Swap V2 order endpoint in ExactIn mode with the wallet as taker, the caller's slippage limit, and excludeRouters=jupiterz. Across all reference-implementation instances in one JVM, starts of order HTTP requests SHALL be separated by at least two seconds. This limiter SHALL remain local to the implementation and SHALL NOT delay execution requests.
Scenario: Every order excludes JupiterZ
- WHEN the service requests an order
- THEN the request identifies the exact input and output mints, raw input amount, wallet taker, exact-input mode, slippage limit, and excludes the
jupiterzrouter
Scenario: Concurrent service instances request orders
- WHEN multiple reference-implementation instances concurrently need Jupiter orders
- THEN their order HTTP requests begin at least two seconds apart JVM-wide while execution requests remain unthrottled
Scenario: Order wait is interrupted
- WHEN a caller is interrupted while waiting for its permitted order-request time
- THEN the service propagates interruption without sending that order request
Requirement: Order integrity validation before signing
Before signing, the service SHALL require a successful HTTP response containing a well-formed order whose input mint, output mint, raw input amount, exact-input mode, and taker match the request. The returned slippage SHALL be present, valid, and no greater than the caller's maximum. The service SHALL also require a non-blank, valid Base64 unsigned transaction that decodes to at least one byte, a non-blank request identifier, and a positive valid last block height. Any order build error, malformed body, mismatch, or missing required value SHALL fail before signing.
Scenario: Valid matching order
- WHEN Jupiter returns a well-formed order matching both requested mints, the exact raw input amount, wallet taker, and permitted slippage with all required transaction metadata
- THEN the service passes the returned unsigned transaction to the configured Solana wallet for signing
Scenario: Mismatching or incomplete order
- WHEN an order changes a mint, amount, swap mode, or taker; returns missing, invalid, or excessive slippage; or lacks a valid non-empty Base64 transaction, request identifier, or last valid block height
- THEN the service rejects the order without signing or executing it
Scenario: Jupiter order HTTP or decoding failure
- WHEN the order endpoint returns a non-success status or malformed JSON
- THEN the service reports an I/O failure with useful endpoint response context and does not sign a transaction
Requirement: Single-attempt managed execution
After successful signing, the service SHALL reject a blank signed transaction and submit exactly one Swap V2 execution request containing the signed transaction, order request identifier, and last valid block height. The service SHALL never automatically retry after the execution request has been submitted, because transport failure, timeout, or interruption can leave the execution outcome unknown.
Scenario: Signed transaction is executed once
- WHEN wallet signing returns a non-blank transaction
- THEN the service sends one execution request without applying the order limiter
Scenario: Execution outcome is unknown
- WHEN the execution HTTP exchange times out, is interrupted, loses its response, or otherwise fails after submission
- THEN the service reports the failure and does not automatically request another order or resubmit execution
Scenario: Blank signed transaction
- WHEN wallet signing returns a blank serialized transaction
- THEN the service fails without submitting an execution request
Requirement: Confirmed execution result validation
The service SHALL first accept a Jupiter execution response only when it has status Success, explicitly has result code 0, contains a non-blank transaction signature, and contains positive valid actual total input and output raw amounts. It SHALL convert each actual amount using the independently resolved precision of its mint. After this provider-response validation, the service SHALL independently await the returned signature on Solana at CONFIRMED using its configured confirmation timeout and SHALL return the already validated swap result only for a SUCCEEDED outcome. Non-success HTTP responses, malformed responses, expired or rejected swaps, failed or contradictory status/code combinations, missing result codes, blank signatures, and invalid result amounts SHALL remain provider failures and SHALL not be represented as successful swaps.
Scenario: Provider response and on-chain confirmation succeed
- WHEN Jupiter reports status
Success, code0, a signature, and valid actual total input and output amounts, and that signature independently reachesCONFIRMEDwith aSUCCEEDEDoutcome - THEN the service returns the signature and those actual amounts in human-readable units
Scenario: Jupiter rejects or fails execution
- WHEN Jupiter returns a failed status, a missing or non-zero result code, a contradictory status/code combination, expiration, rejection, non-success HTTP status, malformed body, blank signature, or invalid actual amount
- THEN the service reports an I/O failure without treating provider validation as an on-chain confirmation
Scenario: Provider succeeds but transaction fails on-chain
- WHEN a fully validated Jupiter execution response contains a signature whose independent
CONFIRMEDoutcome isFAILED - THEN the service throws a structured checked outcome exception preserving the signature, requested commitment, slot, and complete compact Solana failure details
Scenario: Provider succeeds but confirmation times out
- WHEN a fully validated Jupiter execution response contains a signature whose independent
CONFIRMEDoutcome isTIMED_OUT - THEN the service throws a structured checked outcome exception preserving the signature, requested commitment, and unknown outcome warning and does not automatically resubmit an equivalent transaction
Requirement: Swap confirmation timeout policy
The reference service SHALL accept a constructor-injected, non-null, strictly positive confirmation timeout and SHALL preserve its existing constructor with a two-minute default. The configured duration SHALL be passed unchanged to independent confirmation after submission and SHALL begin governing only when awaiting the known signature starts.
Scenario: Valid custom timeout
- WHEN a reference service is constructed with a positive duration and later obtains a validated execution signature
- THEN it awaits that signature at
CONFIRMEDwith exactly the supplied duration
Scenario: Invalid custom timeout
- WHEN a reference service is constructed with a null, zero, or negative duration
- THEN construction fails before any operation can be submitted
Requirement: Post-submission communication and interruption policy
If independent confirmation fails with an I/O error after the signature is known, the service SHALL throw an IOException whose message identifies the swap operation, includes the signature, and states that the on-chain outcome is unknown, with the original error retained as its cause. Interruption SHALL propagate directly as InterruptedException without another RPC or Jupiter request. Neither condition SHALL trigger rebuilding, re-signing, retrying, or resubmitting a transaction.
Scenario: Confirmation communication fails
- WHEN independent confirmation throws an I/O error for a known submitted signature
- THEN the service reports the signature and unknown on-chain outcome while preserving the original error and performs no retry or resubmission
Scenario: Confirmation is interrupted
- WHEN independent confirmation throws
InterruptedException - THEN that interruption propagates directly and the service performs no subsequent request
Requirement: API and implementation separation
The stable service interface and result value SHALL reside in the Jupiter swap API package, while HTTP DTOs, endpoint handling, throttling, and the reference implementation SHALL remain in the reference-implementation package.
Scenario: Downstream API consumer
- WHEN downstream code depends only on the Jupiter swap API package
- THEN it can invoke swaps and inspect results without depending on private wire DTOs or reference-implementation mechanics