tempo_precompiles/storage_credits/
dispatch.rs1use crate::{
4 Precompile, charge_input_cost, dispatch_call, mutate_void, storage_credits::StorageCredits,
5 view,
6};
7use alloy::{primitives::Address, sol_types::SolInterface};
8use revm::precompile::PrecompileResult;
9use tempo_contracts::precompiles::IStorageCredits::IStorageCreditsCalls;
10
11impl Precompile for StorageCredits {
12 fn call(&mut self, calldata: &[u8], msg_sender: Address) -> PrecompileResult {
13 if let Some(err) = charge_input_cost(&mut self.storage, calldata) {
14 return err;
15 }
16
17 dispatch_call(
18 calldata,
19 &[],
20 IStorageCreditsCalls::abi_decode,
21 |call| match call {
22 IStorageCreditsCalls::balanceOf(call) => view(call, |c| self.balance_of(c.account)),
23 IStorageCreditsCalls::modeOf(call) => {
24 view(call, |c| self.mode_of(c.account).map(Into::into))
25 }
26 IStorageCreditsCalls::budgetOf(call) => view(call, |c| self.budget_of(c.account)),
27 IStorageCreditsCalls::setMode(call) => {
28 mutate_void(call, msg_sender, |sender, c| {
29 self.set_mode(sender, c.newMode)
30 })
31 }
32 IStorageCreditsCalls::setBudget(call) => {
33 mutate_void(call, msg_sender, |sender, c| {
34 self.set_budget(sender, c.creditBudget)
35 })
36 }
37 },
38 )
39 }
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45 use crate::{
46 storage::{StorageCtx, hashmap::HashMapStorageProvider},
47 test_util::{assert_full_coverage, check_selector_coverage},
48 };
49 use alloy::sol_types::SolCall;
50 use tempo_contracts::precompiles::{IStorageCredits, StorageCreditsError};
51
52 #[test]
53 fn test_storage_credits_selector_coverage() -> eyre::Result<()> {
54 let mut storage = HashMapStorageProvider::new(1);
55 StorageCtx::enter(&mut storage, || {
56 let mut storage_credits_precompile = StorageCredits::new();
57
58 let unsupported = check_selector_coverage(
59 &mut storage_credits_precompile,
60 IStorageCreditsCalls::SELECTORS,
61 "IStorageCredits",
62 IStorageCreditsCalls::name_by_selector,
63 );
64
65 assert_full_coverage([unsupported]);
66 Ok(())
67 })
68 }
69
70 #[test]
71 fn test_storage_credits_set_budget_zero_stays_direct() -> eyre::Result<()> {
72 let caller = Address::repeat_byte(0x11);
73 let mut storage = HashMapStorageProvider::new(1);
74
75 StorageCtx::enter(&mut storage, || {
76 let mut storage_credits_precompile = StorageCredits::new();
77
78 let set_budget = IStorageCredits::setBudgetCall { creditBudget: 7 };
79 let output = storage_credits_precompile.call(&set_budget.abi_encode(), caller)?;
80 assert!(!output.is_revert());
81
82 let mode = storage_credits_precompile.call(
83 &IStorageCredits::modeOfCall { account: caller }.abi_encode(),
84 caller,
85 )?;
86 assert_eq!(
87 IStorageCredits::modeOfCall::abi_decode_returns(&mode.bytes)?,
88 IStorageCredits::Mode::Direct
89 );
90
91 let budget = storage_credits_precompile.call(
92 &IStorageCredits::budgetOfCall { account: caller }.abi_encode(),
93 caller,
94 )?;
95 assert_eq!(
96 IStorageCredits::budgetOfCall::abi_decode_returns(&budget.bytes)?,
97 7
98 );
99
100 let zero_budget = IStorageCredits::setBudgetCall { creditBudget: 0 };
101 let output = storage_credits_precompile.call(&zero_budget.abi_encode(), caller)?;
102 assert!(!output.is_revert());
103
104 let mode = storage_credits_precompile.call(
105 &IStorageCredits::modeOfCall { account: caller }.abi_encode(),
106 caller,
107 )?;
108 assert_eq!(
109 IStorageCredits::modeOfCall::abi_decode_returns(&mode.bytes)?,
110 IStorageCredits::Mode::Direct,
111 "setBudget(0) keeps Direct selected with a zero spend budget"
112 );
113
114 let budget = storage_credits_precompile.call(
115 &IStorageCredits::budgetOfCall { account: caller }.abi_encode(),
116 caller,
117 )?;
118 assert_eq!(
119 IStorageCredits::budgetOfCall::abi_decode_returns(&budget.bytes)?,
120 0
121 );
122
123 Ok(())
124 })
125 }
126
127 #[test]
128 fn test_storage_credits_set_mode_rejects_reserved_mode() -> eyre::Result<()> {
129 let caller = Address::repeat_byte(0x33);
130 let mut storage = HashMapStorageProvider::new(1);
131
132 StorageCtx::enter(&mut storage, || {
133 let mut storage_credits_precompile = StorageCredits::new();
134 let mut calldata = IStorageCredits::setModeCall {
135 newMode: IStorageCredits::Mode::Refund,
136 }
137 .abi_encode();
138 *calldata
139 .last_mut()
140 .expect("setMode ABI calldata must contain the enum word") = 3;
141
142 let output = storage_credits_precompile.call(&calldata, caller)?;
143 assert!(output.is_revert());
144 assert_eq!(
145 &output.bytes[..4],
146 StorageCreditsError::invalid_mode().selector().as_slice()
147 );
148
149 Ok(())
150 })
151 }
152}