Skip to content

Conversation

kilnhast
Copy link
Collaborator

@kilnhast kilnhast commented Sep 20, 2025

This pull request closes #82 by implementing a submission fee system for the RepositoryRegistry contract:

  • Adds ERC20 fee payment required for repository submission.
  • Fee amount and fee wallet are configurable by the contract owner.
  • Integrates SafeERC20 for secure token transfers.
  • Updates events to include fee payment details.
  • Adds/updates tests to cover fee payment, allowance failures, and correct fee transfer.
  • Ensures backward compatibility for other contract features.

This enables protocol-level monetization and anti-spam for repository submissions.

Summary by CodeRabbit

  • New Features
    • Submitting a repository now requires paying a fee in a specified ERC‑20 token.
    • Users must approve the app to spend the token before submission.
    • The paid fee is recorded alongside submissions for transparency.
    • Admins can update the submission fee and the destination wallet without downtime.

Copy link

coderabbitai bot commented Sep 20, 2025

Walkthrough

Adds ERC20-based submission fees to RepositoryRegistry: new token, fee wallet, and fee amount state; fee transfer on submit; updated events and constructor; owner-setters for fee and wallet. Tests introduce a mock token, minting, approvals, fee assertions, and failure on insufficient allowance.

Changes

Cohort / File(s) Summary
Smart contract: fee-enabled submissions
contracts/RepositoryRegistry.sol
Introduces IERC20 token, feeWallet, submissionFee; updates constructor; enforces fee via safeTransferFrom in submitRepository; expands RepositorySubmitted event with feePaid; adds SubmissionFeeUpdated and FeeWalletUpdated; adds onlyOwner setters setSubmissionFee and setFeeWallet; public accessors.
Tests: tokenized fee flow
test/RepositoryRegistry.ts
Adds MockDEVToken fixture, mints to maintainers, sets approvals; instantiates registry with token, fee wallet, fee; asserts fee transfers on submission; tests failure on insufficient allowance; updates assertions to include fee wallet/token interactions.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Maintainer
  participant Registry as RepositoryRegistry
  participant Token as ERC20 Token
  participant Wallet as Fee Wallet

  rect rgba(230,245,255,0.6)
  Maintainer->>Registry: submitRepository(...)
  note right of Registry: Validate inputs
  Registry->>Token: safeTransferFrom(Maintainer, Wallet, submissionFee)
  Token-->>Registry: transfer success/fail
  alt Fee transfer succeeds
    Registry-->>Wallet: (Fee credited via Token)
    Registry-->>Maintainer: emit RepositorySubmitted(id, maintainer, feePaid)
  else Fee transfer fails
    Registry-->>Maintainer: revert (insufficient allowance/balance)
  end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • metanodreamer
  • nowak-token

Poem

A token hops from paw to purse, so neat, so fee!
I stamp my repo, twitch my nose, submit with glee.
Events go pop, the wallet rings—ker-ching!
Owner tweaks the knobs: new fee, new spring.
On-chain clover fields, I bound—ERC20-free? Not me! 🐇💠

Pre-merge checks and finishing touches

❌ Failed checks (3 warnings)
Check name Status Explanation Resolution
Linked Issues Check ⚠️ Warning The PR does not implement the objectives of linked issue [#82] (implement withdrawVote with validation, helper functions, vote-tracking updates, and appropriate events); instead the changes add an ERC20 submission fee system and associated tests, and there is no withdrawVote function or related state/mapping updates present in the provided summaries. Either implement the withdrawVote functionality and all required validations/state changes described in issue #82, or remove/replace the issue link and update the PR description to reference the correct ticket for the fee feature so issue tracking remains accurate.
Out of Scope Changes Check ⚠️ Warning The PR introduces feature work (ERC20 fee variables, constructor change, fee transfer logic, new events, and tests touching RepositoryRegistry.sol and test/RepositoryRegistry.ts) that is unrelated to the withdrawVote work specified in linked issue #82, so these changes are out-of-scope relative to that issue. If the intent is to add the fee system, update or remove the association with issue #82 and target the correct issue; if the intent is to close #82, split or revert the fee changes and add the withdrawVote implementation with the required validations and state updates in this or a follow-up PR.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "feat: Add ERC20 submission fee system to RepositoryRegistry" is concise and accurately reflects the primary change in the diff (adding token, feeWallet, submissionFee, fee transfer logic, and related events/tests in RepositoryRegistry).
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/repo-registry-fee

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (8)
test/RepositoryRegistry.ts (5)

203-235: Also assert payer balance delta and registry never holds fees

Strengthen this fee test by checking:

  • maintainer1 balance decreases by submissionFee
  • registry token balance stays 0 (fees go straight to feeWallet)
-      const feeWalletBefore = await mockDEVToken.read.balanceOf([feeWallet.account.address]);
+      const feeWalletBefore = await mockDEVToken.read.balanceOf([feeWallet.account.address]);
+      const maintainerBefore = await mockDEVToken.read.balanceOf([maintainer1.account.address]);
+      const registryBefore = await mockDEVToken.read.balanceOf([repositoryRegistry.address]);

@@
-      const feeWalletAfter = await mockDEVToken.read.balanceOf([feeWallet.account.address]);
+      const feeWalletAfter = await mockDEVToken.read.balanceOf([feeWallet.account.address]);
+      const maintainerAfter = await mockDEVToken.read.balanceOf([maintainer1.account.address]);
+      const registryAfter = await mockDEVToken.read.balanceOf([repositoryRegistry.address]);

@@
       expect(repo.tags).to.deep.equal(tags);
       expect(feeWalletAfter - feeWalletBefore).to.equal(submissionFee);
+      expect(maintainerBefore - maintainerAfter).to.equal(submissionFee);
+      expect(registryBefore).to.equal(0n);
+      expect(registryAfter).to.equal(0n);

255-273: Decode and assert RepositorySubmitted event args (id, maintainer, feePaid)

Current check only asserts “has logs”. Decode the specific event and assert its parameters, including feePaid.

-      const receipt = await publicClient.waitForTransactionReceipt({ hash });
-
-      expect(receipt.logs).to.have.lengthOf.at.least(1);
-      expect(receipt.status).to.equal("success");
+      const receipt = await publicClient.waitForTransactionReceipt({ hash });
+      expect(receipt.status).to.equal("success");
+
+      // Decode RepositorySubmitted(id, maintainer, feePaid)
+      const { decodeEventLog, parseAbiItem } = await import("viem");
+      const evt = parseAbiItem(
+        'event RepositorySubmitted(uint256 indexed id, address indexed maintainer, uint256 feePaid)'
+      );
+      const log = receipt.logs.find(l => l.address.toLowerCase() === repositoryRegistry.address.toLowerCase());
+      expect(log).to.not.be.undefined;
+      const decoded = decodeEventLog({ ...log!, abi: [evt] });
+      expect(decoded.eventName).to.equal("RepositorySubmitted");
+      // id should be 1 on first submission
+      expect(decoded.args.id).to.equal(1n);
+      expect(decoded.args.maintainer.toLowerCase()).to.equal(maintainer1.account.address.toLowerCase());
+      // feePaid should equal the configured submissionFee
+      // (cannot import fixture variables here, so just assert > 0n or thread it through if accessible)
+      expect(decoded.args.feePaid > 0n).to.be.true;

236-253: Tighter assertion on allowance failure

Prefer asserting a specific revert (custom error or message) to avoid false positives. If your MockDEVToken uses OZ v5, it likely reverts with ERC20InsufficientAllowance (custom error).

If it’s OZ v5, update to:

-      ).to.be.rejected;
+      ).to.be.rejectedWith('ERC20InsufficientAllowance');

Else, adapt to your mock’s actual revert. If you share MockDEVToken.sol, I can tailor the exact assertion.


48-57: Scope approvals to the exact fee to reflect real usage

Approving mintAmount is over‑permissive. Approve just submissionFee inside tests that submit, and skip approval entirely in tests that expect allowance failure.

-    await mockDEVToken.write.approve([
-      repositoryRegistry.address,
-      mintAmount,
-    ], { account: maintainer1.account });
+    await mockDEVToken.write.approve([
+      repositoryRegistry.address,
+      submissionFee,
+    ], { account: maintainer1.account });

Alternatively, create a second fixture without approvals for the “insufficient allowance” test to avoid mutation in the test body.


12-21: Process mismatch: PR objective references withdrawVote, but tests/changes add ERC20 fees

