Skip to content

Commit 5bc5900

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

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed

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

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ 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+
forced_approved_requests: Map<HashType, (Timestamp, RequestStatus)>,
1820
}
1921

2022
#[event]
@@ -61,6 +63,50 @@ pub(crate) mod RequestApprovalsComponent {
6163
self.approved_requests.write(key: request_hash, value: RequestStatus::PENDING);
6264
request_hash
6365
}
66+
/// Registers an approval for a request without signature or caller validation.
67+
///
68+
/// This is intended for use with forced requests, which generate two distinct hashes:
69+
/// one for the original request and one for the forced request. The forced request hash
70+
/// is validated with signature and caller checks via `register_forced_approval`, while
71+
/// this function is used to register the original request hash in the approvals map.
72+
fn unsafe_register_approval<T, +OffchainMessageHash<T>, +Drop<T>>(
73+
ref self: ComponentState<TContractState>, public_key: PublicKey, args: T,
74+
) -> HashType {
75+
let request_hash = args.get_message_hash(:public_key);
76+
assert(
77+
self._get_request_status(:request_hash) == RequestStatus::NOT_REGISTERED,
78+
errors::REQUEST_ALREADY_REGISTERED,
79+
);
80+
self.approved_requests.write(key: request_hash, value: RequestStatus::PENDING);
81+
request_hash
82+
}
83+
/// Registers a forced approval for a request.
84+
/// If the owner_account is non-zero, the caller must be the owner_account.
85+
/// The approval is signed with the public key.
86+
/// The signature is verified with the hash of the request.
87+
/// The request is stored with a status of PENDING, and the current time.
88+
fn register_forced_approval<T, +OffchainMessageHash<T>, +Drop<T>>(
89+
ref self: ComponentState<TContractState>,
90+
owner_account: Option<ContractAddress>,
91+
public_key: PublicKey,
92+
signature: Signature,
93+
args: T,
94+
) -> HashType {
95+
let request_hash = args.get_message_hash(:public_key);
96+
97+
assert(
98+
self._get_forced_request_status(request_hash) == RequestStatus::NOT_REGISTERED,
99+
errors::REQUEST_ALREADY_REGISTERED,
100+
);
101+
if let Option::Some(owner_account) = owner_account {
102+
assert(owner_account == get_caller_address(), errors::CALLER_IS_NOT_OWNER_ACCOUNT);
103+
}
104+
validate_stark_signature(:public_key, msg_hash: request_hash, :signature);
105+
self
106+
.forced_approved_requests
107+
.write(key: request_hash, value: (Time::now(), RequestStatus::PENDING));
108+
request_hash
109+
}
64110

65111
/// Consumes an approved request.
66112
/// Marks the request with status DONE.
@@ -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_approved_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_approved_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_request_status(
165+
self: @ComponentState<TContractState>, request_hash: HashType,
166+
) -> RequestStatus {
167+
let (_, status) = self.forced_approved_requests.read(request_hash);
168+
status
169+
}
94170
}
95171
}

0 commit comments

Comments
 (0)