F-2024-0002·missing-validation

Incorrect token transfer in _processERC20Payment() function

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

_processERC20Payment uses transferFrom on the contract's own address with the initiator as recipient, but no allowance exists, so every ERC-20 subscription payment reverts with insufficient allowance.

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

Description

The current design of the payment management within the SubExecutor contract introduces significant risks of logic failures in token transfer operations, potentially resulting in the inability to fulfill certain subscriptions and the potential locking of tokens.

The SubExecutor contract is designed to manage subscriptions and process automatic payments between accounts, using native/ERC20 tokens as a payment method. The current implementation presents an inconsistency in the authorization flow and execution of payments, specifically in the _processERC20Payment() function, which attempts to transfer ERC20 tokens from the SubExecutor contract to the initiator of the subscription using the transferFrom() method.

The payment functionality consists of the following steps:

  1. Payment Processing (processPayment()): invoked by the initiator to process a payment based on an active subscription. The logic ensures that only the initiator can execute the payment. It executes either _processERC20Payment() or _processNativePayment() whether it is a native payment or ERC20.

  2. ERC20 Token Transfer (_processERC20Payment()): the use of transferFrom() implies that the initiator has permitted the SubExecutor to withdraw tokens on their behalf, which does not align with the subscription model where the SubExecutor is the holder of the funds and must send them directly to the initiator. This approach misinterprets the authorization dynamics in ERC20 contracts, leading to a potential failure in payment execution due to "insufficient allowance".

solidity
/// @notice Processes an ERC20 payment for the subscription
function _processERC20Payment(SubStorage storage sub) internal {
IERC20 token = IERC20(sub.erc20Token);
uint256 balance = token.balanceOf(address(this));
require(balance >= sub.amount, "Insufficient token balance");
token.transferFrom(address(this), sub.initiator, sub.amount);
}
03Section · Impact

Impact

Every ERC-20 subscription payment reverts due to missing allowance, breaking the protocol's primary recurring-payment functionality for ERC-20 tokens.

04Section · Recommendation

Recommendation

Consider changing the code in the following way:

diff
- token.transferFrom(address(this), sub.initiator, sub.amount);
+ token.transfer(sub.initiator, sub.amount);
05Section · Resolution

Resolution

Team Response: Acknowledged and fixed as suggested.

Status
Fixed
Fix commit
79cddfeb6070
F-2024-0002

oog
zealynx

Smart Contract Security Digest

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

© 2026 Zealynx