Fix soroban fee pricing, circuit breaker, retry, and stream flags - #378
Merged
Jaydbrown merged 1 commit intoAug 27, 2026
Conversation
…am flags (conduit-protocol#357 conduit-protocol#358 conduit-protocol#359 conduit-protocol#360) ## conduit-protocol#360 — Dynamic inclusion fee pricing - Read getFeeStats() and bid multiplier × the observed p70 fee instead of pinning all bids to BASE_FEE (100 stroops), which was not selected under inclusion-fee pressure. - Added NEXT_PUBLIC_SOROBAN_FEE_MULTIPLIER env var (default 2×). - Bids are floored at multiplier × BASE_FEE, capped at 0.1 XLM, cached for 30s. - Under surge pricing, the app can now finalize transactions instead of timing out. ## conduit-protocol#359 — Circuit breaker discriminates transport failures from contract reverts - An on-chain revert (NothingToWithdraw, StreamCancelled, InsufficientDeposit) means the RPC worked fine — the contract simply said no. Only recordFailure() for true transport-level errors (timeout, network, 5xx). - Introduced TransactionRevertedError (distinct from retryable RPC failures). - isTransportFailure() classifies errors; only those open the breaker. - Users can now retry after a legitimately-reverting call without hitting the breaker. ## conduit-protocol#358 — Sign + submit happen exactly once; only polling is retried - Moved the poll loop outside withRetry so a transient poll error cannot replay simulate → sign → submit. Previously, a poll timeout would re-prompt the wallet and send a second transaction (second stream, second withdrawal). - Extracted pollForConfirmation(); it retries only the getTransaction() RPC call. - On poll exhaustion (confirmation window elapses), return the hash as pending rather than replaying the transaction. ## conduit-protocol#357 — Stream flags encoded as bits, not fields - getStreamInfo() now reads `flags` (u32) and derives paused/clawbackEnabled/cancelled by bit-masking (FLAG_PAUSED=1, FLAG_CLAWBACK_ENABLED=2, FLAG_CANCELLED=4), matching the contract's StreamInfo::is_paused() / is_clawback_enabled() / is_cancelled(). - Previously read them as missing fields and threw for every real stream, rendering empty dashboards. Co-Authored-By: emmanuelochaje <adocheofficial@gmail.com>
|
@EmmanuelOchaje Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Addresses four critical issues in the Soroban transaction pipeline and contract data parsing:
fee: BASE_FEEand never bumped #360: Dynamic inclusion fee pricing — readgetFeeStats()and bid multiplier × the observed p70 fee instead of BASE_FEE (100 stroops), which was never selected under surge pricing.invokeContractretries the whole simulate→sign→submit→poll pipeline on a transient poll error — re-prompts the wallet and can double-submit #358: Sign + submit happen exactly once; only polling is retried. A transient poll failure no longer replays the whole pipeline, preventing duplicate wallet prompts and duplicate on-chain transactions.getStreamInforeadspaused/clawback_enabled/cancelledfields the contract'sStreamInfodoesn't have — it throws for every real stream #357: Stream flags are now read as a u32 bitmask (FLAG_PAUSED=1, FLAG_CLAWBACK_ENABLED=2, FLAG_CANCELLED=4) instead of missing fields, fixing "getStreamInfo throws for every real stream."Changes
lib/env.ts(new)getFeeMultiplier()— readsNEXT_PUBLIC_SOROBAN_FEE_MULTIPLIER(default 2×), safely ignores invalid values..env.exampleNEXT_PUBLIC_SOROBAN_FEE_MULTIPLIER.lib/soroban.ts(core fixes)TransactionRevertedError— exported, distinct from retryable RPC failures.isTransportFailure()— classifies errors; only these increment the circuit breaker.getInclusionFee()— fetchesgetFeeStats(), caches for 30s, applies multiplier, defaults to multiplier × BASE_FEE if RPC unavailable.invokeContract():pollForConfirmation()— retries only the poll RPC, never submit.getInclusionFee()for dynamic fee pricing.simulateReadOnly():getInclusionFee()for dynamic fee pricing.pollForConfirmation():TransactionRevertedErroron FAILED status (not retried, doesn't open breaker).withRetry():recordFailure()ifisTransportFailure(err)on exhaustion.lib/stream.tsgetStreamInfo():flagsas a u32 and derives booleans by bit-masking.readU32()helper.Tests
lib/soroban-pipeline.test.ts:lib/stream.test.ts:Behavior
Before: Under inclusion-fee pressure, all transactions timed out after 30s with a misleading "network congested" message. Users saw empty dashboards (getStreamInfo threw on every stream). Repeated contract reverts (trying to withdraw from an empty stream) locked the whole app out of RPC for 10s+.
After: Transactions are priced dynamically and finalize even under surge pricing. Real streams load. Users can retry after contract reverts without hitting the circuit breaker.
Notes
NEXT_PUBLIC_SOROBAN_FEE_MULTIPLIER.Closes #357, Closes #358, Closes #359, Closes #360