Skip to content

Conversation

@MohammadNassar1
Copy link
Collaborator

@MohammadNassar1 MohammadNassar1 commented Nov 17, 2025

Note

Introduce timestamped forced-approval flow with new storage and APIs, and refactor approval registration to a helper.

  • Request approvals component:
    • Storage: Add forced_action_requests map storing (Timestamp, RequestStatus) per request_hash.
    • API:
      • New register_forced_approval(...) to register forced actions with Time::now() and PENDING status.
      • New consume_forced_approved_request(...) to mark forced actions PROCESSED and return (timestamp, request_hash).
      • Extract store_approval(...); make register_approval(...) delegate to it and return HashType.
    • Private helpers: Add _get_forced_action_request_status(...).
    • Validation/flows: Preserve owner/signature checks; ensure NOT_REGISTERED → PENDING → PROCESSED transitions in respective maps.

Written by Cursor Bugbot for commit a355d5d. This will update automatically on new commits. Configure here.


This change is Reviewable

@codecov
Copy link

codecov bot commented Nov 17, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@e195542). Learn more about missing BASE report.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #137   +/-   ##
=======================================
  Coverage        ?   89.36%           
=======================================
  Files           ?       43           
  Lines           ?     2464           
  Branches        ?        0           
=======================================
  Hits            ?     2202           
  Misses          ?      262           
  Partials        ?        0           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@MohammadNassar1 MohammadNassar1 force-pushed the mohammad/request-approval/support-forced-requests branch from dc9069e to 45de586 Compare November 17, 2025 15:03
@MohammadNassar1 MohammadNassar1 force-pushed the mohammad/request-approval/support-forced-requests branch from 45de586 to 5bc5900 Compare November 18, 2025 10:36
Copy link
Collaborator

@remollemo remollemo left a comment

Choose a reason for hiding this comment

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

@remollemo reviewed all commit messages.
Reviewable status: 0 of 1 files reviewed, 4 unresolved discussions (waiting on @ishay-starkware)


