diff --git a/src/lib.cairo b/src/lib.cairo index 21e58e6..9d658dd 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -5,6 +5,7 @@ mod governance_token; mod governor; mod interfaces; mod timelock; +mod utils; #[cfg(test)] mod airdrop_test; diff --git a/src/timelock.cairo b/src/timelock.cairo index ca6eacb..c120ccb 100644 --- a/src/timelock.cairo +++ b/src/timelock.cairo @@ -3,62 +3,8 @@ use core::traits::TryInto; use core::result::ResultTrait; use starknet::{ContractAddress}; use starknet::account::{Call}; -use core::integer::{u128_to_felt252, u64_try_from_felt252, u128_safe_divmod}; use starknet::storage_access::{StorePacking}; - -#[starknet::interface] -trait ITimelock { - // Queue a list of calls to be executed after the delay. Only the owner may call this. - fn queue(ref self: TStorage, calls: Span) -> felt252; - - // Cancel a queued proposal before it is executed. Only the owner may call this. - fn cancel(ref self: TStorage, id: felt252); - - // Execute a list of calls that have previously been queued. Anyone may call this. - fn execute(ref self: TStorage, calls: Span) -> Array>; - - // Return the execution window, i.e. the start and end timestamp in which the call can be executed - fn get_execution_window(self: @TStorage, id: felt252) -> ExecutionWindow; - - // Get the current owner - fn get_owner(self: @TStorage) -> ContractAddress; - - // Returns the delay and the window for call execution - fn get_configuration(self: @TStorage) -> TimelockConfig; - - // Transfer ownership, i.e. the address that can queue and cancel calls. This must be self-called via #queue. - fn transfer(ref self: TStorage, to: ContractAddress); - // Configure the delay and the window for call execution. This must be self-called via #queue. - fn configure(ref self: TStorage, config: TimelockConfig); -} - - -const TWO_POW_64: u128 = 0x10000000000000000; - -#[inline(always)] -impl ThreeU64TupleStorePacking of StorePacking<(u64, u64, u64), felt252> { - fn pack(value: (u64, u64, u64)) -> felt252 { - let (a, b, c) = value; - u256 { low: TwoU64TupleStorePacking::pack((a, b)), high: c.into() }.try_into().unwrap() - } - fn unpack(value: felt252) -> (u64, u64, u64) { - let u256_value: u256 = value.into(); - let (a, b) = TwoU64TupleStorePacking::unpack(u256_value.low); - (a, b, (u256_value.high).try_into().unwrap()) - } -} - -#[inline(always)] -impl TwoU64TupleStorePacking of StorePacking<(u64, u64), u128> { - fn pack(value: (u64, u64)) -> u128 { - let (a, b) = value; - a.into() + b.into() * TWO_POW_64 - } - fn unpack(value: u128) -> (u64, u64) { - let (q, r) = u128_safe_divmod(value, TWO_POW_64.try_into().unwrap()); - (r.try_into().unwrap(), q.try_into().unwrap()) - } -} +use governance::utils::timestamps::{ThreeU64TupleStorePacking, TwoU64TupleStorePacking}; #[derive(Copy, Drop, Serde)] struct ExecutionState { @@ -67,11 +13,12 @@ struct ExecutionState { canceled: u64 } -#[inline(always)] impl ExecutionStateStorePacking of StorePacking { + #[inline(always)] fn pack(value: ExecutionState) -> felt252 { ThreeU64TupleStorePacking::pack((value.started, value.executed, value.canceled)) } + #[inline(always)] fn unpack(value: felt252) -> ExecutionState { let (started, executed, canceled) = ThreeU64TupleStorePacking::unpack(value); ExecutionState { started, executed, canceled } @@ -84,41 +31,55 @@ struct TimelockConfig { window: u64, } -#[inline(always)] impl TimelockConfigStorePacking of StorePacking { + #[inline(always)] fn pack(value: TimelockConfig) -> u128 { TwoU64TupleStorePacking::pack((value.delay, value.window)) } + #[inline(always)] fn unpack(value: u128) -> TimelockConfig { let (delay, window) = TwoU64TupleStorePacking::unpack(value); TimelockConfig { delay, window } } } +#[starknet::interface] +trait ITimelock { + // Queue a list of calls to be executed after the delay. Only the owner may call this. + fn queue(ref self: TStorage, calls: Span) -> felt252; + + // Cancel a queued proposal before it is executed. Only the owner may call this. + fn cancel(ref self: TStorage, id: felt252); + + // Execute a list of calls that have previously been queued. Anyone may call this. + fn execute(ref self: TStorage, calls: Span) -> Array>; + + // Return the execution window, i.e. the start and end timestamp in which the call can be executed + fn get_execution_window(self: @TStorage, id: felt252) -> ExecutionWindow; + + // Get the current owner + fn get_owner(self: @TStorage) -> ContractAddress; + + // Returns the delay and the window for call execution + fn get_configuration(self: @TStorage) -> TimelockConfig; + + // Transfer ownership, i.e. the address that can queue and cancel calls. This must be self-called via #queue. + fn transfer(ref self: TStorage, to: ContractAddress); + // Configure the delay and the window for call execution. This must be self-called via #queue. + fn configure(ref self: TStorage, config: TimelockConfig); +} + #[derive(Copy, Drop, Serde)] struct ExecutionWindow { earliest: u64, latest: u64 } -#[inline(always)] -impl ExecutionWindowStorePacking of StorePacking { - fn pack(value: ExecutionWindow) -> u128 { - TwoU64TupleStorePacking::pack((value.earliest, value.latest)) - } - fn unpack(value: u128) -> ExecutionWindow { - let (earliest, latest) = TwoU64TupleStorePacking::unpack(value); - ExecutionWindow { earliest, latest, } - } -} - - #[starknet::contract] mod Timelock { use super::{ ITimelock, ContractAddress, Call, TimelockConfig, ExecutionState, - TimelockConfigStorePacking, ExecutionStateStorePacking, ExecutionWindow, - ExecutionWindowStorePacking, TwoU64TupleStorePacking, ThreeU64TupleStorePacking + TimelockConfigStorePacking, ExecutionStateStorePacking, ExecutionWindow }; use governance::call_trait::{CallTrait, HashCall}; use hash::{LegacyHash}; @@ -285,7 +246,7 @@ mod Timelock { let latest = earliest + configuration.window; - ExecutionWindow { earliest: earliest, latest: latest } + ExecutionWindow { earliest, latest } } fn get_owner(self: @ContractState) -> ContractAddress { diff --git a/src/utils.cairo b/src/utils.cairo new file mode 100644 index 0000000..2a3544f --- /dev/null +++ b/src/utils.cairo @@ -0,0 +1 @@ +mod timestamps; \ No newline at end of file diff --git a/src/utils/timestamps.cairo b/src/utils/timestamps.cairo new file mode 100644 index 0000000..47c2d3a --- /dev/null +++ b/src/utils/timestamps.cairo @@ -0,0 +1,31 @@ +use core::integer::{u128_to_felt252, u64_try_from_felt252, u128_safe_divmod}; +use starknet::storage_access::{StorePacking}; + +const TWO_POW_64: u128 = 0x10000000000000000; + +impl ThreeU64TupleStorePacking of StorePacking<(u64, u64, u64), felt252> { + #[inline(always)] + fn pack(value: (u64, u64, u64)) -> felt252 { + let (a, b, c) = value; + u256 { low: TwoU64TupleStorePacking::pack((a, b)), high: c.into() }.try_into().unwrap() + } + #[inline(always)] + fn unpack(value: felt252) -> (u64, u64, u64) { + let u256_value: u256 = value.into(); + let (a, b) = TwoU64TupleStorePacking::unpack(u256_value.low); + (a, b, (u256_value.high).try_into().unwrap()) + } +} + +impl TwoU64TupleStorePacking of StorePacking<(u64, u64), u128> { + #[inline(always)] + fn pack(value: (u64, u64)) -> u128 { + let (a, b) = value; + a.into() + b.into() * TWO_POW_64 + } + #[inline(always)] + fn unpack(value: u128) -> (u64, u64) { + let (q, r) = u128_safe_divmod(value, TWO_POW_64.try_into().unwrap()); + (r.try_into().unwrap(), q.try_into().unwrap()) + } +} \ No newline at end of file