tempo_precompiles/tip1060_storage_credits/
mod.rs1pub mod dispatch;
4pub mod gas_state;
5
6pub use gas_state::{StorageCreditsBackend, StorageCreditsError, sstore_storage_credits};
7
8use crate::{
9 STORAGE_CREDITS_ADDRESS,
10 error::{Result, TempoPrecompileError},
11 storage::{Handler, LayoutCtx, StorableType},
12};
13use alloy::primitives::{Address, U256};
14use tempo_contracts::precompiles::{
15 ITIP1060StorageCredits::Mode, TIP1060StorageCreditsError, TIP1060StorageCreditsEvent,
16};
17use tempo_precompiles_macros::{Storable, contract};
18
19#[repr(u8)]
20#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Storable)]
21pub enum CreditMode {
22 #[default]
23 Refund,
24 Preserve,
25 Direct,
26}
27
28#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
30pub struct TransientState {
31 pub budget: u64,
33 pub mode: CreditMode,
35 pub pending_refunds: u64,
37}
38
39impl TryFrom<U256> for TransientState {
40 type Error = TempoPrecompileError;
41
42 #[inline]
43 fn try_from(value: U256) -> Result<Self> {
44 let limbs = value.as_limbs();
45 Ok(Self {
46 budget: limbs[0],
47 mode: (limbs[1] as u8).try_into()?,
48 pending_refunds: limbs[3],
49 })
50 }
51}
52
53impl From<TransientState> for U256 {
54 #[inline]
55 fn from(value: TransientState) -> Self {
56 Self::from_limbs([value.budget, value.mode as u64, 0, value.pending_refunds])
57 }
58}
59
60#[contract(addr = STORAGE_CREDITS_ADDRESS)]
74pub struct TIP1060StorageCredits {}
75
76impl TIP1060StorageCredits {
77 pub fn initialize(&mut self) -> Result<()> {
78 self.__initialize()
79 }
80
81 pub fn balance_of(&self, account: Address) -> Result<u64> {
82 u64::handle(Self::slot(account), LayoutCtx::FULL, self.address).read()
83 }
84
85 pub fn mode_of(&self, account: Address) -> Result<CreditMode> {
86 self.credit_state_of(account).map(|state| state.mode)
87 }
88
89 pub fn budget_of(&self, account: Address) -> Result<u64> {
90 self.credit_state_of(account).map(|state| state.budget)
91 }
92
93 pub fn set_mode(&mut self, msg_sender: Address, mode: Mode) -> Result<()> {
94 let mode = CreditMode::try_from(mode)?;
95 let budget = if matches!(mode, CreditMode::Direct) {
96 u64::MAX
97 } else {
98 0
99 };
100
101 self.write_mode_with_budget(msg_sender, mode, budget)?;
102 self.emit_event(TIP1060StorageCreditsEvent::mode_updated(
103 msg_sender,
104 mode.into(),
105 ))
106 }
107
108 pub fn set_budget(&mut self, msg_sender: Address, credit_budget: u64) -> Result<()> {
109 self.write_mode_with_budget(msg_sender, CreditMode::Direct, credit_budget)?;
110 self.emit_event(TIP1060StorageCreditsEvent::mode_updated(
111 msg_sender,
112 Mode::Direct,
113 ))
114 }
115
116 fn write_mode_with_budget(
117 &mut self,
118 msg_sender: Address,
119 mode: CreditMode,
120 budget: u64,
121 ) -> Result<()> {
122 let mut state = self.credit_state_of(msg_sender)?;
123 state.mode = mode;
124 state.budget = budget;
125 self.write_credit_state_of(msg_sender, state)
126 }
127
128 #[inline]
129 pub fn slot(account: Address) -> U256 {
130 U256::from_be_bytes(account.into_word().0)
131 }
132
133 #[inline]
134 fn credit_state_of(&self, account: Address) -> Result<TransientState> {
135 U256::handle(Self::slot(account), LayoutCtx::FULL, self.address)
136 .t_read()?
137 .try_into()
138 }
139
140 #[inline]
141 fn write_credit_state_of(&mut self, account: Address, state: TransientState) -> Result<()> {
142 U256::handle(Self::slot(account), LayoutCtx::FULL, self.address).t_write(state.into())
143 }
144}
145
146impl TryFrom<u8> for CreditMode {
147 type Error = TempoPrecompileError;
148
149 fn try_from(value: u8) -> Result<Self> {
150 match value {
151 0 => Ok(Self::Refund),
152 1 => Ok(Self::Preserve),
153 2 => Ok(Self::Direct),
154 _ => Err(TIP1060StorageCreditsError::invalid_mode().into()),
155 }
156 }
157}
158
159impl TryFrom<Mode> for CreditMode {
160 type Error = TempoPrecompileError;
161
162 fn try_from(mode: Mode) -> Result<Self> {
163 match mode {
164 Mode::Refund => Ok(Self::Refund),
165 Mode::Preserve => Ok(Self::Preserve),
166 Mode::Direct => Ok(Self::Direct),
167 _ => Err(TIP1060StorageCreditsError::invalid_mode().into()),
168 }
169 }
170}
171
172impl From<CreditMode> for Mode {
173 fn from(mode: CreditMode) -> Self {
174 match mode {
175 CreditMode::Refund => Self::Refund,
176 CreditMode::Preserve => Self::Preserve,
177 CreditMode::Direct => Self::Direct,
178 }
179 }
180}