packages/utils/src/components/request_approvals/request_approvals.cairo line 19 at r3 (raw file):

    pub struct Storage {
        approved_requests: Map<HashType, RequestStatus>,
        forced_approved_requests: Map<HashType, (Timestamp, RequestStatus)>,

Suggestion:

forced_action_requests

packages/utils/src/components/request_approvals/request_approvals.cairo line 47 at r3 (raw file):

        /// The signature is verified with the hash of the request.
        /// The request is stored with a status of PENDING.
        fn register_approval<T, +OffchainMessageHash<T>, +Drop<T>>(
  1. consolidate fn register_approval
    with the inner part that is now called fn unsefe_register_approval

  2. rename fn unsafe_register_approval to something more sane. maybe
    fn _store_approval

Code quote:

  fn register_approval<T, +OffchainMessageHash<T>, +Drop<T>>(

packages/utils/src/components/request_approvals/request_approvals.cairo line 72 at r3 (raw file):

        /// is validated with signature and caller checks via `register_forced_approval`, while
        /// this function is used to register the original request hash in the approvals map.
        fn unsafe_register_approval<T, +OffchainMessageHash<T>, +Drop<T>>(

this name is bad. it's not unsafe at all. it's simply an unvalidated internal flow.
see comment above

update commment accordingly

Code quote:

fn unsafe_register_approva

packages/utils/src/components/request_approvals/request_approvals.cairo line 87 at r3 (raw file):

        /// The approval is signed with the public key.
        /// The signature is verified with the hash of the request.
        /// The request is stored with a status of PENDING, and the current time.

how is it expeted to be used? (it's an intenal one, it's called from within fn forced_withdraw for example?

Suggestion:

        /// Registers a forced action.
        /// If the owner_account is non-zero, the caller must be the owner_account.
        /// The request is signed with the public key.
        /// The request is stored with a status of PENDING, and the current time.

Copy link
Collaborator

@remollemo remollemo left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 1 files reviewed, 5 unresolved discussions (waiting on @ishay-starkware and @MohammadNassar1)


a discussion (no related file):
it's very unclear how you bind the action_request with the forced_action_request.

@MohammadNassar1 MohammadNassar1 force-pushed the mohammad/request-approval/support-forced-requests branch from 5bc5900 to 71e6a79 Compare November 19, 2025 08:47
Copy link
Collaborator Author

@MohammadNassar1 MohammadNassar1 left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 1 files reviewed, 4 unresolved discussions (waiting on @ishay-starkware and @remollemo)


a discussion (no related file):

Previously, remollemo wrote…

it's very unclear how you bind the action_request with the forced_action_request.

The forced request stores a hash computed from the same fields as a regular request. in addition, both have similar logic (calculating hash then validating sigs, and storing).

So the link between a regular request and its forced counterpart is that they share the same request fields. The forced map just adds timestamp + its own status while the regular map tracks the generic approval status.

I added comments, so the link between the request would be clearer.


packages/utils/src/components/request_approvals/request_approvals.cairo line 47 at r3 (raw file):

Previously, remollemo wrote…
  1. consolidate fn register_approval
    with the inner part that is now called fn unsefe_register_approval

  2. rename fn unsafe_register_approval to something more sane. maybe
    fn _store_approval

Done.


packages/utils/src/components/request_approvals/request_approvals.cairo line 87 at r3 (raw file):

Previously, remollemo wrote…

how is it expeted to be used? (it's an intenal one, it's called from within fn forced_withdraw for example?

Exactly, it's called in forced_withdraw_request and force_trade_request.


packages/utils/src/components/request_approvals/request_approvals.cairo line 19 at r3 (raw file):

    pub struct Storage {
        approved_requests: Map<HashType, RequestStatus>,
        forced_approved_requests: Map<HashType, (Timestamp, RequestStatus)>,

Done.

Copy link
Collaborator

@remollemo remollemo left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 1 files reviewed, 1 unresolved discussion (waiting on @ishay-starkware)


packages/utils/src/components/request_approvals/request_approvals.cairo line 73 at r4 (raw file):

        ) -> HashType {
            self._store_approval(:public_key, :args)
        }

this makes no sense.
delete fn store_approval() and call fn _store_approval() directly.

Code quote:

        fn store_approval<T, +OffchainMessageHash<T>, +Drop<T>>(
            ref self: ComponentState<TContractState>, public_key: PublicKey, args: T,
        ) -> HashType {
            self._store_approval(:public_key, :args)
        }

Copy link
Collaborator Author

@MohammadNassar1 MohammadNassar1 left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 1 files reviewed, 1 unresolved discussion (waiting on @ishay-starkware and @remollemo)


packages/utils/src/components/request_approvals/request_approvals.cairo line 73 at r4 (raw file):

Previously, remollemo wrote…

this makes no sense.
delete fn store_approval() and call fn _store_approval() directly.

_store_approval is a private function, so it cannot be called from withdraw contract.
and since I call it in register_approval, I think we should have a private method.

@MohammadNassar1 MohammadNassar1 force-pushed the mohammad/request-approval/support-forced-requests branch from 71e6a79 to a355d5d Compare November 19, 2025 14:42
@MohammadNassar1 MohammadNassar1 enabled auto-merge (squash) November 19, 2025 14:43
Copy link
Collaborator Author

@MohammadNassar1 MohammadNassar1 left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 1 files reviewed, 1 unresolved discussion (waiting on @ishay-starkware and @remollemo)


packages/utils/src/components/request_approvals/request_approvals.cairo line 73 at r4 (raw file):

Previously, MohammadNassar1 (mohammad-starkware) wrote…

_store_approval is a private function, so it cannot be called from withdraw contract.
and since I call it in register_approval, I think we should have a private method.

Removed _store_approval.

@MohammadNassar1 MohammadNassar1 merged commit 12bc7a6 into main Nov 19, 2025
3 of 4 checks passed
@MohammadNassar1 MohammadNassar1 deleted the mohammad/request-approval/support-forced-requests branch November 19, 2025 14:44
self.approved_requests.write(key: request_hash, value: RequestStatus::PENDING);
self
.forced_action_requests
.write(key: request_hash, value: (Time::now(), RequestStatus::PENDING));
Copy link

Choose a reason for hiding this comment

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

Bug: Forced approval misses required dual-map registration

The register_forced_approval function only writes to forced_action_requests but doesn't register the request in approved_requests. According to the documentation for store_approval, forced requests should be tracked in both maps to maintain consistency. This breaks the dual-map invariant and could cause issues when the request is later processed, as _get_request_status() will return NOT_REGISTERED instead of PENDING.

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants