F-2025-0004·missing-validation

Missing duplicate prevention in setSubscription allows multiple subscriptions per vault

Fixednfterc721erc20
TL;DR

setSubscription accepts repeat calls for the same vaultId, creating duplicate entries in vaultOwnerSubscriptions while the underlying setAccess overwrites only the latest, breaking get and delete semantics.

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

Description

The setSubscription function lacks validation to prevent creating multiple subscription entries for the same vault ID. When called multiple times with the same vaultId, the function creates duplicate entries in the vaultOwnerSubscriptions array while overwriting the access control settings, violating the expected behavior that each vault should have only one subscription.

Vulnerable Scenario:

The following steps help understand the issue:

  1. Alice calls setSubscription("vault-123", 100, 3600, ...) to create a subscription.
  2. Alice later calls setSubscription("vault-123", 200, 7200, ...) with different parameters for the same vault.
  3. The function creates a second entry in vaultOwnerSubscriptions[alice] with the same vault ID.
  4. The underlying setAccess() overwrites the previous access control settings, making only the latest parameters active.
  5. getVaultOwnerSubscriptions() returns duplicate entries for the same vault.
  6. deleteSubscription() only removes the first matching entry, leaving orphaned duplicates.
03Section · Impact

Impact

Breaks the expected one-subscription-per-vault behavior, causing operational issues where vault owners can accidentally create multiple subscription entries for the same content, leading to UI confusion and incomplete deletion of subscription data.

04Section · Recommendation

Recommendation

Use a mapping to check for existing duplicates:

solidity
mapping(address => mapping(string => bool)) private hasSubscription;
function setSubscription(string calldata vaultId, ...) external {
require(!hasSubscription[msg.sender][vaultId], "Subscription exists");
hasSubscription[msg.sender][vaultId] = true;
// ... rest of logic
}
05Section · Resolution

Resolution

Ipal Network: Confirmed. An alternative solution has been implemented to prevent the creation of duplicate subscription entries for the same vault ID.

Zealynx: Partially Fixed: Duplicate prevention implemented correctly, but the solution adds update logic to the create function instead of separating concerns into distinct setSubscription (create) and updateSubscription (update) functions, making the code harder to maintain and reason about.

UPDATE: Fixed.

Status
Fixed
F-2025-0004

oog
zealynx

Smart Contract Security Digest

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

© 2026 Zealynx