Skip to content

feat(acl): add acl scan to asset sync#954

Merged
atticusofsparta merged 4 commits into
developfrom
acl-sync-scan
Jun 24, 2026
Merged

feat(acl): add acl scan to asset sync#954
atticusofsparta merged 4 commits into
developfrom
acl-sync-scan

Conversation

@atticusofsparta

@atticusofsparta atticusofsparta commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • Added “Sync Ownership” flow to help sync on-chain owned ANTs into the local registry.
    • ANTs needing sync now show a “Sync needed” indicator, including a new warning in the notifications menu.
    • Added developer tools to scan for ACL drift and sync affected ANTs.
  • UX Updates
    • Replaced “Process ID” wording with “Token Address” across domains, tooltips, modals, and related UI elements.
  • Chores
    • Updated @ar.io/sdk dependency.

@atticusofsparta atticusofsparta requested a review from a team as a code owner June 22, 2026 19:14
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds ACL drift detection, sync entry points, and token address label updates across state, pages, tables, modals, and devtools. A new utility computes drift, state marks affected ANTs, and the UI exposes sync actions and indicators.

Changes

ACL Drift Detection and Ownership Sync

Layer / File(s) Summary
Core ACL drift detection
src/utils/aclSync.ts
Adds ANT ownership scanning, ACL set reads, and drift computation that returns drift records plus owned mint tracking.
State drift flag and reconciliation
src/state/contexts/ArNSState.tsx, src/state/actions/dispatchArNSUpdate.ts, src/components/data-display/tables/DomainsTable.tsx
Extends ANT state with needsOwnerSync, reconciles ArNS updates against ACL drift, and changes ANT retention logic when ground-truth ownership is available.
Sync ownership entry points and modal
src/components/modals/ant-management/SyncOwnershipModal/SyncOwnershipModal.tsx, src/components/pages/Manage/Manage.tsx, src/components/pages/ManageDomain/ManageDomain.tsx, src/components/layout/Navbar/NotificationMenu/NotificationMenu.tsx
Adds the modal for syncing ownership, wires Manage and ManageDomain to open it, and refreshes ArNS state after sync completes.
ACL drift devtools and SDK bump
src/components/pages/Settings/devtools/AclSync.tsx, src/components/pages/Settings/devtools/DevTools.tsx, package.json
Adds the devtools panel for scanning and syncing ACL drift, wires it into DevTools, and updates the SDK dependency version.
Token address label updates
src/components/Tooltips/ANTDetailsTip.tsx, src/components/Tooltips/DomainDetailsTip.tsx, src/components/cards/ANTCard/ANTCard.tsx, src/components/forms/DomainSettings/DomainSettings.tsx, src/components/inputs/text/NameTokenSelector/NameTokenSelector.tsx, src/components/modals/ant-management/*.tsx, src/utils/network.ts
Renames process-id wording to token-address wording across tooltips, cards, domain settings, modals, selector help text, and query validation messages.

Sequence Diagram(s)

sequenceDiagram
  participant ManagePage
  participant dispatchArNSUpdate
  participant computeAclDrift
  participant SyncOwnershipModal

  ManagePage->>dispatchArNSUpdate: refresh() with wallet + arioContract
  dispatchArNSUpdate->>computeAclDrift: owner, ario
  computeAclDrift-->>dispatchArNSUpdate: driftRecords, ownedMints
  dispatchArNSUpdate->>ManagePage: ants with needsOwnerSync
  ManagePage->>SyncOwnershipModal: open with outOfSyncItems
  SyncOwnershipModal->>SyncOwnershipModal: syncAcl per mint
  SyncOwnershipModal-->>ManagePage: onSynced()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • ar-io/arns-react#843: Directly reworks dispatchArNSUpdate's ANT ownership reconciliation and userDomains/setAnts handling, which this PR extends with ACL drift and needsOwnerSync.
  • ar-io/arns-react#934: Modifies the same dispatchArNSUpdate code path that this PR further changes for ground-truth ownership and ANT removal decisions.
  • ar-io/arns-react#942: Updates the @ar.io/sdk dependency version in package.json, the same file modified here for the SDK bump.

Suggested labels

codex

Suggested reviewers

  • dtfiedler

🐇 I sniff the drift in moonlit grass,
Then hop to sync the strays at last.
A token shines where process once was,
And badges bloom with gentle buzz.
One leap, one click, the path is clear,
My whiskers twitch: all ants are near.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and matches the main change: adding ACL scan/sync functionality to asset sync flows.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch acl-sync-scan

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.

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown

Permaweb Preview Deployed

Your preview is available at: https://arns-react-pr-954_arns.ar.io

Property Value
Transaction ID 32m3Mn1Y_wCNN4-tCudeDhKPcDh5YTPCDts6FwnsP02
ArNS Name arns
Undername arns-react-pr-954

This preview will be available permanently on Arweave via the AR.IO Network.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (1)
package.json (1)

32-32: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider pinning the alpha SDK exactly rather than with a caret.

^4.1.0-alpha.1 allows resolution to drift to other prerelease/minor builds of the 4.x line. Since this PR depends on freshly-added APIs in an alpha release, an exact pin (4.1.0-alpha.1) reduces the risk of a future install picking up a different, possibly breaking, alpha.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` at line 32, The "`@ar.io/sdk`" dependency is using a caret
version pin (^4.1.0-alpha.1) which allows npm to resolve to other prerelease or
minor versions of the 4.x line. Since this PR depends on APIs from this specific
alpha release, remove the caret character from the version string to pin it
exactly to "4.1.0-alpha.1", ensuring consistent installs and preventing
unexpected breaking changes from other alpha versions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/data-display/tables/DomainsTable.tsx`:
- Around line 250-256: The Link component at line 255 uses target="_blank" to
open an external URL in a new tab without including the rel="noopener
noreferrer" attribute, creating a tabnabbing security vulnerability. Add the
rel="noopener noreferrer" attribute to the Link component to prevent the newly
opened page from accessing the window.opener property and potentially
redirecting the original page. This hardening protects against tabnabbing
attacks when opening external links in new tabs.

In
`@src/components/modals/ant-management/SyncOwnershipModal/SyncOwnershipModal.tsx`:
- Around line 60-63: The catch block in the error handler is emitting the raw
error `e` to the eventEmitter without normalizing it to the project's expected
error contract. Before emitting the error via eventEmitter.emit('error', ...),
check whether the caught error is a NotificationOnlyError (expected/user-facing)
or a standard Error (unexpected), and normalize it accordingly so that expected
failures are properly classified and handled per the project's coding guidelines
for error types.
- Line 91: The style prop in SyncOwnershipModal contains an invalid CSS value
where `padding: 'none'` is being applied for mobile devices. The string 'none'
is not a valid CSS padding value and will be ignored by the browser. Replace
`'none'` with a valid CSS padding value such as `0` or `'0'` to properly remove
padding on mobile devices while maintaining the correct styling behavior.
- Around line 67-74: The `syncAll` function lacks a re-entry guard, allowing
multiple concurrent executions if the user clicks quickly before the UI disables
based on the `running` state. Add a guard check at the beginning of the
`syncAll` function that immediately returns if `running` is already true,
preventing duplicate non-idempotent `syncAcl` calls. This ensures only one sync
loop can execute at a time by blocking re-entry until the current operation
completes.

In `@src/state/actions/dispatchArNSUpdate.ts`:
- Around line 205-208: The issue is that `ownedMints` stays `null` only when
`computeAclDrift` throws, but when `scanOwnedAntAssetMints` silently swallows
parse errors via catch blocks, `computeAclDrift` returns a non-null empty Set
instead of null. This makes the drop condition unable to distinguish between a
successful scan finding zero assets versus a degraded scan that silently skipped
all assets, causing unintended mass-dropping of ANTs. Modify the logic to
distinguish scan success from scan degradation by checking whether
`getProgramAccounts` actually returned zero raw accounts versus whether all
assets were skipped/unparseable. When scan degradation is detected (all assets
skipped but getProgramAccounts returned accounts), fall back to the legacy owner
check condition instead of applying the `ownedMints !== null` branch that relies
on the unreliable empty Set result.

---

Nitpick comments:
In `@package.json`:
- Line 32: The "`@ar.io/sdk`" dependency is using a caret version pin
(^4.1.0-alpha.1) which allows npm to resolve to other prerelease or minor
versions of the 4.x line. Since this PR depends on APIs from this specific alpha
release, remove the caret character from the version string to pin it exactly to
"4.1.0-alpha.1", ensuring consistent installs and preventing unexpected breaking
changes from other alpha versions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c80d5e28-d0eb-4ce7-83b2-baf59dc25aa9

📥 Commits

Reviewing files that changed from the base of the PR and between 76eaf5f and 8d9ec23.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (9)
  • package.json
  • src/components/data-display/tables/DomainsTable.tsx
  • src/components/modals/ant-management/SyncOwnershipModal/SyncOwnershipModal.tsx
  • src/components/pages/Manage/Manage.tsx
  • src/components/pages/Settings/devtools/AclSync.tsx
  • src/components/pages/Settings/devtools/DevTools.tsx
  • src/state/actions/dispatchArNSUpdate.ts
  • src/state/contexts/ArNSState.tsx
  • src/utils/aclSync.ts

Comment on lines +250 to +256
<Link
className="link gap-2 w-fit whitespace-nowrap items-center"
to={`https://${encodeDomainToASCII(
row.getValue('name'),
)}.${NETWORK_DEFAULTS.ARNS.HOST}`}
target="_blank"
>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Harden external link opened in a new tab.

At Line 255, target="_blank" is used without rel="noopener noreferrer", which leaves a tabnabbing window.

🛡️ Proposed fix
                     <Link
                       className="link gap-2 w-fit whitespace-nowrap items-center"
                       to={`https://${encodeDomainToASCII(
                         row.getValue('name'),
                       )}.${NETWORK_DEFAULTS.ARNS.HOST}`}
                       target="_blank"
+                      rel="noopener noreferrer"
                     >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Link
className="link gap-2 w-fit whitespace-nowrap items-center"
to={`https://${encodeDomainToASCII(
row.getValue('name'),
)}.${NETWORK_DEFAULTS.ARNS.HOST}`}
target="_blank"
>
<Link
className="link gap-2 w-fit whitespace-nowrap items-center"
to={`https://${encodeDomainToASCII(
row.getValue('name'),
)}.${NETWORK_DEFAULTS.ARNS.HOST}`}
target="_blank"
rel="noopener noreferrer"
>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/data-display/tables/DomainsTable.tsx` around lines 250 - 256,
The Link component at line 255 uses target="_blank" to open an external URL in a
new tab without including the rel="noopener noreferrer" attribute, creating a
tabnabbing security vulnerability. Add the rel="noopener noreferrer" attribute
to the Link component to prevent the newly opened page from accessing the
window.opener property and potentially redirecting the original page. This
hardening protects against tabnabbing attacks when opening external links in new
tabs.

Comment on lines +60 to +63
} catch (e) {
setStatus((s) => ({ ...s, [mint]: 'error' }));
eventEmitter.emit('error', e);
return false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Normalize emitted errors to the project’s expected/unknown error contract.

At Line 62, emitting raw e bypasses the expected NotificationOnlyError vs Error split, so expected wallet/user-facing failures can be misclassified and over-reported.

As per coding guidelines, **/*.{ts,tsx}: “Use NotificationOnlyError for expected/user-facing errors ... Use standard Error for unexpected errors ... Use eventEmitter ... for error handling.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/components/modals/ant-management/SyncOwnershipModal/SyncOwnershipModal.tsx`
around lines 60 - 63, The catch block in the error handler is emitting the raw
error `e` to the eventEmitter without normalizing it to the project's expected
error contract. Before emitting the error via eventEmitter.emit('error', ...),
check whether the caught error is a NotificationOnlyError (expected/user-facing)
or a standard Error (unexpected), and normalize it accordingly so that expected
failures are properly classified and handled per the project's coding guidelines
for error types.

Source: Coding guidelines

Comment on lines +67 to +74
async function syncAll() {
setRunning(true);
try {
// Sequential — one signed tx per ANT; parallel would prompt the wallet
// N times at once.
for (const item of remaining) {
await syncOne(item.mint);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Guard syncAll against re-entry before running state is committed.

At Line 67, a fast double-click can start multiple loops before the disabled state is rendered, causing duplicate non-idempotent syncAcl calls.

🔒 Proposed fix
-import { useState } from 'react';
+import { useRef, useState } from 'react';
@@
   const [running, setRunning] = useState(false);
+  const runningRef = useRef(false);
@@
   async function syncAll() {
+    if (runningRef.current) return;
+    runningRef.current = true;
     setRunning(true);
     try {
       // Sequential — one signed tx per ANT; parallel would prompt the wallet
       // N times at once.
       for (const item of remaining) {
         await syncOne(item.mint);
       }
     } finally {
+      runningRef.current = false;
       setRunning(false);
     }
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/components/modals/ant-management/SyncOwnershipModal/SyncOwnershipModal.tsx`
around lines 67 - 74, The `syncAll` function lacks a re-entry guard, allowing
multiple concurrent executions if the user clicks quickly before the UI disables
based on the `running` state. Add a guard check at the beginning of the
`syncAll` function that immediately returns if `running` is already true,
preventing duplicate non-idempotent `syncAcl` calls. This ensures only one sync
loop can execute at a time by blocking re-entry until the current operation
completes.

