Skip to content

feat(core): add simulate_subscribe() read-only preview function #45

Description

@Demilade10

Description

This issue evaluates and, if justified, adds a public read-only preview for the PayStream-owned plan validation used by subscribe().

The preview can confirm that a plan exists and currently accepts subscriptions. It must not be represented as proof that the later authenticated subscribe() transaction will succeed.

Blocked by #44: follow its final naming, shared-validation, result-shape, public-read, and RPC-simulation conventions.

Requirements and Context

The proposed API is:

pub fn simulate_subscribe(
    env: Env,
    plan_id: u64,
) -> Result<(), Error>

However, this signature receives neither subscriber nor allowance. It therefore cannot validate the complete real subscription path, including:

  • subscriber authorization;
  • invalid subscriber-specific state;
  • duplicate/trial eligibility where those features exist;
  • internal allowance validation added by later features;
  • live token-contract approval, expiration, or balance;
  • subscription-ID counter overflow;
  • timestamp plus interval/trial overflow;
  • state changes between preview and execution.

The PR must define this honestly as a plan-eligibility preview, not an exact subscription simulation.

API value and naming

Evaluate overlap with the existing public get_plan(plan_id), which already returns plan terms without authorization.

Choose and justify one approach:

  1. No new API

    • If get_plan() plus client-side active checking provides the same reliable value, document that conclusion and avoid unnecessary ABI surface.
  2. Validated plan preview

    • Add a clearer name such as preview_subscribe or get_subscribable_plan.
    • Return the validated plan or a dedicated preview struct.
  3. Requested simulate_subscribe API

A useful result may be:

pub fn simulate_subscribe(
    env: Env,
    plan_id: u64,
) -> Result<Plan, Error>

or a dedicated struct containing only merged, stable terms relevant to consent. If #28 plan versioning is merged, include the exact resolved plan ID/version and do not allow the preview to imply that a group alias cannot change before execution.

Shared validation

Extract the deterministic plan-eligibility checks into one internal helper used by both subscribe() and the preview.

The helper must:

  1. load the plan or return PlanNotFound;
  2. reject inactive plans with PlanInactive;
  3. return the validated concrete plan needed by subscribe();
  4. perform no authorization, token call, or storage mutation.

Keep subscriber authorization in the real subscribe() path. The preview intentionally has no require_auth() because it only reads public plan eligibility.

The refactor must not change real subscription behavior, authorization, ID allocation, due-time calculation, allowance state, trial behavior, or storage writes.

RPC simulation comparison

Document how the explicit preview differs from Soroban RPC simulation of the real subscribe() operation.

Confirm behavior using the pinned SDK/toolchain rather than assuming that a prospective user must pay a fee simply to simulate. Explain:

  • RPC simulation can execute the actual contract path and produce authorization requirements without submitting a transaction;
  • a contract preview may offer a smaller/richer public read response;
  • preview results can become stale;
  • successful preview does not guarantee successful authenticated execution.

Public data

Document that no authorization is required and anyone may query:

  • whether a plan exists and is active;
  • any plan terms returned by the preview;
  • resolved plan/version identifiers where applicable.

Do not return private or subscriber-specific information.

Suggested Execution

  • Branch name: feat/simulate-subscribe-preview
  • Files to touch:
    • src/lib.rs — shared plan validation and optional preview API
    • src/test.rs — parity, public-read, zero-mutation, and regression tests
    • docs/ARCHITECTURE.md — API purpose, no-auth decision, return shape, and RPC comparison
  • Example commit message: feat(core): add subscription eligibility preview with shared validation

Test and Commit Steps

  1. Confirm feat(core): add simulate_charge() read-only preview function #44 is merged and follow its established preview conventions.
  2. Compare the proposed function with get_plan() and document why new ABI surface is or is not justified.
  3. Decide the name and return type.
  4. Regression-test subscribe() before refactoring.
  5. Extract shared deterministic plan validation.
  6. If justified, implement the preview without authorization or mutation.
  7. Add tests covering:
    • active plan returns the expected result;
    • nonexistent plan returns PlanNotFound;
    • inactive plan returns PlanInactive;
    • preview requires no authorization;
    • repeated previews leave plans, subscriptions, counters, due times, allowances, and balances unchanged;
    • real subscribe() still requires subscriber authorization;
    • real subscription behavior remains unchanged;
    • successful preview does not validate token approval or subscriber-specific eligibility;
    • preview/execution stale-state behavior where testable;
    • exact version resolution if feat(core): add plan versioning scheme #28 is merged.
  8. Run the complete native suite using crate-type = ["rlib"] on Windows if required:
cargo fmt --all -- --check
cargo test
cargo clippy --all-targets --all-features -- -D warnings
  1. Restore crate-type = ["cdylib", "rlib"] and build:
stellar contract build --target wasm32v1-none
  1. Commit with:
git add src/lib.rs src/test.rs docs/ARCHITECTURE.md
git commit -m "feat(core): add subscription eligibility preview with shared validation"

Guidelines

  • Comment with confirmation that feat(core): add simulate_charge() read-only preview function #44 is merged and the proposed API value/name/result shape to request assignment.
  • Do not start or open a PR until assigned by a maintainer.
  • The PR description must include Closes #<issue-number>.
  • Do not duplicate get_plan() without a demonstrated benefit.
  • Do not claim the preview validates authorization, token approval, or guaranteed execution.
  • Keep subscribe() authorization unchanged.
  • Perform no storage mutation from the preview.
  • Do not renumber existing contract errors.
  • Do not upgrade Soroban SDK from 27.0.3.
  • Follow Conventional Commits and repository branch conventions.

Complexity

High (200 pts)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions