aurora_engine_precompiles/
promise_result.rs

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