Skip to content

feat: Safe admin integration + V2 audit follow-ups#8

Merged
Wieedze merged 9 commits intomainfrom
feat/safe-admin-integration
Apr 28, 2026
Merged

feat: Safe admin integration + V2 audit follow-ups#8
Wieedze merged 9 commits intomainfrom
feat/safe-admin-integration

Conversation

@Wieedze
Copy link
Copy Markdown
Owner

@Wieedze Wieedze commented Apr 28, 2026

Bundle of work tracks landed on this branch — see commits + .claude/SAFE_INTEGRATION_PLAN.md for context.

Wieedze and others added 9 commits April 28, 2026 08:42
Two small UX wins around the Safe story:

Deploy form (packages/webapp/src/pages/Deploy.tsx):
- "+ Add my wallet" button (when connected) — appends the EOA to the
  admins textarea
- "+ Add Safe address" toggle — opens an inline input with an Add
  button that validates checksummed-address before appending
- appendAdmin helper handles dedup so the same address can't land
  twice
- Existing "Heads up" hint now links to /docs/safe-admin

Removes friction for the recommended pattern (EOA + Safe co-admins
from genesis) — users no longer have to copy-paste two addresses.

ProxyAdminSafeBanner (packages/webapp/src/components/):
- Both EOA banners (mainnet rose + dev amber) now link to
  /docs/safe-admin via react-router Link
- "Safe admin guide" call-to-action is the actionable next step for
  someone who lands on the warning

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Foundation for routing webapp admin actions through a Safe multisig
when one is whitelisted, instead of forcing users to a CLI.

src/hooks/useSafePropose.ts:
- Builds an AdminOp into a SafeTx via packages/safe-tx (buildSafeTx)
- Signs EIP-712 via wagmi.signTypedData (no private key on the
  webapp side — uses the connected wallet's signing UI)
- Posts to Den's STS via api-kit (POST .../multisig-transactions/)
- Returns { propose, isProposing, proposed, error, denUrl, reset }
- Caller passes the target Safe address (typically derived from the
  proxy's admins list)

src/hooks/useSafeAdmin.ts:
- Convenience: scans a proxy's admins via useAdmins + useSafeStatuses,
  returns the first detected Safe address (or undefined)
- Multi-Safe edge case: takes the first match, picker can come later

src/components/SetFeesPanel.tsx (PoC integration):
- Keeps the existing direct-write buttons for direct EOA admins
- When useSafeAdmin finds a Safe in the admins list, surfaces a second
  "Propose via Safe" button per fee field (fixed + percentage)
- Click -> uses ops.v2Admin.setDepositFixedFee / setDepositPercentageFee
  to build the AdminOp, then useSafePropose handles the Safe path
- Success state shows the safeTxHash + a link to Den UI for co-signing

Confirm + execute remain in Den UI (per discussion: don't rebuild what
Den does well — Discord/Telegram bots, Tenderly simulations, owner
notifications). The webapp's role is to be the entry point for admin
intents.

Other 5+ admin panels (Withdraw, Admins, UpgradeAuthority, Versions,
ClaimLimits, FundPool, Reclaim) follow the same pattern — refactor
landing in a follow-up commit once this PoC is validated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Surfaces the Safe transaction queue from Den's STS directly inside
the proxy detail page, so an owner doesn't have to context-switch to
discover what's awaiting their signature.

src/hooks/usePendingSafeTxs.ts:
- usePendingSafeTxs(safe): polls Den STS via apiKit.getPendingTxs
- Returns { txs, isLoading, error, refetch }
- Refresh on demand (button) — no aggressive polling, owners typically
  refresh after coming back from Den UI

src/components/PendingSafeTxsPanel.tsx:
- Lists each pending tx: nonce + target + confirmations count
- Per-row "Sign in Den" deep link (transactions/tx?safe=int:...&id=
  multisig_<safe>_<safeTxHash>) — opens directly the right Safe
  detail page in Den
- Top "Open in Den" link to the queue overview
- Empty state and error state both handled cleanly
- Read-only by design — co-sign / execute happen in Den (per the
  delegation strategy decided earlier, we don't rebuild Safe's
  signing UX)

src/components/AdminsTab.tsx:
- Renders PendingSafeTxsPanel when useSafeAdmin detects a Safe in
  the fee admins list — otherwise the panel doesn't appear (no Safe,
  no queue to display)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
useSafePropose / usePendingSafeTxs / SetFeesPanel import from
@intuition-fee-proxy/safe-tx but the webapp's package.json didn't
declare it. Vite couldn't resolve the import at dev start, blocking
the page with a 500.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Admins section used <Field> which renders a <label>. Putting
<button>s inside a <label> leads to ambiguous interactive behavior
on hover/focus — the browser routes interaction events between
label children in ways that depend on the form control they're
associated with.

Replaces <Field> with an equivalent <div>-based block keeping the
exact visual layout (label-style heading, hint, action buttons,
Safe input row, textarea, callout). No <label>, no propagation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an "intuitionFork" hardhat network entry pointing at
http://127.0.0.1:8545 with chainId 1155 — matches what Anvil
exposes when forking Intuition mainnet, so contracts deployed
through hardhat land on the fork (which inherits all mainnet state,
including the Safe at 0xf10D...).

Wires:
- packages/contracts/hardhat.config.ts: new network entry, uses the
  well-known LOCAL_DEV_KEY (no PRIVATE_KEY required)
- packages/contracts/package.json: deploy:fork script alias
- package.json (root): contracts:deploy:fork workspace alias

Use case: developer wants to test the Safe-aware webapp paths
(propose via Safe, PendingSafeTxsPanel) end-to-end. Workflow:
  anvil --fork-url https://rpc.intuition.systems --chain-id 1155
  bun contracts:deploy:fork
  # take the printed Factory address, set VITE_FACTORY_ADDRESS in
  # packages/webapp/.env.local, restart bun webapp:dev
  # then deploy a proxy from the webapp with the Safe as admin

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous gate restricted the dev override to testnet. With Anvil
fork now used to test Safe-aware webapp flows on chainId 1155 (the
fork has the real Safe state), the developer needs to point the
webapp at a freshly-deployed local Factory on what wagmi resolves as
"mainnet".

The override only applies when the env var is explicitly set in
packages/webapp/.env.local, which is gitignored and absent in the
prod build — production behavior is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Side effects of declaring @intuition-fee-proxy/safe-tx as a webapp
workspace dep (commit fa80074) and as a bin in safe-tx/package.json
(commit 2a1d483). Bun installs the workspace symlink and chmods the
bin file to 0755 — committing the artifacts so the working tree is
clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The textarea + dual-button pattern made deletion clumsy (find the
right line, select, delete). Replaces with a chip-list UX:

- Each added admin renders as a rounded pill with truncated address
  + a × button that removes it
- Single input below: paste/type an address, press Enter or click Add
- Inline validation: invalid format -> "Invalid address.", duplicate
  -> "Already added."
- "+ my wallet" quick-action only shown when the connected EOA
  isn't already in the list (auto-hides after first add)

Internal state changes from `adminsRaw: string` to
`admins: Address[]` — `deploy()` already received an Address[], no
shape change at the call site.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Wieedze Wieedze merged commit 5e0772a into main Apr 28, 2026
1 check passed
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