As of 24 Oct 2026, Subscription Token Protocol V2 has 2 audit reports by 1 security firm on Trustblock; the latest is by Quantstamp, dated 30 May 2024. Those reports list no open critical findings.
Record last updated . It holds audits and findings, contracts and incidents up to Oct 2026. Trustblock does not review code.
Newest first
| Audit | Security firm | Date | Scope | Critical findings | Report |
|---|---|---|---|---|---|
| Subscription Token Protocol V2 | Security firm: Quantstamp | Date: | Scope: Repository at commit 2b993d1 | No open critical findings | Open · certificate.quantstamp.com |
| Subscription Token - Fabric | Security firm: Quantstamp | Date: | Scope: Repository at commit 504dd06 | No open critical findings | Open · certificate.quantstamp.com |
Statuses are the security firm’s own, from each report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 0 | 0 | 2 |
| Medium | 0 | 2 | 3 |
| Low | 0 | 6 | 12 |
Smart contracts will often have `owner` variables to designate the person with special privileges to make modifications to the smart contract. `Owner` can: * set/update the transfer recipient * set/update the supply cap * pause/unpause the contract * grant time to any accounts * update contract metadata * refund remaining purchase time to any account * withdraw contract funds to any account * create/delete referral codes
Open the reportThe function `mintWithReferralFor()` takes both the `referralCode` and the `referrer` as user input. This means the user is free to choose whatever value they would like for `referrer`, including their own address. Take the following scenario as an example: 1. Alice provides Bob with her referral code. 2. Bob purchases a subscription for himself, but instead of specifying Alice as the `referrer`, he specifies himself. 3. Bob gets a discount on his purchase, while Alice does not receive any payout.
Open the reportThe identifier of a reward curve is stored in an `uint8`. As a result, the function `RewardPoolLib.createCurve()` will revert if there are already `256` created reward curves. This limitation should be documented to creators.
Open the reportThe addresses receiving the fees for the client and the protocol can be updated with the functions `updateClientFeeRecipient()` and `updateProtocolFeeRecipient()`. However, only the current recipient can execute this function. As a result, if an incorrect address owned by no one or unable to receive fees is set, the amount transferred to this address can be considered lost. The likelihood is assessed as Low.
Open the reportWe include the observed privileged roles and their capabilities below: `STPV2Factory` - `owner` as in the owner of the Subscription Factory contract; the Subscription Token Protocol team. - `setProtocolFeeRecipient()` - `setDeployFee()` - Client fee recipient - `updateClientFeeRecipient()` - Protocol fee recipient - `updateProtocolFeeRecipient()` `STPV2` - `owner`, as in, the deployer of the Subscription Token contract; the entity to which people are subscribing - `refund()` an account and clear their subscription - `grantTime()` or `revokeTime()` to an address subscribing - `issueRewardShares()` to grant rewards to a user, independent of any reward curves defined - `transferFunds()` to an address different from the `_transferRecipient` stored in the contracts; although, anyone can invoke this function to transfer to the defined `_transferRecipient` address - `updateMetadata()` to redefine the URI used for the NFTs - `setTransferRecipient()` defining a new address to receive the owner's portion of the subscription revenue - `setGlobalSupplyCap()` defining the amount of subscriptions that can exist for this given Subscription - `createTier()` defining a new subscription tier available for subscribers, or `updateTier()` redefining the parameters for an existing tier - `setReferralCode()` assigning a basis points reward for a specified referrer address - `issueRewardShares()` to give reward shares to a specified account - `createRewardCurve()` for a subscription - `recoverCurrency()` to withdraw any funds that are not the currency defined for the subscription to any specified address - `ROLE_MANAGER` - `grantTime()` or `revokeTime()`to an address subscribing - `updateMetadata()` to redefine the URI used for the NFTs - `createTier()` defining a new subscription tier available for subscribers, or `updateTier()` redefining the parameters for an existing tier - `createRewardCurve()` for a subscription - `ROLE_AGENT` - `grantTime()` or `revokeTime()` to an address subscribing
Open the reportWhile there are some mitigation strategies to prevent users from withdrawing the contract-denominated currency in the contract, as shown in `recoverCurrency()`, it does not actually prevent the owner from stealing all the funds in the contract. Note that users deposit currency into the contract through `purchase()`, which allocates the deposited amount as a reward. So, to drain the contract's currency, it suffices to claim all the rewards available allocated in the contract. To this end, the owner can call `issueRewardShares()` to issue a large number of shares to an account he controls, diluting the shares of other users and effectively stealing the rewards from other users.
Open the reportThe hook `_beforeTokenTransfer()` checks if the `to` address has a subscription, whether it is active or not. If that is the case, it prevents the transfer. This will prevent transferring to users who have expired subscriptions.
Open the reportTo update the `_feeCollector`, the call to `updateFeeRecipient()` must come from the current `_feeCollector`. If the `_feeCollector` was not initially set to a non zero address during initialization, then the `_feeCollector` can't be updated from the duration of the contract's life cycle.
Open the reportThe reports name a repository or files. Open a report to see its scope.
Is this your project? Show this record on your own website. The label states what the record holds, links to this page and updates by itself. It is free and needs no sign-in.
Automatic: the three strongest facts of this project’s own record (its last audit and two more), highlighted. They update with the record. Picking a fact turns Automatic off.
<head><script src="https://widget.trustblock.run/project-widget.min.js" defer></script></head><body><divdata-tb-project-widgetdata-tb-project-slug="subscription-token-protocol-v2"></div></body>
Paste the code where the label should appear. It uses no API credit and updates by itself.
Something wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.
Tell us