Skip to content

Add invoice cloneability pre-flight validator #486

Description

@Kingsman-99

Description

Cloning an existing invoice to create a new one with the same structure can fail silently if the source invoice contains expired trustlines, removed recipients, or assets that are no longer available. This issue adds an InvoiceCloneabilityValidator that runs a comprehensive pre-flight check on a source invoice before cloning, identifying every field that would produce an invalid clone and providing field-level remediation hints.

Technical Context

  • New file: src/preflight/InvoiceCloneabilityValidator.ts; result type: CloneabilityReport { invoiceId: string; cloneable: boolean; fieldReports: FieldReport[] }
  • FieldReport: { field: string; valid: boolean; reason?: string; suggestedFix?: string }
  • Checks performed: (1) all recipient accounts still exist and have required trustlines, (2) the invoice asset is still active on the network, (3) the cloned deadline would be in the future (relative to current ledger time + minDeadlineBufferMs), (4) no CANCELLED/DISPUTED status that prevents cloning
  • Uses RecipientBalancePreCheck (Issue 45) for recipient checks and SorobanRpc.Server.getAssetStats() for asset validity
  • SplitClient.cloneInvoice(invoiceId, overrides?) calls the validator and throws InvoiceNotCloneableError listing all failing fields

Acceptance Criteria

  • A source invoice with an expired deadline and a removed recipient returns a CloneabilityReport with two failing FieldReport entries
  • A fully valid source invoice returns { cloneable: true, fieldReports: [] } and cloneInvoice() proceeds
  • The deadline check uses ledger time (from getLatestLedger()) not Date.now() to avoid local clock skew
  • InvoiceNotCloneableError includes the full CloneabilityReport in its details field
  • skipValidation: true option in cloneInvoice() bypasses the validator entirely for advanced users
  • All CI checks (npm test, npm run build, ESLint) pass and the branch has no merge conflicts

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions