You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This issue evaluates and, if justified, adds a read-only contract preview for PayStream-owned charge validation. The preview can expose due status and computed charge information without mutating subscription state.
It must not be represented as a guarantee that the real charge() will succeed.
Requirements and Context
Soroban RPC already supports simulating the actual charge() invocation before submission. In the current keeper, prepareTransaction() performs simulation/preparation before signing and sending. Therefore:
many deterministic contract failures may already be discovered before a transaction is submitted;
simulation of the real charge() includes the external token transfer_from path and is more faithful than a preview that deliberately omits it;
an explicit preview still requires constructing/invoking a Soroban contract operation through RPC;
preview state can change before the real transaction executes;
a separate pre-check adds RPC load and may not reduce fees materially.
The PR must document the exact SDK behavior observed with the pinned version and explain why an explicit contract API is still valuable. Valid reasons may include richer structured UI data, reusable PayStream-owned validation, or cheaper/readable diagnostics—not an unsupported promise of fee elimination.
API design
Evaluate whether simulate_charge is the clearest name. It may be confused with Soroban RPC simulation. A name such as preview_charge may better describe contract-level validation, but preserve the requested name if repository API consistency favors it.
Use only fields available in the merged contract. Missing usage-billing fields must not be invented before #36 lands.
The API must:
Load and validate the same PayStream-owned state used by charge().
Return the same typed errors for missing/inactive/not-due/internal-allowance conditions.
Use the same checked amount calculation as charge().
Perform no token transfer.
Make no persistent, temporary, or instance storage mutation.
Not advance next_due, reduce allowance, reset usage, or alter pending adjustments.
Require no authorization if the read is intentionally public.
Document the data exposed publicly.
Shared logic
Refactor deterministic PayStream-owned validation and calculation into an internal helper used by both functions.
The helper should return validated data required by charge(), not merely (), so the real charge does not reload or recalculate conflicting state.
Do not force external side effects into the shared read helper. Separate:
deterministic PayStream validation/calculation;
external token/router/oracle calls;
post-success state mutation.
The refactor must not change charge() behavior, ordering, errors, authorization, amounts, or atomicity.
External-state limitations
A preview that omits transfer_from cannot conclusively validate:
live token-contract allowance;
allowance expiration;
subscriber token balance;
token freeze/clawback or other asset rules;
router liquidity/slippage;
oracle availability;
state changes between preview and execution.
Do not map these limitations to Ok(()) meaning “guaranteed success.” Document that Ok means PayStream's previewed checks passed at the observed ledger state.
If querying token balance/allowance is added, it remains a snapshot and must use the exact token interface safely. It still cannot guarantee later execution.
Keeper integration
Keeper integration is optional and must be justified with measurements or clear behavior.
Before adding it, compare:
existing prepareTransaction() simulation of the real charge;
explicit preview followed by preparation and submission.
Do not add the preview if it merely doubles RPC calls without reducing submitted failures or improving diagnostics. If added:
make it configurable;
preserve retry/backoff classification;
handle preview/execution races;
do not skip a charge permanently based on stale preview state;
test that retry and confirmation behavior are unchanged.
Suggested Execution
Branch name:feat/simulate-charge-preview
Files to touch:
src/lib.rs — shared deterministic validation/calculation and preview API
src/test.rs — parity, read-only, privacy, and regression tests
docs/ARCHITECTURE.md — preview semantics and RPC-simulation comparison
keeper/keeper.js and keeper tests only if integration is justified
Example commit message:feat(core): add read-only charge preview with shared validation
Test and Commit Steps
Verify and document the pinned SDK's prepareTransaction()/simulation behavior.
Define the preview's unique value, name, result type, and public-data exposure.
Regression-test current charge() behavior before refactoring.
Extract deterministic validation/calculation into a shared helper.
Implement the preview without external transfers or state mutation.
Add tests covering:
parity for every PayStream-owned error;
missing subscription and plan;
cancelled subscription;
immediately before, exactly at, and after next_due;
Description
This issue evaluates and, if justified, adds a read-only contract preview for PayStream-owned charge validation. The preview can expose due status and computed charge information without mutating subscription state.
It must not be represented as a guarantee that the real
charge()will succeed.Requirements and Context
Soroban RPC already supports simulating the actual
charge()invocation before submission. In the current keeper,prepareTransaction()performs simulation/preparation before signing and sending. Therefore:charge()includes the external tokentransfer_frompath and is more faithful than a preview that deliberately omits it;The PR must document the exact SDK behavior observed with the pinned version and explain why an explicit contract API is still valuable. Valid reasons may include richer structured UI data, reusable PayStream-owned validation, or cheaper/readable diagnostics—not an unsupported promise of fee elimination.
API design
Evaluate whether
simulate_chargeis the clearest name. It may be confused with Soroban RPC simulation. A name such aspreview_chargemay better describe contract-level validation, but preserve the requested name if repository API consistency favors it.A minimal API may be:
A richer read-only result is preferable if it provides stable client value, for example:
Use only fields available in the merged contract. Missing usage-billing fields must not be invented before #36 lands.
The API must:
charge().charge().next_due, reduce allowance, reset usage, or alter pending adjustments.Shared logic
Refactor deterministic PayStream-owned validation and calculation into an internal helper used by both functions.
The helper should return validated data required by
charge(), not merely(), so the real charge does not reload or recalculate conflicting state.Do not force external side effects into the shared read helper. Separate:
The refactor must not change
charge()behavior, ordering, errors, authorization, amounts, or atomicity.External-state limitations
A preview that omits
transfer_fromcannot conclusively validate:Do not map these limitations to
Ok(())meaning “guaranteed success.” Document thatOkmeans PayStream's previewed checks passed at the observed ledger state.If querying token balance/allowance is added, it remains a snapshot and must use the exact token interface safely. It still cannot guarantee later execution.
Keeper integration
Keeper integration is optional and must be justified with measurements or clear behavior.
Before adding it, compare:
prepareTransaction()simulation of the real charge;Do not add the preview if it merely doubles RPC calls without reducing submitted failures or improving diagnostics. If added:
Suggested Execution
feat/simulate-charge-previewsrc/lib.rs— shared deterministic validation/calculation and preview APIsrc/test.rs— parity, read-only, privacy, and regression testsdocs/ARCHITECTURE.md— preview semantics and RPC-simulation comparisonkeeper/keeper.jsand keeper tests only if integration is justifiedfeat(core): add read-only charge preview with shared validationTest and Commit Steps
prepareTransaction()/simulation behavior.charge()behavior before refactoring.next_due;charge()regression behavior and exact balance/state updates;Okpreview does not bypass real preparation;crate-type = ["rlib"]on Windows if required:crate-type = ["cdylib", "rlib"]and build:Only stage keeper paths if they were changed.
Guidelines
Closes #<issue-number>.charge()behavior.27.0.3.Complexity
High (200 pts)