return (
<div
className="modal-container"
style={isMobile ? { padding: 'none' } : {}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use a valid CSS value for mobile padding.

At Line 91, padding: 'none' is invalid and ignored. Use 0 (or '0') to reliably remove padding.

🎯 Proposed fix
-      style={isMobile ? { padding: 'none' } : {}}
+      style={isMobile ? { padding: 0 } : {}}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
style={isMobile ? { padding: 'none' } : {}}
style={isMobile ? { padding: 0 } : {}}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/components/modals/ant-management/SyncOwnershipModal/SyncOwnershipModal.tsx`
at line 91, The style prop in SyncOwnershipModal contains an invalid CSS value
where `padding: 'none'` is being applied for mobile devices. The string 'none'
is not a valid CSS padding value and will be ignored by the browser. Replace
`'none'` with a valid CSS padding value such as `0` or `'0'` to properly remove
padding on mobile devices while maintaining the correct styling behavior.

Comment thread src/state/actions/dispatchArNSUpdate.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/layout/Navbar/NotificationMenu/NotificationMenu.tsx`:
- Around line 99-104: Fix the singular/plural agreement in the notification
message built in NotificationMenu so the verb matches the count. Update the
message JSX in the NotificationMenu component to make the verb conditional
alongside the noun, using the same antsNeedingSync check that already controls
the “name(s)” text, so the singular case reads naturally and the plural case
remains unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3c84901e-07bb-4525-bb53-4ca50a17a482

📥 Commits

Reviewing files that changed from the base of the PR and between 8d9ec23 and eb5f985.

📒 Files selected for processing (15)
  • src/components/Tooltips/ANTDetailsTip.tsx
  • src/components/Tooltips/DomainDetailsTip.tsx
  • src/components/cards/ANTCard/ANTCard.tsx
  • src/components/data-display/tables/DomainsTable.tsx
  • src/components/forms/DomainSettings/DomainSettings.tsx
  • src/components/inputs/text/NameTokenSelector/NameTokenSelector.tsx
  • src/components/layout/Navbar/NotificationMenu/NotificationMenu.tsx
  • src/components/modals/ant-management/AddControllerModal/AddControllerModal.tsx
  • src/components/modals/ant-management/ReassignNameModal/ReassignNameModal.tsx
  • src/components/modals/ant-management/RemoveControllerModal/RemoveControllerModal.tsx
  • src/components/modals/ant-management/TransferANTModal/TransferANTModal.tsx
  • src/components/pages/Manage/Manage.tsx
  • src/components/pages/ManageDomain/ManageDomain.tsx
  • src/utils/aclSync.ts
  • src/utils/network.ts
✅ Files skipped from review due to trivial changes (7)
  • src/components/Tooltips/ANTDetailsTip.tsx
  • src/components/inputs/text/NameTokenSelector/NameTokenSelector.tsx
  • src/components/Tooltips/DomainDetailsTip.tsx
  • src/components/modals/ant-management/RemoveControllerModal/RemoveControllerModal.tsx
  • src/components/modals/ant-management/AddControllerModal/AddControllerModal.tsx
  • src/utils/network.ts
  • src/components/modals/ant-management/TransferANTModal/TransferANTModal.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/pages/Manage/Manage.tsx
  • src/components/data-display/tables/DomainsTable.tsx

Comment on lines +99 to +104
message: (
<span className="w-full">
<span className="text-bold">{antsNeedingSync}</span> name
{antsNeedingSync === 1 ? '' : 's'} you own need ownership synced.
</span>
),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix subject-verb agreement in the singular message.

When antsNeedingSync === 1, the message reads "1 name you own need ownership synced", which is grammatically incorrect. The verb should be conditional like the noun.

✏️ Proposed fix
-        <span className="text-bold">{antsNeedingSync}</span> name
-        {antsNeedingSync === 1 ? '' : 's'} you own need ownership synced.
+        <span className="text-bold">{antsNeedingSync}</span> name
+        {antsNeedingSync === 1 ? ' you own needs' : 's you own need'} ownership
+        synced.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
message: (
<span className="w-full">
<span className="text-bold">{antsNeedingSync}</span> name
{antsNeedingSync === 1 ? '' : 's'} you own need ownership synced.
</span>
),
message: (
<span className="w-full">
<span className="text-bold">{antsNeedingSync}</span> name
{antsNeedingSync === 1 ? ' you own needs' : 's you own need'} ownership
synced.
</span>
),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/layout/Navbar/NotificationMenu/NotificationMenu.tsx` around
lines 99 - 104, Fix the singular/plural agreement in the notification message
built in NotificationMenu so the verb matches the count. Update the message JSX
in the NotificationMenu component to make the verb conditional alongside the
noun, using the same antsNeedingSync check that already controls the “name(s)”
text, so the singular case reads naturally and the plural case remains
unchanged.

@atticusofsparta atticusofsparta merged commit 7b6c2a7 into develop Jun 24, 2026
4 checks passed
@atticusofsparta atticusofsparta deleted the acl-sync-scan branch June 24, 2026 17:45
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.

1 participant