Skip to content

Commit a355d5d

Browse files
feat: add support for forced request approvals
1 parent e195542 commit a355d5d

File tree

1 file changed

+77
-1
lines changed

1 file changed

+77
-1
lines changed

packages/utils/src/components/request_approvals/request_approvals.cairo

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ pub(crate) mod RequestApprovalsComponent {
1111
use starkware_utils::signature::stark::{
1212
HashType, PublicKey, Signature, validate_stark_signature,
1313
};
14+
use starkware_utils::time::time::{Time, Timestamp};
1415

1516
#[storage]
1617
pub struct Storage {
1718
approved_requests: Map<HashType, RequestStatus>,
19+
/// Stores forced action requests as (timestamp, status) by request_hash.
20+
forced_action_requests: Map<HashType, (Timestamp, RequestStatus)>,
1821
}
1922

2023
#[event]
@@ -48,17 +51,60 @@ pub(crate) mod RequestApprovalsComponent {
4851
public_key: PublicKey,
4952
signature: Signature,
5053
args: T,
54+
) -> HashType {
55+
let request_hash = self.store_approval(:public_key, :args);
56+
if let Option::Some(owner_account) = owner_account {
57+
assert(owner_account == get_caller_address(), errors::CALLER_IS_NOT_OWNER_ACCOUNT);
58+
}
59+
validate_stark_signature(:public_key, msg_hash: request_hash, :signature);
60+
request_hash
61+
}
62+
/// Registers an approval for a request without signature or caller validation.
63+
///
64+
/// This is intended for use with forced requests, where we track the same logical
65+
/// request in two maps:
66+
/// - `approved_requests` (generic approval status)
67+
/// - `forced_action_requests` (forced metadata: timestamp + status)
68+
/// This function is used to register the original request hash in the approvals map.
69+
70+
fn store_approval<T, +OffchainMessageHash<T>, +Drop<T>>(
71+
ref self: ComponentState<TContractState>, public_key: PublicKey, args: T,
5172
) -> HashType {
5273
let request_hash = args.get_message_hash(:public_key);
5374
assert(
5475
self._get_request_status(:request_hash) == RequestStatus::NOT_REGISTERED,
5576
errors::REQUEST_ALREADY_REGISTERED,
5677
);
78+
self.approved_requests.write(key: request_hash, value: RequestStatus::PENDING);
79+
request_hash
80+
}
81+
/// Registers a forced approval action.
82+
/// If the owner_account is non-zero, the caller must be the owner_account.
83+
/// The request is signed with the public key.
84+
/// The request is stored with a status of PENDING, and the current time.
85+
fn register_forced_approval<T, +OffchainMessageHash<T>, +Drop<T>>(
86+
ref self: ComponentState<TContractState>,
87+
owner_account: Option<ContractAddress>,
88+
public_key: PublicKey,
89+
signature: Signature,
90+
args: T,
91+
) -> HashType {
92+
let request_hash = args.get_message_hash(:public_key);
93+
94+
assert(
95+
self
96+
._get_forced_action_request_status(
97+
request_hash,
98+
) == RequestStatus::NOT_REGISTERED,
99+
errors::REQUEST_ALREADY_REGISTERED,
100+
);
57101
if let Option::Some(owner_account) = owner_account {
58102
assert(owner_account == get_caller_address(), errors::CALLER_IS_NOT_OWNER_ACCOUNT);
59103
}
60104
validate_stark_signature(:public_key, msg_hash: request_hash, :signature);
61-
self.approved_requests.write(key: request_hash, value: RequestStatus::PENDING);
105+
self
106+
.forced_action_requests
107+
.write(key: request_hash, value: (Time::now(), RequestStatus::PENDING));
62108
request_hash
63109
}
64110

@@ -80,6 +126,30 @@ pub(crate) mod RequestApprovalsComponent {
80126
self.approved_requests.write(request_hash, RequestStatus::PROCESSED);
81127
request_hash
82128
}
129+
130+
/// Consumes a forced-approved request and updates its status to DONE.
131+
///
132+
/// Validations:
133+
/// - The request must be in a PENDING state.
134+
///
135+
/// Returns:
136+
/// - The timestamp of when the forced request was approved.
137+
/// - The hash of the forced request.
138+
fn consume_forced_approved_request<T, +OffchainMessageHash<T>, +Drop<T>>(
139+
ref self: ComponentState<TContractState>, args: T, public_key: PublicKey,
140+
) -> (Timestamp, HashType) {
141+
let request_hash = args.get_message_hash(:public_key);
142+
let (request_time, request_status) = self.forced_action_requests.read(request_hash);
143+
match request_status {
144+
RequestStatus::NOT_REGISTERED => panic_with_felt252(errors::REQUEST_NOT_REGISTERED),
145+
RequestStatus::PROCESSED => panic_with_felt252(errors::REQUEST_ALREADY_PROCESSED),
146+
RequestStatus::PENDING => {},
147+
}
148+
self
149+
.forced_action_requests
150+
.write(request_hash, (request_time, RequestStatus::PROCESSED));
151+
(request_time, request_hash)
152+
}
83153
}
84154

85155
#[generate_trait]
@@ -91,5 +161,11 @@ pub(crate) mod RequestApprovalsComponent {
91161
) -> RequestStatus {
92162
self.approved_requests.read(request_hash)
93163
}
164+
fn _get_forced_action_request_status(
165+
self: @ComponentState<TContractState>, request_hash: HashType,
166+
) -> RequestStatus {
167+
let (_, status) = self.forced_action_requests.read(request_hash);
168+
status
169+
}
94170
}
95171
}

0 commit comments

Comments
 (0)