Summary
The recent hardening patch addressed batch-size enforcement, paginated reads, and early overflow rejection. Two meaningful product and abuse-resistance gaps still remain in the subscription contract design:
- free-trial protection is scoped only to
(subscriber, service_id), so the same subscriber can still farm trial-only access across multiple services
- failed charges immediately disable
auto_renew, but there is no recovery or retry surface for subscribers and merchants
Remaining Findings
1. Cross-service trial farming
The current dedup key is SubServicePair(Address, u64), which prevents repeated free trials only for the same service. It does not provide any cooldown or global policy for trial-only subscriptions across services.
Impact:
- repeated zero-revenue trial cycling across merchant offerings
- no on-chain hook for merchants who want a stricter anti-abuse policy
Possible directions:
- subscriber-level trial cooldown storage
- merchant-configurable trial policy at service registration time
- optional allowlist / stricter trial gating for trial-only subscriptions
2. No recovery path after charge failure
process() sets auto_renew = false on payment failure and emits chg_fail, but the contract exposes no first-class retry or recovery method.
Impact:
- transient low-balance or allowance issues permanently disable renewal until a manual resubscribe or toggle flow is stitched together off-chain
- merchants cannot query failed renewals through a dedicated API
- subscribers lack a direct remediation path tied to the failure state
Possible directions:
- add failed-charge query surfaces for merchants/subscribers
- add explicit retry / restore-renewal methods after balance or allowance recovery
- consider richer failure reasons or structured failure events for downstream automation
Why a Follow-up Issue
These changes are broader than the patch already proposed in PR #11 and should be reviewed separately to keep the scaling fix narrowly scoped.
Summary
The recent hardening patch addressed batch-size enforcement, paginated reads, and early overflow rejection. Two meaningful product and abuse-resistance gaps still remain in the subscription contract design:
(subscriber, service_id), so the same subscriber can still farm trial-only access across multiple servicesauto_renew, but there is no recovery or retry surface for subscribers and merchantsRemaining Findings
1. Cross-service trial farming
The current dedup key is
SubServicePair(Address, u64), which prevents repeated free trials only for the same service. It does not provide any cooldown or global policy for trial-only subscriptions across services.Impact:
Possible directions:
2. No recovery path after charge failure
process()setsauto_renew = falseon payment failure and emitschg_fail, but the contract exposes no first-class retry or recovery method.Impact:
Possible directions:
Why a Follow-up Issue
These changes are broader than the patch already proposed in PR #11 and should be reviewed separately to keep the scaling fix narrowly scoped.