|
| 1 | +<page |
| 2 | + clientName="towns" |
| 3 | + reportDate="October 20, 2025" |
| 4 | + auditTitle="Towns A-12" |
| 5 | + auditVersion="1.0.0" |
| 6 | + repoUrl="https://github.com/towns-protocol/towns" |
| 7 | + repoCommitHash="571d65e38e62dbbe5c5af68bd9965716991ace44" |
| 8 | + layout="/library/audits/_layout.html" |
| 9 | +> |
| 10 | + |
| 11 | +<content-for name="schedule"> |
| 12 | + The security audit was performed by the Macro security team from October 7, 2025 to October 10, 2025. |
| 13 | +</content-for> |
| 14 | + |
| 15 | +<content-for name="spec"> |
| 16 | + <ul> |
| 17 | + <li>Discussions on Slack with the HNT team.</li> |
| 18 | + </ul> |
| 19 | + |
| 20 | + <template type="audit-markdown"> |
| 21 | + <h2 id="tmaar">Trust Model, Assumptions, and Accepted Risks (TMAAR)</h2> |
| 22 | + |
| 23 | + The `SubscriptionModule` is a smart contract module designed to be installed on a user's Modular Account (ERC-6900), enabling automated, recurring payments for spaces NFT based memberships. The system relies on trusted operators to trigger renewals, but is designed to ensure the operator cannot act without the user's pre-approved consent as defined by the subscription parameters. |
| 24 | + |
| 25 | + - Renewals are triggered by a trusted off-chain Operator, but executed with the user's own funds from their Modular Account. |
| 26 | + - The module validates that the user still owns the membership NFT before processing a renewal. |
| 27 | + - The module checks that the renewal is within the expected time window (after `nextRenewalTime` but before `GRACE_PERIOD` ends) to prevent spamming and premature renewals. |
| 28 | + - The renewal process is routed through the user's Modular Account using `executeWithRuntimeValidation`, ensuring the user's account, not the operator, is the `msg.sender` for the final `renewMembership` call. |
| 29 | + - The core parameters of the renewal, the target `space` contract and the `tokenId`, are immutable after the subscription is installed. |
| 30 | + |
| 31 | + ## Actors |
| 32 | + |
| 33 | + ### **Protocol Owner** |
| 34 | + |
| 35 | + The Protocol Owner is the entity that deployed and controls the `SubscriptionModule` diamond contract itself. This is a highly trusted role responsible for maintaining and upgrading the module's logic and the diamonds facets. |
| 36 | + |
| 37 | + - Is trusted to maintain and upgrade the module's logic securely. As the owner of the diamond, this role has the absolute power to change any part of the module's implementation at any time by performing a diamond cut (adding, replacing, or removing facets). Users trust that the owner will not introduce malicious code that could steal funds, modify subscription parameters without consent, or otherwise compromise the security of their accounts. |
| 38 | + - Is trusted to manage the operator whitelist responsibly. The Protocol Owner controls which addresses are designated as operators and are authorized to call `batchProcessRenewals`. |
| 39 | + - Cannot directly access user funds or change individual subscription parameters. The owner's power is over the contract's logic, not its state. Any malicious action would require deploying new, malicious code to the diamond first. |
| 40 | + - If compromised, a malicious Protocol Owner could replace the entire `SubscriptionModule` logic with a contract that drains funds from all subscribed users on the next renewal cycle. The primary mitigation is the security of the protocol's multisig process itself which governs the owner account. |
| 41 | + |
| 42 | + ### **Operator (`operator` role)** |
| 43 | + |
| 44 | + The Operator is a trusted, whitelisted off-chain entity responsible for monitoring all active subscriptions and triggering the on-chain renewal process. The contract is designed to limit its power, making it a transaction facilitator rather than a decision-maker. |
| 45 | + |
| 46 | + - Can trigger the `batchProcessRenewals` function for any subscription that is due. |
| 47 | + - Cannot renew a subscription that is not due, is paused, or for which the user is no longer the owner of the membership NFT. |
| 48 | + - Cannot alter the terms of a subscription (e.g., change the price or duration). |
| 49 | + - Cannot access or spend user funds directly; it can only initiate a transaction that the user's own account must validate and execute. |
| 50 | + - If compromised, an attacker could temporarily disrupt the service by failing to process renewals. However, they cannot steal funds or renew subscriptions against the rules of the contract. The primary mitigation is that any other authorized operator can process the due renewals and its role can be modified by the protocol owner. |
| 51 | + |
| 52 | + ### **Space Contract** |
| 53 | + |
| 54 | + The `space` is the target contract that manages the actual membership. It is trusted by the `SubscriptionModule` to provide accurate information about the membership's status and terms. |
| 55 | + |
| 56 | + - Can define the price and duration of a membership renewal. |
| 57 | + - Can verify ownership and execute the `renewMembership` function. |
| 58 | + - The `SubscriptionModule` checks for the `space` to be a legitimate contract. |
| 59 | + - The `SubscriptionModule` includes internal logic to handle changes in membership terms. If the `space` modifies the price or duration, the module will automatically pause renewals and emit appropriate events to notify the user. |
| 60 | + |
| 61 | + ### **End User (`ModularAccount` owner)** |
| 62 | + |
| 63 | + The User is the owner of the Modular Account and the ultimate principal in the system. They are trusted to make their own financial decisions by installing and configuring the module. |
| 64 | + |
| 65 | + - Can install the `SubscriptionModule` for a specific NFT membership, defining the `entityId`. |
| 66 | + - Can pause, resume, and uninstall the module at any time, giving them full control over their automated payments. |
| 67 | + - Cannot be forced into a subscription; installation is a voluntary, user-initiated action. |
| 68 | + - The primary risk for the user is a loss of funds through multiple renewals due to misconfiguration. The primary risk for the user is a loss of funds through multiple renewals due to misconfiguration. The module's internal checks protect against interacting with a malicious `space` contract or unexpected changes in the membership's terms (price/duration), serving as the primary mitigation against operator error. |
| 69 | + </template> |
| 70 | +</content-for> |
| 71 | + |
| 72 | +<content-for name="source-code"> |
| 73 | + |
| 74 | + <p>Specifically, we audited the following contracts within <i>contracts/src/apps/modules</i> repository directory:</p> |
| 75 | + |
| 76 | + <template type="file-hashes"> |
| 77 | + a880a4de3839ab8f6ab31671459e7368cd0199129e9ffde81f9b6064edb59f27 /subscription/ISubscriptionModule.sol |
| 78 | + 7a09015e00f310b007cf829d6f063ce6faf07fe445679062abc54ed74c1a12bd /subscription/SubscriptionModuleFacet.sol |
| 79 | + fcac773dcfdf83cbb8b05f205437a6db0be983f98c4c841aab17e1c53b5e1abc /subscription/SubscriptionModuleStorage.sol |
| 80 | + </template> |
| 81 | + |
| 82 | +</content-for> |
| 83 | + |
0 commit comments