Linked issue says “withdrawVote(...)” while this PR adds submission fees. Confirm linkage or update the issue reference so reviewers aren’t misled.

Also applies to: 33-36

contracts/RepositoryRegistry.sol (3)

104-114: Admin: permit disabling fees and add guardrails

  • Setting fee to 0 should intentionally disable charging (aligned with the refactor above).
  • Consider upper‑bound sanity checks to avoid accidental large fees (optional).

54-61: Consistent not‑found/error messaging across functions

updateRepository reverts with “Not owner” for missing IDs, while deactivateRepository uses “Repository does not exist”. Consider aligning for clearer DX (e.g., first check existence, then ownership).


70-99: Minor: gas/style

  • repoCounter += 1; can be ++repoCounter; and cached as uint256 id = ++repoCounter; to avoid repeated map indexing when emitting or extending logic.
  • tags: tags copies calldata array into storage; this is fine in 0.8.x, just noting the cost. No action required.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6afab64 and e80c1cd.

📒 Files selected for processing (2)
  • contracts/RepositoryRegistry.sol (4 hunks)
  • test/RepositoryRegistry.ts (4 hunks)
🔇 Additional comments (2)
contracts/RepositoryRegistry.sol (2)

4-9: Dependency/version sanity check (OZ v5 vs v4 pathing)

ReentrancyGuard lives under utils in OZ v5, under security in v4. Ensure the repo uses OZ v5 to match this import; otherwise switch the path.

I can auto‑verify with a quick script if you want.


32-38: Restore legacy RepositorySubmitted signature and emit a separate fee event

Changing RepositorySubmitted to include feePaid alters the event signature (topic) and will break existing indexers/consumers. Restore/keep the original two‑arg event (id, maintainer) and emit it alongside a new fee event (e.g., SubmissionFeePaid(uint256 id, address payer, uint256 amount)) from submitRepository so consumers keep working.

File: contracts/RepositoryRegistry.sol — current events:

    // Events
    event RepositorySubmitted(uint256 indexed id, address indexed maintainer, uint256 feePaid);
    event RepositoryUpdated(uint256 indexed id, address indexed maintainer);
    event RepositoryDeactivated(uint256 indexed id);
    event SubmissionFeeUpdated(uint256 oldFee, uint256 newFee);
    event FeeWalletUpdated(address oldWallet, address newWallet);

Comment on lines +81 to +84
// Require fee payment
require(submissionFee > 0, "Submission fee not set");
token.safeTransferFrom(msg.sender, feeWallet, submissionFee);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Allow zero fee to disable charging; move external transfer after state updates (CEI)

  • Requiring submissionFee > 0 prevents disabling fees, contradicting “Backward compatibility maintained.”
  • Perform the ERC20 transfer after storage writes to follow checks‑effects‑interactions. nonReentrant remains a good guard; a revert still rolls back state.
-        // Require fee payment
-        require(submissionFee > 0, "Submission fee not set");
-        token.safeTransferFrom(msg.sender, feeWallet, submissionFee);
+        // Effects first, interactions later (fee may be zero to disable charging)
@@
-        // Emit event
-        emit RepositorySubmitted(repoCounter, msg.sender, submissionFee);
+        // Interactions (charge fee if enabled)
+        uint256 fee = submissionFee;
+        if (fee > 0) {
+            token.safeTransferFrom(msg.sender, feeWallet, fee);
+        }
+        // Emit event
+        emit RepositorySubmitted(repoCounter, msg.sender, fee);

Also applies to: 100-103

🤖 Prompt for AI Agents
In contracts/RepositoryRegistry.sol around lines 81-84 (and similarly lines
100-103), the code currently requires submissionFee > 0 and performs the ERC20
transfer before updating storage; remove the require so a zero submissionFee is
allowed (disabling charging), and move the token.safeTransferFrom call to after
the state changes to follow Checks-Effects-Interactions (you can keep
nonReentrant); ensure you still call safeTransferFrom only when submissionFee >
0 to avoid unnecessary external calls when fee is zero.

@metanodreamer metanodreamer merged commit 3b4e54f into main Sep 20, 2025
1 check passed
@nowak-token nowak-token deleted the feat/repo-registry-fee branch September 25, 2025 18:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement withdrawVote function with validation and state updates
2 participants