F-2024-0004·incorrect-validation-order

Registering and paying a subscription is not possible in a single UserOperation batch

Fixedaccount-abstractionerc-4337subscriptiongithub.com/bastion-wallet
TL;DR

registerSubscription sets validAfter to block.timestamp while initiatePayment requires validAfter < block.timestamp, making both functions non-callable in the same block (incompatible with batched userOperations).

Severity
MEDIUM
Impact
MEDIUM
Likelihood
MEDIUM
Method
MManual review
CAT.
Complexity
LOW
Exploitability
LOW
02Section · Description

Description

The Bastion wallet supports batched userOperations. However, it will not be possible to register a subscription and initiate payment for it in the same batch. That is because with the current implementation, registerSubscription() sets validAfter to block.timestamp whereas the initiatePayment() checks if validAfter < block.timestamp. These circumstances make both functions non-callable in the same block.

solidity
function registerSubscription(
address _subscriber,
uint256 _amount,
uint256 _validUntil,
uint256 _paymentInterval,
address _erc20Token
) public {
function initiatePayment(address _subscriber) public nonReentrant {
03Section · Impact

Impact

Account-abstraction batches are a core selling point of the Bastion SDK. Users cannot register and pay in a single batched user operation, forcing them into two separate transactions and breaking the intended UX.

04Section · Recommendation

Recommendation

Ensure that in registerSubscription() the validAfter parameter is not hard coded but passed as input and is greater or equal to block.timestamp, the same applies to the initiatePayment() as well:

diff
function registerSubscription(
address _subscriber,
uint256 _amount,
uint256 _validUntil,
+ uint256 _validAfter,
uint256 _paymentInterval,
address _erc20Token
) public {
+ require(_validAfter >= block.timestamp, "Sub cannot be valid after a time in the past");
- validAfter: block.timestamp,
+ validAfter: _validAfter,
function initiatePayment(address _subscriber) public nonReentrant {
- require(subscription.validAfter < block.timestamp, "Subscription is not active");
+ require(subscription.validAfter <= block.timestamp, "Subscription is not active");
}
05Section · Resolution

Resolution

Team Response: Acknowledged and fixed as suggested.

Status
Fixed
Fix commit
79cddfeb6070
F-2024-0004

oog
zealynx

Smart Contract Security Digest

Monthly exploit breakdowns, audit checklists, and DeFi security research — straight to your inbox

© 2026 Zealynx