Conversation
Working production implementation used in Vortiq for Jupiter swap using the JupiterSwapManager
WalkthroughAdded a new Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 🚥 Pre-merge checks | ❌ 3❌ Failed checks (2 warnings, 1 inconclusive)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Samples`~/Solana Wallet/Scripts/example/screens/JupiterSwapManager.cs:
- Around line 242-249: CalculateRequiredBuffer currently always adds ~0.00204
SOL for ATA rent when platform fees are enabled, causing false "Need More SOL"
states; change it to only include the ATA rent buffer when an ATA creation is
actually required. Update CalculateRequiredBuffer(string inputToken) to either
accept a boolean (e.g., needsAtaCreation) or to call the same ATA-existence
check used by EstimateSwapFees()/preCheckATA and only add the 0.00204 amount
when that check indicates the ATA is missing; mirror the same conditional logic
in the other occurrences you flagged (the blocks around the other
CalculateRequiredBuffer/fee-estimate usages at the noted ranges) so ATA rent is
not baked in unconditionally. Ensure you reference and reuse the existing
EstimateSwapFees()/preCheckATA logic rather than duplicating different
heuristics.
- Line 365: The sample currently builds hardcoded Metis Swap V1 endpoints (e.g.,
the string URL in JupiterSwapManager that uses "/swap/v1/quote" and similar
"/swap/v1/swap"); refactor by introducing an interface (e.g., ISwapApiClient
with methods like GetQuoteAsync and ExecuteSwapAsync) and change
JupiterSwapManager to depend on that interface instead of composing the URL
directly, then provide a concrete implementation (e.g., MetisSwapV1Client) that
encapsulates the current "/swap/v1/..." URL construction and a future SwapV2
client/adapter; inject the chosen implementation into JupiterSwapManager so the
UI flow is no longer coupled to the hardcoded endpoint family.
- Around line 202-215: Handlers OnInputAmountChanged and OnTokenSelectionChanged
ignore new user changes while isUpdatingQuote is true, causing in-flight
responses to overwrite currentQuote with stale data; change the flow to track
and validate requests rather than blocking: introduce a monotonically increasing
requestId (or cancellation token) captured when calling UpdateQuote,
clear/replace a stored latestRequestId, and when the async quote response
returns only assign to currentQuote and enable ExecuteSwap if the response's
requestId matches the latestRequestId (do the same pattern for the other
quote-related handlers/methods around the UpdateQuote/ExecuteSwap flow). Also
ensure UpdateQuote no longer early-returns based solely on isUpdatingQuote and
instead sets isUpdatingQuote while awaiting and resets it after validating the
response against the latestRequestId.
- Around line 171-181: The code dereferences result.Result.Value without
checking for null/empty and then falls back to a hardcoded decimals=9; update
the block around Web3.Rpc.GetTokenAccountsByOwnerAsync so you first verify
result.WasSuccessful && result.Result != null && result.Result.Value.Count > 0
before accessing Value[0]; if no token account exists, set tokenBalances[symbol]
= new TokenBalance { mint = mint, balance = 0, decimals = <actual mint decimals>
} by retrieving the mint decimals via a mint query (e.g., call
Web3.Rpc.GetMintAsync or your project's mint-info helper) or a token-decimals
mapping, instead of hardcoding 9, and keep TokenBalance usage the same.
- Around line 223-237: The code subtracts a SOL-denominated requiredBuffer from
every token balance; change the Max-button logic in the method handling
inputAmountField (the block using tokenData.balance,
CalculateRequiredBuffer(inputToken), and inputToken) so that requiredBuffer is
only applied when the input token is native SOL (e.g. check inputToken == SOL or
inputToken.IsNative or inputToken.symbol == "SOL" depending on your token
model); for non-SOL tokens leave maxAmount = tokenData.balance and do not show
the "Insufficient SOL" popup, but retain the existing behavior for SOL inputs
(subtract requiredBuffer and show popup when balance <= buffer). Ensure you
reference the same identifiers tokenData, inputToken, CalculateRequiredBuffer,
and inputAmountField when making the change.
- Around line 560-570: The request object currently assigns priority fee via
computeUnitPriceMicroLamports; update the anonymous reqData object to remove
computeUnitPriceMicroLamports and instead include prioritizationFeeLamports set
to the existing priorityFee variable (which is computed from useAutoPriorityFee
and fixedPriorityFeeLamports). In other words, in the reqData construction (the
variable named reqData) replace the computeUnitPriceMicroLamports field with
prioritizationFeeLamports: priorityFee while keeping dynamicComputeUnitLimit =
true and other fields (quoteResponse, userPublicKey, wrapAndUnwrapSol,
asLegacyTransaction, feeAccount) unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: ee87eedd-6ecf-43cf-b225-f1b7d7a7a423
📒 Files selected for processing (1)
Samples~/Solana Wallet/Scripts/example/screens/JupiterSwapManager.cs
| var result = await Web3.Rpc.GetTokenAccountsByOwnerAsync(Web3.Account.PublicKey, mint, null); | ||
| if (result.WasSuccessful && result.Result.Value.Count > 0) | ||
| { | ||
| var tokenAccount = result.Result.Value[0]; | ||
| double balance = double.Parse(tokenAccount.Account.Data.Parsed.Info.TokenAmount.UiAmountString); | ||
| int decimals = tokenAccount.Account.Data.Parsed.Info.TokenAmount.Decimals; | ||
| tokenBalances[symbol] = new TokenBalance { mint = mint, balance = balance, decimals = decimals }; | ||
| } | ||
| else | ||
| { | ||
| tokenBalances[symbol] = new TokenBalance { mint = mint, balance = 0, decimals = 9 }; |
There was a problem hiding this comment.
Handle missing token accounts without falling back to 9 decimals.
GetTokenAccountsByOwnerAsync can return Result == null || Value.Count == 0 for a missing ATA (see Runtime/codebase/WalletBase.cs:139-154). Line 172 dereferences result.Result.Value anyway, so wallets without an ATA fall into the empty catch, and the explicit fallback hardcodes decimals = 9, which mis-scales USDC values.
Suggested fix
- if (result.WasSuccessful && result.Result.Value.Count > 0)
+ if (result.WasSuccessful && result.Result != null && result.Result.Value.Count > 0)
{
var tokenAccount = result.Result.Value[0];
double balance = double.Parse(tokenAccount.Account.Data.Parsed.Info.TokenAmount.UiAmountString);
int decimals = tokenAccount.Account.Data.Parsed.Info.TokenAmount.Decimals;
tokenBalances[symbol] = new TokenBalance { mint = mint, balance = balance, decimals = decimals };
}
else
{
- tokenBalances[symbol] = new TokenBalance { mint = mint, balance = 0, decimals = 9 };
+ int knownDecimals = tokenBalances.TryGetValue(symbol, out var existing) ? existing.decimals : 9;
+ tokenBalances[symbol] = new TokenBalance { mint = mint, balance = 0, decimals = knownDecimals };
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Samples`~/Solana Wallet/Scripts/example/screens/JupiterSwapManager.cs around
lines 171 - 181, The code dereferences result.Result.Value without checking for
null/empty and then falls back to a hardcoded decimals=9; update the block
around Web3.Rpc.GetTokenAccountsByOwnerAsync so you first verify
result.WasSuccessful && result.Result != null && result.Result.Value.Count > 0
before accessing Value[0]; if no token account exists, set tokenBalances[symbol]
= new TokenBalance { mint = mint, balance = 0, decimals = <actual mint decimals>
} by retrieving the mint decimals via a mint query (e.g., call
Web3.Rpc.GetMintAsync or your project's mint-info helper) or a token-decimals
mapping, instead of hardcoding 9, and keep TokenBalance usage the same.
| private void OnInputAmountChanged(string value) | ||
| { | ||
| UpdateButtonState(); | ||
| if (isUpdatingQuote) return; | ||
| if (IsValidInput(out float amount)) UpdateQuote(); | ||
| else ClearQuoteInfo(); | ||
| } | ||
|
|
||
| private void OnTokenSelectionChanged() | ||
| { | ||
| UpdateBalanceDisplays(); | ||
| UpdateButtonState(); | ||
| if (IsValidInput(out float amount)) UpdateQuote(); | ||
| else ClearQuoteInfo(); |
There was a problem hiding this comment.
Don't drop the latest user input while a quote request is running.
Both handlers stop re-quoting when isUpdatingQuote is true. If the user changes amount or tokens during an in-flight request, the older response still overwrites currentQuote, and ExecuteSwap() can submit a stale route/amount that no longer matches the current UI.
Also applies to: 315-351
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Samples`~/Solana Wallet/Scripts/example/screens/JupiterSwapManager.cs around
lines 202 - 215, Handlers OnInputAmountChanged and OnTokenSelectionChanged
ignore new user changes while isUpdatingQuote is true, causing in-flight
responses to overwrite currentQuote with stale data; change the flow to track
and validate requests rather than blocking: introduce a monotonically increasing
requestId (or cancellation token) captured when calling UpdateQuote,
clear/replace a stored latestRequestId, and when the async quote response
returns only assign to currentQuote and enable ExecuteSwap if the response's
requestId matches the latestRequestId (do the same pattern for the other
quote-related handlers/methods around the UpdateQuote/ExecuteSwap flow). Also
ensure UpdateQuote no longer early-returns based solely on isUpdatingQuote and
instead sets isUpdatingQuote while awaiting and resets it after validating the
response against the latestRequestId.
| double maxAmount = tokenData.balance; | ||
| double requiredBuffer = CalculateRequiredBuffer(inputToken); | ||
|
|
||
| if (maxAmount <= requiredBuffer) | ||
| { | ||
| maxAmount = 0; | ||
| ShowPopup("Insufficient SOL", $"Need {requiredBuffer:F4} SOL minimum", Color.red); | ||
| } | ||
| else | ||
| { | ||
| maxAmount = maxAmount - requiredBuffer; | ||
| } | ||
|
|
||
| string format = "0." + new string('#', tokenData.decimals); | ||
| inputAmountField.text = maxAmount.ToString(format); |
There was a problem hiding this comment.
Reserve SOL fees only when the input token is SOL.
requiredBuffer is denominated in SOL, but this branch subtracts it from PLAY/USDC balances too. For non-SOL inputs the Max button underfills the amount and can zero out small token balances even when the user has enough SOL separately.
Suggested fix
double maxAmount = tokenData.balance;
- double requiredBuffer = CalculateRequiredBuffer(inputToken);
-
- if (maxAmount <= requiredBuffer)
+ if (inputToken == "SOL")
{
- maxAmount = 0;
- ShowPopup("Insufficient SOL", $"Need {requiredBuffer:F4} SOL minimum", Color.red);
- }
- else
- {
- maxAmount = maxAmount - requiredBuffer;
+ double requiredBuffer = CalculateRequiredBuffer(inputToken);
+ if (maxAmount <= requiredBuffer)
+ {
+ maxAmount = 0;
+ ShowPopup("Insufficient SOL", $"Need {requiredBuffer:F4} SOL minimum", Color.red);
+ }
+ else
+ {
+ maxAmount -= requiredBuffer;
+ }
+ }
+ else if (!HasSufficientSolForFees(inputToken, 0))
+ {
+ ShowPopup("Insufficient SOL", $"Need {CalculateRequiredBuffer(inputToken):F4} SOL minimum", Color.red);
+ return;
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Samples`~/Solana Wallet/Scripts/example/screens/JupiterSwapManager.cs around
lines 223 - 237, The code subtracts a SOL-denominated requiredBuffer from every
token balance; change the Max-button logic in the method handling
inputAmountField (the block using tokenData.balance,
CalculateRequiredBuffer(inputToken), and inputToken) so that requiredBuffer is
only applied when the input token is native SOL (e.g. check inputToken == SOL or
inputToken.IsNative or inputToken.symbol == "SOL" depending on your token
model); for non-SOL tokens leave maxAmount = tokenData.balance and do not show
the "Insufficient SOL" popup, but retain the existing behavior for SOL inputs
(subtract requiredBuffer and show popup when balance <= buffer). Ensure you
reference the same identifiers tokenData, inputToken, CalculateRequiredBuffer,
and inputAmountField when making the change.
| private double CalculateRequiredBuffer(string inputToken) | ||
| { | ||
| double buffer = 0.000005; | ||
| if (useAutoPriorityFee) buffer += 0.00005; | ||
| else buffer += fixedPriorityFeeLamports / 1_000_000_000.0; | ||
|
|
||
| if (!string.IsNullOrEmpty(platformFeeWallet) && platformFeePercent > 0) buffer += 0.00204; | ||
| buffer += 0.0001; |
There was a problem hiding this comment.
Don't always bake ATA rent into the SOL guard.
CalculateRequiredBuffer() always adds ~0.00204 SOL when platform fees are enabled, while EstimateSwapFees() separately probes ATA existence and even ignores preCheckATA. That can leave the button stuck on “Need More SOL” for swaps that are actually affordable.
Also applies to: 304-310, 686-699
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Samples`~/Solana Wallet/Scripts/example/screens/JupiterSwapManager.cs around
lines 242 - 249, CalculateRequiredBuffer currently always adds ~0.00204 SOL for
ATA rent when platform fees are enabled, causing false "Need More SOL" states;
change it to only include the ATA rent buffer when an ATA creation is actually
required. Update CalculateRequiredBuffer(string inputToken) to either accept a
boolean (e.g., needsAtaCreation) or to call the same ATA-existence check used by
EstimateSwapFees()/preCheckATA and only add the 0.00204 amount when that check
indicates the ATA is missing; mirror the same conditional logic in the other
occurrences you flagged (the blocks around the other
CalculateRequiredBuffer/fee-estimate usages at the noted ranges) so ATA rent is
not baked in unconditionally. Ensure you reference and reuse the existing
EstimateSwapFees()/preCheckATA logic rather than duplicating different
heuristics.
| string routeParams = $"&asLegacyTransaction=true&maxAccounts={maxAccounts}"; | ||
| if (onlyDirectRoutes) routeParams += "&onlyDirectRoutes=true"; | ||
|
|
||
| string url = $"{jupiterBaseUrl}/swap/v1/quote?inputMint={inputMint}&outputMint={outputMint}&amount={amountRaw}&slippageBps={slippageBps}{feeParam}{routeParams}"; |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider isolating this sample from Metis Swap V1.
The hardcoded /swap/v1/quote and /swap/v1/swap endpoints are now marked by Jupiter as part of the Metis Swap API, which is no longer actively maintained and has been superseded by Swap V2. Since this is a new sample, hiding the transport behind an interface now would avoid coupling the UI flow to an endpoint family you already know will need migration. (dev.jup.ag)
Also applies to: 575-575
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Samples`~/Solana Wallet/Scripts/example/screens/JupiterSwapManager.cs at line
365, The sample currently builds hardcoded Metis Swap V1 endpoints (e.g., the
string URL in JupiterSwapManager that uses "/swap/v1/quote" and similar
"/swap/v1/swap"); refactor by introducing an interface (e.g., ISwapApiClient
with methods like GetQuoteAsync and ExecuteSwapAsync) and change
JupiterSwapManager to depend on that interface instead of composing the URL
directly, then provide a concrete implementation (e.g., MetisSwapV1Client) that
encapsulates the current "/swap/v1/..." URL construction and a future SwapV2
client/adapter; inject the chosen implementation into JupiterSwapManager so the
UI flow is no longer coupled to the hardcoded endpoint family.
| object priorityFee = useAutoPriorityFee ? "auto" : fixedPriorityFeeLamports; | ||
|
|
||
| var reqData = new | ||
| { | ||
| quoteResponse = quote, | ||
| userPublicKey = Web3.Account.PublicKey.ToString(), | ||
| wrapAndUnwrapSol = true, | ||
| asLegacyTransaction = true, | ||
| dynamicComputeUnitLimit = true, | ||
| computeUnitPriceMicroLamports = priorityFee, | ||
| feeAccount = feeAccount |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "JupiterSwapManager.cs" -type fRepository: magicblock-labs/Solana.Unity-SDK
Length of output: 146
🏁 Script executed:
# Check if there are any comments or documentation in the codebase about Jupiter API
rg -i "prioritizationFeeLamports\|computeUnitPriceMicroLamports" --type cs -B 2 -A 2Repository: magicblock-labs/Solana.Unity-SDK
Length of output: 58
🏁 Script executed:
# Look for any existing Jupiter API references or comments
rg -i "jupiter" --type cs -i -C 3Repository: magicblock-labs/Solana.Unity-SDK
Length of output: 14105
🏁 Script executed:
cat -n "Samples~/Solana Wallet/Scripts/example/screens/JupiterSwapManager.cs" | sed -n '550,580p'Repository: magicblock-labs/Solana.Unity-SDK
Length of output: 1731
🏁 Script executed:
# Get more context around the swap request building
cat -n "Samples~/Solana Wallet/Scripts/example/screens/JupiterSwapManager.cs" | sed -n '540,590p'Repository: magicblock-labs/Solana.Unity-SDK
Length of output: 2702
🏁 Script executed:
# Search for all occurrences of prioritization or compute unit fields
rg "prioritization|computeUnit" "Samples~/Solana Wallet/Scripts/example/screens/JupiterSwapManager.cs" -iRepository: magicblock-labs/Solana.Unity-SDK
Length of output: 176
🌐 Web query:
Jupiter API swap instructions computeUnitPriceMicroLamports prioritizationFeeLamports
💡 Result:
Jupiter API swap instructions, particularly via the /swap-instructions endpoint (Metis Swap API V1, superseded by V2 but parameters similar), allow setting priority fees using computeUnitPriceMicroLamports and prioritizationFeeLamports. Key parameters in POST /swap-instructions request body: - computeUnitPriceMicroLamports (uint64, optional): Sets exact compute unit price in micro-lamports per CU for priority fee calculation. Formula: priority fee = computeUnitLimit (default 1,400,000) * computeUnitPriceMicroLamports. Jupiter recommends using prioritizationFeeLamports + dynamicComputeUnitLimit instead. - prioritizationFeeLamports (object or integer, optional): Specifies total additional priority fee in lamports. Supports: - Fixed integer lamports. - priorityLevelWithMaxLamports: {priorityLevel: 'low'|'medium'|'high'|'veryHigh', maxLamports: uint64} - Auto-estimates based on level, capped at maxLamports. - jitoTipLamports: uint64 - Jito MEV tip (mutually exclusive with priority fee by default). Note: For both priority fee AND Jito tip, use /swap-instructions to get separate computeBudgetInstructions and otherInstructions (Jito tip), then compose manually. Response includes: - computeBudgetInstructions: Array of ComputeBudgetProgram instructions (SetComputeUnitPrice, etc.). - otherInstructions: Jito tip if set. Example request snippet: { "userPublicKey": "YourWalletPubkey", "quoteResponse": {...}, "computeUnitPriceMicroLamports": 10000, "prioritizationFeeLamports": { "priorityLevelWithMaxLamports": { "priorityLevel": "veryHigh", "maxLamports": 1000000 } }, "dynamicComputeUnitLimit": true } In Swap API V2 (/swap/v2/order or /build), priorityFeeLamports and jitoTipLamports are query params; compute unit price is auto-handled in assembled tx or provided as instructions. To use: 1. GET /quote for route. 2. POST /swap-instructions with params. 3. Add returned instructions to tx (computeBudget first), sign, send. Official docs emphasize dynamicComputeUnitLimit: true for accurate CU estimation and prioritizationFeeLamports over fixed microLamports for better local fee market accuracy.
Citations:
- 1: https://dev.jup.ag/api-reference/swap/swap-instructions
- 2: https://dev.jup.ag/docs/swap-api/build-swap-transaction
- 3: https://dev.jup.ag/api-reference/swap/v1/swap-instructions
- 4: https://www.quicknode.com/docs/solana/swap
- 5: https://station.jup.ag/docs/api/swap-instructions
- 6: https://dev.jup.ag/docs/api/swap-api/swap-instructions
🌐 Web query:
Jupiter DEX swap API documentation field specifications compute unit price
💡 Result:
In Jupiter DEX (Solana) swap APIs, particularly the Metis Swap API (/swap endpoint in v1), the field for specifying compute unit price is computeUnitPriceMicroLamports. Field specifications: - Type: integer (uint64 format) - Description: Sets an exact compute unit price (in micro-lamports per compute unit) to calculate the priority fee. The priority fee is computed as computeUnitLimit (default or dynamic: up to 1,400,000) * computeUnitPriceMicroLamports. - Usage: Optional parameter in the POST /swap request body. Adds a ComputeBudgetProgram.setComputeUnitPrice instruction to the transaction. - Recommendation: Official docs advise using prioritizationFeeLamports (with options like "auto", priority levels, or exact amounts) and dynamicComputeUnitLimit: true instead, as they provide better optimization via simulation and network fee estimation. Note: Newer Swap API V2 (/swap/v2/order, /build) favors priorityFeeLamports directly (in lamports total), without exposing the micro-lamports price field, handling CU dynamically. Example in request body: { "quoteResponse": {...}, "userPublicKey": "wallet_pubkey", "computeUnitPriceMicroLamports": 10000 // e.g., 10,000 micro-lamports per CU } This generates priority fee = CU limit * 10,000 micro-lamports (1 lamport = 1,000,000 micro-lamports).
Citations:
- 1: https://dev.jup.ag/api-reference/swap/swap
- 2: https://dev.jup.ag/api-reference/swap/v1/swap-instructions
- 3: https://dev.jup.ag/api-reference
- 4: https://dev.jup.ag/docs/swap/v2/build
- 5: https://dev.jup.ag/docs/old/apis/landing-transactions
- 6: https://station.jup.ag/docs/api/swap-instructions
Use prioritizationFeeLamports instead of computeUnitPriceMicroLamports for priority fees.
The code sends fixedPriorityFeeLamports (a lamport amount) via computeUnitPriceMicroLamports, which Jupiter documents as a micro-lamports-per-compute-unit field. Additionally, the "auto" mode is not documented as valid for computeUnitPriceMicroLamports. Jupiter's API recommends using prioritizationFeeLamports, which properly supports both "auto" and fixed lamport semantics and is explicitly noted as the preferred approach when combined with dynamicComputeUnitLimit. (See dev.jup.ag/api-reference/swap/swap-instructions)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Samples`~/Solana Wallet/Scripts/example/screens/JupiterSwapManager.cs around
lines 560 - 570, The request object currently assigns priority fee via
computeUnitPriceMicroLamports; update the anonymous reqData object to remove
computeUnitPriceMicroLamports and instead include prioritizationFeeLamports set
to the existing priorityFee variable (which is computed from useAutoPriorityFee
and fixedPriorityFeeLamports). In other words, in the reqData construction (the
variable named reqData) replace the computeUnitPriceMicroLamports field with
prioritizationFeeLamports: priorityFee while keeping dynamicComputeUnitLimit =
true and other fields (quoteResponse, userPublicKey, wrapAndUnwrapSol,
asLegacyTransaction, feeAccount) unchanged.
|
@Ragna022 Can you take a look at the CodeRabbit majors? |
This PR introduces JupiterSwapManager, a highly optimized, standalone component for interacting with the Jupiter Swap API (v1/quote and v1/swap).
The current standard components occasionally fall short when dealing with Solana framework size limits (1232 bytes) or legacy transaction inflation. This script introduces specialized logic—such as direct raw byte signing in the Unity Editor and strict API-level account routing—to guarantee smoother transaction delivery.
Key Technical Implementations
Raw Byte Signing (Zero Inflation Editor Path): Includes a specialised #if UNITY_EDITOR execution path that signs raw transaction bytes directly via Web3.Account.Sign(message). This completely bypasses the standard Transaction.Deserialize() -> Serialize() overhead, which is notorious for inflating transaction sizes and causing "transaction too large" errors on legacy routes.
Strict Size Bounds Control: Introduces maxAccounts (default 20) and asLegacyTransaction=true parameters to the Jupiter payload. It also includes an onlyDirectRoutes toggle to entirely guarantee the swap fits under the wire limits on mobile.
Smart Buffer & Priority Fee Scaling: Replaces static logic with a responsive useAutoPriorityFee mode ("auto" compute unit price) or fixed priority lamports. It also implements an advanced pre-swap SOL buffer calculator CalculateRequiredBuffer() that protects the exact amount of SOL needed to execute, ensuring the user is never left with an uncallable swap.
Native ATA Pre-Flighting: Dynamically derives the ATA for platform fee routing (platformFeeWallet & platformFeePercent). It executes an RPC check (CheckATAExists) and perfectly calculates the ~0.00203928 SOL creation fee into the quote estimations before the user attempts the swap.
Seamless SDK Integration: Updated to strictly use Web3.Account and Web3.Rpc patterns internally, while plugging directly into native SDK UI components like Toast for non-blocking transaction flow alerts.
Changes Made
[NEW] Added Samples~/Solana Wallet/Scripts/example/screens/JupiterSwapManager.cs.
How to Test
Add JupiterSwapManager.cs to any canvas in a Sample scene.
Link the required standard UI dependencies (TMP_Dropdown, TMP_InputField, Toast, etc.).
Test a quote from a standard SPL token (e.g., USDC) to SOL to observe dynamic fee calculation buffering.
Execute the swap in the Editor to verify the optimized raw byte signing path triggers successfully.
Execute via a mobile build to verify the fallback Transaction.Deserialize path works with wallet adapters.
Summary by CodeRabbit