aurora_engine_precompiles/
promise_result.rs

1use aurora_engine_sdk::promise::ReadOnlyPromiseHandler;
2use aurora_engine_types::{Cow, Vec, borsh};
3use aurora_evm::{Context, ExitError};
4
5use super::{EvmPrecompileResult, Precompile};
6use crate::prelude::types::{Address, EthGas, make_address};
7use crate::{PrecompileOutput, utils};
8
9/// `get_promise_results` precompile address
10///
11/// Address: `0x0a3540f79be10ef14890e87c1a0040a68cc6af71`
12/// This address is computed as: `&keccak("getPromiseResults")[12..]`
13pub const ADDRESS: Address = make_address(0x0a3540f7, 0x9be10ef14890e87c1a0040a68cc6af71);
14
15pub mod costs {
16    use crate::prelude::types::EthGas;
17
18    /// This cost is always charged for calling this precompile.
19    pub const PROMISE_RESULT_BASE_COST: EthGas = EthGas::new(111);
20    /// This is the cost per byte of promise result data.
21    pub const PROMISE_RESULT_BYTE_COST: EthGas = EthGas::new(2);
22}
23
24pub struct PromiseResult<H> {
25    handler: H,
26}
27
28impl<H> PromiseResult<H> {
29    pub const fn new(handler: H) -> Self {
30        Self { handler }
31    }
32}
33
34impl<H: ReadOnlyPromiseHandler> Precompile for PromiseResult<H> {
35    fn required_gas(_input: &[u8]) -> Result<EthGas, ExitError> {
36        // Only gives the cost we can know without reading any promise data.
37        // This allows failing fast in the case the base cost cannot even be covered.
38        Ok(costs::PROMISE_RESULT_BASE_COST)
39    }
40
41    fn run(
42        &self,
43        input: &[u8],
44        target_gas: Option<EthGas>,
45        context: &Context,
46        _is_static: bool,
47    ) -> EvmPrecompileResult {
48        utils::validate_no_value_attached_to_precompile(context.apparent_value)?;
49        let mut cost = Self::required_gas(input)?;
50        let check_cost = |cost: EthGas| -> Result<(), ExitError> {
51            if let Some(target_gas) = target_gas {
52                if cost > target_gas {
53                    return Err(ExitError::OutOfGas);
54                }
55            }
56            Ok(())
57        };
58        check_cost(cost)?;
59
60        let num_promises = self.handler.ro_promise_results_count();
61        let n_usize = usize::try_from(num_promises).map_err(utils::err_usize_conv)?;
62        let mut results = Vec::with_capacity(n_usize);
63        for i in 0..num_promises {
64            if let Some(result) = self.handler.ro_promise_result(i) {
65                let n_bytes = u64::try_from(result.size()).map_err(utils::err_usize_conv)?;
66                cost = EthGas::new(n_bytes)
67                    .checked_mul(costs::PROMISE_RESULT_BYTE_COST)
68                    .and_then(|result| result.checked_add(cost))
69                    .ok_or(ExitError::Other(Cow::Borrowed("ERR_OVERFLOW_NUMBER")))?;
70                check_cost(cost)?;
71                results.push(result);
72            }
73        }
74
75        let bytes = borsh::to_vec(&results)
76            .map_err(|_| ExitError::Other(Cow::Borrowed("ERR_PROMISE_RESULT_SERIALIZATION")))?;
77        Ok(PrecompileOutput::without_logs(cost, bytes))
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use crate::prelude::sdk::types::near_account_to_evm_address;
84    use crate::promise_result;
85
86    #[test]
87    fn test_get_promise_results_precompile_id() {
88        assert_eq!(
89            promise_result::ADDRESS,
90            near_account_to_evm_address(b"getPromiseResults")
91        );
92    }
93}