use core::marker::PhantomData; use fp_evm::{ExitError, PrecompileFailure}; use frame_support::pallet_prelude::{StorageValue, ValueQuery}; use frame_support::traits::StorageInstance; use pallet_evm::PrecompileHandle; use precompile_utils::EvmResult; use crate::{PrecompileExt, PrecompileHandleExt}; struct DidUpdateStorage; impl StorageInstance for DidUpdateStorage { const STORAGE_PREFIX: &'static str = "DidUpdate"; fn pallet_prefix() -> &'static str { "Timestamp" } } type DidUpdate = StorageValue; pub struct TimestampPrecompile(PhantomData); impl PrecompileExt for TimestampPrecompile where R: frame_system::Config + pallet_evm::Config + pallet_timestamp::Config, R::AccountId: From<[u8; 32]>, R::Moment: TryInto, { const INDEX: u64 = 2065; } #[precompile_utils::precompile] impl TimestampPrecompile where R: frame_system::Config + pallet_evm::Config + pallet_timestamp::Config, R::AccountId: From<[u8; 32]>, R::Moment: TryInto, { #[precompile::public("getTimestamp()")] #[precompile::view] fn get_timestamp(handle: &mut impl PrecompileHandle) -> EvmResult { handle.record_db_reads::(1)?; let timestamp_millis: u64 = pallet_timestamp::Pallet::::get() .try_into() .map_err(|_| conversion_error("timestamp moment"))?; Ok(timestamp_millis / 1_000) } #[precompile::public("wasUpdatedThisBlock()")] #[precompile::view] fn was_updated_this_block(handle: &mut impl PrecompileHandle) -> EvmResult { handle.record_db_reads::(1)?; Ok(DidUpdate::get()) } } fn conversion_error(field: &'static str) -> PrecompileFailure { PrecompileFailure::Error { exit_status: ExitError::Other(field.into()), } } #[cfg(test)] mod tests { use super::*; use crate::mock::{ Runtime, Timestamp, addr_from_index, new_test_ext, precompiles, selector_u32, }; use precompile_utils::{ prelude::RuntimeHelper, solidity::{encode_return_value, encode_with_selector}, testing::PrecompileTesterExt, }; #[test] fn views_match_evm_timestamp_and_update_state() { new_test_ext().execute_with(|| { assert_eq!(TimestampPrecompile::::INDEX, 2065); Timestamp::set_timestamp(1_234_567); let precompiles = precompiles::>(); let caller = addr_from_index(1); let address = addr_from_index(2065); let read_cost = RuntimeHelper::::db_read_gas_cost(); precompiles .prepare_test( caller, address, encode_with_selector(selector_u32("getTimestamp()"), ()), ) .with_static_call(true) .expect_cost(read_cost) // Frontier exposes pallet timestamp milliseconds as EVM seconds. .execute_returns_raw(encode_return_value(1_234u64)); precompiles .prepare_test( caller, address, encode_with_selector(selector_u32("wasUpdatedThisBlock()"), ()), ) .with_static_call(true) .expect_cost(read_cost) .execute_returns_raw(encode_return_value(true)); }); } }