Files
FbartoliandGitHub e3eb5e13c1 feat(web): enhance proposer functionality with nested safe support (#7028)
* feat(web): enhance proposer functionality with nested safe support

- Updated ProposersList component to use CheckWallet for wallet validation.
- Refactored UpsertProposer to support signing for nested safes, including new utility functions for EIP-1271 signature encoding.
- Added TypeScript dependencies for improved compatibility with Next.js and React.

This update improves the handling of nested safes and enhances the overall proposer management experience.

* feat(web): enhance proposer management with multi-sig support
- Added support for multi-signature delegation in UpsertProposer and DeleteProposerDialog components.
- Integrated PendingDelegationsList to display pending actions for nested safe owners.
- Updated utility functions for EIP-1271 signature encoding to handle multi-owner scenarios.
- Enhanced ProposersList to conditionally show pending delegations based on nested safe ownership.
These changes improve the user experience for managing proposers in nested safe environments.

* fix(workflows): update preview URL format in tx-builder deploy workflow

* feat(web): enhance proposer management and delegation handling
- Updated ProposersList to remove the New chip and conditionally display pending delegations.
- Modified DeleteProposerDialog to allow nested safe owners to delete proposers.
- Enhanced PendingDelegation component to include expiration date and shareable link functionality.
- Implemented accordion layout in PendingDelegationsList for better organization of pending confirmations.
- Updated utility functions to compute TOTP expiration dates.
These changes improve the user experience for managing proposers and pending delegations in nested safe environments.

* fix(web): refine wallet and delegation handling logic
- Updated CheckWallet component to include a check for nested safe owners.
- Changed background color in PendingDelegationCard and PendingDelegationsList for better visual consistency.
- Enhanced TOTP extraction logic in usePendingDelegations hook to ensure valid number conversion.
These changes improve the accuracy of wallet checks and enhance the user interface for pending delegations.

* feat(web): improve edit proposer logic and UI consistency
- Enhanced EditProposerDialog to allow nested safe owners to edit proposers.
- Updated PendingDelegationsList to use a consistent background color for better visual clarity.
These changes enhance the user experience for managing proposer edits and improve the overall interface.

* feat(web): enhance pending delegation handling and UI consistency
- Updated PendingDelegation component to improve shareable link functionality and streamline UI elements.
- Refined PendingDelegationsList to ensure consistent background color across components.
- Enhanced usePendingDelegations hook to implement polling for new signatures and optimize delegation filtering logic.
These changes improve the user experience for managing pending delegations and ensure a more cohesive interface.

* fix(tests): mock useChains hook to return default configs in ProposersList tests

* feat(web): refactor usePendingDelegations hook for improved delegation parsing and filtering
- Introduced parseMessageToDelegation function to streamline the extraction of delegation data from messages.
- Implemented keepLatestPerDelegate and filterActedUponDelegations functions to enhance delegation management logic.
- Optimized the usePendingDelegations hook to improve performance and clarity in handling pending delegations.
These changes enhance the overall efficiency and maintainability of the delegation handling process.

* feat(tests): add comprehensive tests for delegation hooks and services
- Introduced unit tests for useParentSafeThreshold, usePendingDelegations, and useSubmitDelegation hooks to ensure correct functionality and edge case handling.
- Added tests for delegation message creation and confirmation logic to validate proper integration with the messaging system.
- Enhanced test coverage for delegation-related services, improving reliability and maintainability of the codebase.
These additions strengthen the testing framework and ensure robust delegation management in the application.

* refactor(web): update background color in PendingDelegationsList for UI consistency
- Changed background color from a hardcoded value to a CSS variable for improved theming and maintainability.
- Ensured consistent background color across all accordion components within the PendingDelegationsList.
These updates enhance the visual coherence of the user interface for pending delegations.

* fix(md): revert claude claude MD changes

* fix(workflow): update preview URL in tx-builder deployment configuration
- Modified the preview URL in the tx-builder-deploy.yml workflow to include the '/tx-builder/' path for accurate deployment previews.
This change ensures that the deployment preview links direct users to the correct location.

* fix(web): improve delegation handling and error logging
- Updated the PendingDelegation component to include a countdown timer for expiration and enhanced error handling with logging for better debugging.
- Refactored the useSubmitDelegation hook to validate TOTP expiration and improved error management by utilizing the asError utility.
- Adjusted the encodeEIP1271Signature function to return a promise, ensuring consistent handling of asynchronous operations.
These changes enhance the reliability and user experience of delegation management in the application.

* chore(web): remove unused dependency from package.json
- Deleted the "@next/swc-linux-arm64-gnu" dependency from package.json as it is no longer needed. This cleanup helps maintain a leaner and more efficient project structure.

* chore(deps): remove deprecated "@next/swc-linux-arm64-gnu" entry from yarn.lock
- Deleted the "@next/swc-linux-arm64-gnu" entry from yarn.lock as it is no longer required, following the recent cleanup of the package.json. This helps maintain an up-to-date and efficient dependency tree.

* feat(web): enhance delegation polling and error handling in UI components
- Updated constants to include a backoff multiplier for delegation polling.
- Refactored PendingDelegation component to utilize the sameAddress utility for address comparison.
- Introduced DelegationErrorBoundary in PendingDelegationsList to handle loading errors gracefully.
- Improved usePendingDelegations hook with custom polling logic and stable key for dependency comparison.
These changes enhance the reliability and user experience of delegation management in the application.

* refactor(web): enhance delegation components and hooks for improved type safety and functionality
- Updated DelegationErrorBoundary and PendingDelegation components to use function declarations for better readability and type inference.
- Refactored useDelegationPolling and usePendingDelegations hooks to streamline polling logic and improve type definitions.
- Introduced utility functions for delegation origin parsing and filtering, enhancing code clarity and maintainability.
These changes improve the overall structure and reliability of delegation management in the application.

* feat(web): enhance delegation management with edit functionality and improved error handling
- Updated delegation-related types and constants to support 'edit' actions for better delegation management.
- Refactored PendingDelegation and UpsertProposer components to handle edit actions and improve user feedback.
- Streamlined error handling in delegation submission and deletion processes using the asError utility.
- Removed the deprecated useDelegationPolling hook, simplifying the polling logic in usePendingDelegations.
These changes improve the functionality and reliability of delegation operations within the application.

* fix(tests): update delegationMessages tests to include additional properties in origin object
- Enhanced the test cases for delegationMessages by adding 'delegate', 'nestedSafe', and 'label' properties to the origin object for both 'add' and 'remove' actions. This improves the accuracy and coverage of the tests related to delegation management.
2026-02-18 15:10:37 +01:00

9.5 KiB

Tasks: Nested Safe Proposer Management

Input: Design documents from /specs/001-nested-safe-proposer/ Prerequisites: plan.md (required), spec.md (required), research.md, data-model.md, contracts/

Tests: Included — the plan explicitly defines unit tests for each phase.

Organization: Tasks are grouped by user story to enable independent implementation and testing of each story.

Format: [ID] [P?] [Story] Description

  • [P]: Can run in parallel (different files, no dependencies)
  • [Story]: Which user story this task belongs to (e.g., US1, US2, US3)
  • Include exact file paths in descriptions

Phase 1: Foundational (EIP-1271 Utility)

Purpose: Create the shared EIP-1271 signature encoding utility that the signing flow depends on.

Why foundational: The encodeEIP1271Signature function is needed by User Story 1 (full flow) and User Story 3 (signing). It operates on a separate file from the permission gate change, so it can be implemented first without conflicts.

  • T001 Implement encodeEIP1271Signature(parentSafeAddress: string, ownerSignature: string): string utility function in apps/web/src/features/proposers/utils/utils.ts. The function must encode the signature in EIP-1271 contract signature format: r=parentSafeAddress (32 bytes, left-padded), s=0x41 (65, offset to dynamic data), v=0x00, followed by ABI-encoded bytes of the owner signature. Reference format from Safe Transaction Service test _test_add_delegate_using_1271_signature.
  • T002 Add unit test for encodeEIP1271Signature in apps/web/src/features/proposers/utils/utils.test.ts. Test cases: (1) correct byte layout for a known parent Safe address and signature, (2) output is a valid hex string, (3) r-value contains the parent Safe address left-padded to 32 bytes, (4) v-value is 0x00, (5) s-value is 65 (0x41). Use faker for test addresses.

Checkpoint: EIP-1271 encoding utility is available and tested.


Phase 2: User Story 1 + User Story 2 - Permission Gate Fix (Priority: P1) 🎯 MVP

Goal: Enable the "Add proposer" button for nested Safe owners while keeping it disabled for non-owners and proposer-only users.

Independent Test: Connect a wallet that owns a parent Safe (which is an owner of the nested Safe), navigate to Settings > Setup, verify "Add proposer" button is enabled. Connect an unrelated wallet, verify button remains disabled with tooltip.

Implementation

  • T003 [US1] Replace OnlyOwner wrapper with CheckWallet component around the "Add proposer" button in apps/web/src/components/settings/ProposersList/index.tsx. Use props allowProposer={false} to prevent proposers from managing other proposers while allowing nested Safe owners. Remove the OnlyOwner import if no longer used in the file. Add CheckWallet import from @/components/common/CheckWallet.
  • T004 [P] [US1] Add unit tests for ProposersList permission behavior in apps/web/src/components/settings/ProposersList/index.test.tsx. Test cases using MSW and React Testing Library: (1) button enabled when useIsNestedSafeOwner returns true and Safe is deployed, (2) button disabled when user is only a proposer (not owner/nested owner), (3) button disabled when user has no relationship to the Safe (shows tooltip "Your connected wallet is not a signer of this Safe Account"), (4) button disabled when Safe is undeployed (shows "activate Safe" tooltip), (5) button enabled for direct Safe owner (no regression). Mock useIsNestedSafeOwner, useIsSafeOwner, useIsWalletProposer hooks.

Checkpoint: Button correctly reflects permissions for nested owners, direct owners, proposers, and non-owners.


Phase 3: User Story 3 - EIP-1271 Signing Flow (Priority: P2)

Goal: When a nested Safe owner submits the proposer form, sign with the connected wallet and wrap in EIP-1271 format with the parent Safe as delegator.

Independent Test: As a nested Safe owner, click "Add proposer", enter a valid address and label, submit — verify the delegate API is called with delegator: parentSafeAddress and signature in EIP-1271 format.

Implementation

  • T005 [US3] Modify apps/web/src/features/proposers/components/UpsertProposer.tsx to detect nested Safe ownership and use parent Safe as delegator. Changes: (1) Import and call useIsNestedSafeOwner() and useNestedSafeOwners() hooks. (2) In the form submission handler, after signing with the connected wallet (existing signProposerTypedData/signProposerData), check if user is a nested Safe owner. (3) If nested Safe owner: get the first parent Safe address from useNestedSafeOwners(), call encodeEIP1271Signature(parentSafeAddress, eoaSignature) to wrap, and set delegator to parentSafeAddress in the API payload. (4) If direct owner: keep existing behavior unchanged (delegator = wallet.address, raw EOA signature).
  • T006 [P] [US3] Add unit tests for UpsertProposer nested Safe flow in apps/web/src/features/proposers/components/UpsertProposer.test.tsx. Test cases: (1) When user is nested Safe owner and submits form, the delegate API mutation is called with delegator set to the parent Safe address (not the wallet address). (2) When user is nested Safe owner, the signature field in the API call is the EIP-1271 wrapped version. (3) When user is a direct owner, the delegate API is called with delegator set to the wallet address and raw EOA signature (no regression). (4) When user has multiple parent Safes, the first one is used as delegator. Use MSW for API mocking, mock the nested Safe ownership hooks.

Checkpoint: End-to-end flow works — nested Safe owner can add a proposer with parent Safe as delegator using EIP-1271 signature.


Phase 4: Polish & Validation

Purpose: Quality gates, type checking, and validation across all stories.

  • T007 Run yarn workspace @safe-global/web type-check and fix any TypeScript errors introduced by the changes in ProposersList, UpsertProposer, and utils.ts.
  • T008 Run yarn workspace @safe-global/web lint and fix any linting issues.
  • T009 Run yarn workspace @safe-global/web test to verify all existing tests pass (no regressions) and all new tests pass.
  • T010 Verify edge case handling: (1) Confirm undeployed Safe still shows disabled button regardless of nested ownership, (2) Confirm address validation (not self, not existing owner) works for nested Safe owner flow, (3) Confirm ETH_SIGN wallet detection still works correctly for nested Safe owners (Trezor/Keystone use signProposerData fallback).

Dependencies & Execution Order

Phase Dependencies

  • Foundational (Phase 1): No dependencies — can start immediately
  • US1+US2 (Phase 2): No dependency on Phase 1 (different file: ProposersList vs utils.ts)
  • US3 (Phase 3): Depends on Phase 1 (needs encodeEIP1271Signature from utils.ts)
  • Polish (Phase 4): Depends on all previous phases

User Story Dependencies

  • User Story 1 (P1): Permission gate change is independent. Full end-to-end flow requires US3 signing flow.
  • User Story 2 (P2): Automatically satisfied by US1's CheckWallet change (non-owners/proposers blocked via allowProposer={false}).
  • User Story 3 (P2): Depends on Phase 1 foundational utility. Independent of US1 permission gate (different file).

Within Each Phase

  • Tasks marked [P] within the same phase can run in parallel
  • T001 (utility) before T005 (UpsertProposer uses utility)
  • T003 (ProposersList) and T001 (utils) are in different files — can run in parallel
  • Tests ([P] marked) can run in parallel with their phase's implementation if in different files

Parallel Opportunities

  • Phase 1 + Phase 2 can run in parallel (different files: utils.ts vs ProposersList)
  • T002 and T004 can run in parallel (different test files)
  • T005 and T003 are independent (different files)
  • T006 and T004 can run in parallel (different test files)

Parallel Example: Phase 1 + Phase 2

# These can run simultaneously (different files):
Task: "T001 - encodeEIP1271Signature in utils.ts"
Task: "T003 - Replace OnlyOwner with CheckWallet in ProposersList"

# Then tests in parallel:
Task: "T002 - Unit test for EIP-1271 encoding in utils.test.ts"
Task: "T004 - Unit tests for ProposersList in index.test.tsx"

Implementation Strategy

MVP First (User Story 1 - Permission Gate)

  1. Complete Phase 1 (foundational utility) + Phase 2 (permission gate) in parallel
  2. STOP and VALIDATE: Verify button is enabled for nested Safe owners, disabled for non-owners
  3. This alone delivers visible value (unblocks the button)

Full Feature Delivery

  1. Complete Phase 1 + Phase 2 (in parallel) → Button works correctly
  2. Complete Phase 3 → Signing flow uses EIP-1271 with parent Safe as delegator
  3. Complete Phase 4 → All quality gates pass
  4. Full feature complete: Nested Safe owners can add proposers end-to-end

Scope Note

Initial implementation targets 1-of-1 parent Safes (single connected wallet is sole owner of parent Safe). Multi-sig parent Safes (threshold > 1) requiring collection of multiple owner signatures are deferred to a future iteration.


Notes

  • [P] tasks = different files, no dependencies
  • [Story] label maps task to specific user story for traceability
  • US2 is implicitly covered by US1's CheckWallet change — no separate implementation needed
  • The encodeEIP1271Signature utility is the only new function; all other changes modify existing files
  • Existing hooks (useIsNestedSafeOwner, useNestedSafeOwners) require no changes
  • Existing signing functions (signProposerTypedData, signProposerData) require no changes — EIP-1271 wrapping happens after signing