gmsol_store/states/common/
action.rs1use anchor_lang::{prelude::*, ZeroCopy};
2use gmsol_utils::{
3 action::{ActionError, MAX_ACTION_FLAGS},
4 InitSpace,
5};
6
7use crate::{
8 events::Event,
9 states::{NonceBytes, Seed},
10 utils::pubkey::optional_address,
11 CoreError,
12};
13
14pub use gmsol_utils::action::{ActionFlag, ActionState};
15
16#[zero_copy]
18#[cfg_attr(feature = "debug", derive(Debug))]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct ActionHeader {
21 version: u8,
22 action_state: u8,
24 pub(crate) bump: u8,
26 flags: ActionFlagContainer,
27 padding_0: [u8; 4],
28 pub id: u64,
30 pub store: Pubkey,
32 pub market: Pubkey,
34 pub owner: Pubkey,
36 pub nonce: [u8; 32],
38 pub(crate) max_execution_lamports: u64,
40 pub(crate) updated_at: i64,
42 pub(crate) updated_at_slot: u64,
44 pub(crate) creator: Pubkey,
46 rent_receiver: Pubkey,
48 receiver: Pubkey,
50 #[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
51 reserved: [u8; 256],
52}
53
54impl Default for ActionHeader {
55 fn default() -> Self {
56 bytemuck::Zeroable::zeroed()
57 }
58}
59
60gmsol_utils::flags!(ActionFlag, MAX_ACTION_FLAGS, u8);
61
62impl ActionHeader {
63 pub fn action_state(&self) -> Result<ActionState> {
65 ActionState::try_from(self.action_state).map_err(|_| error!(CoreError::UnknownActionState))
66 }
67
68 fn set_action_state(&mut self, new_state: ActionState) {
69 self.action_state = new_state.into();
70 }
71
72 pub(crate) fn completed(&mut self) -> Result<()> {
74 self.set_action_state(
75 self.action_state()?
76 .completed()
77 .map_err(CoreError::from)
78 .map_err(|err| error!(err))?,
79 );
80 Ok(())
81 }
82
83 pub(crate) fn cancelled(&mut self) -> Result<()> {
85 self.set_action_state(
86 self.action_state()?
87 .cancelled()
88 .map_err(CoreError::from)
89 .map_err(|err| error!(err))?,
90 );
91 Ok(())
92 }
93
94 pub(crate) fn signer(&self, seed: &'static [u8]) -> ActionSigner {
96 ActionSigner::new(seed, self.store, *self.creator(), self.nonce, self.bump)
97 }
98
99 pub fn owner(&self) -> &Pubkey {
101 &self.owner
102 }
103
104 pub fn receiver(&self) -> Pubkey {
106 *optional_address(&self.receiver).unwrap_or_else(|| self.owner())
107 }
108
109 pub fn id(&self) -> u64 {
111 self.id
112 }
113
114 pub fn store(&self) -> &Pubkey {
116 &self.store
117 }
118
119 pub fn market(&self) -> &Pubkey {
121 &self.market
122 }
123
124 pub fn nonce(&self) -> &[u8; 32] {
126 &self.nonce
127 }
128
129 pub fn max_execution_lamports(&self) -> u64 {
131 self.max_execution_lamports
132 }
133
134 pub fn updated_at(&self) -> i64 {
136 self.updated_at
137 }
138
139 pub fn updated_at_slot(&self) -> u64 {
141 self.updated_at_slot
142 }
143
144 pub fn bump(&self) -> u8 {
146 self.bump
147 }
148
149 pub fn creator(&self) -> &Pubkey {
152 &self.creator
153 }
154
155 pub fn rent_receiver(&self) -> &Pubkey {
157 &self.rent_receiver
158 }
159
160 #[inline(never)]
161 #[allow(clippy::too_many_arguments)]
162 pub(crate) fn init(
163 &mut self,
164 id: u64,
165 store: Pubkey,
166 market: Pubkey,
167 owner: Pubkey,
168 receiver: Pubkey,
169 nonce: [u8; 32],
170 bump: u8,
171 execution_lamports: u64,
172 should_unwrap_native_token: bool,
173 ) -> Result<()> {
174 let clock = Clock::get()?;
175 self.id = id;
176 self.store = store;
177 self.market = market;
178 self.owner = owner;
179
180 require!(
182 optional_address(&receiver).is_some(),
183 CoreError::InvalidArgument
184 );
185
186 self.receiver = receiver;
187 self.nonce = nonce;
188 self.max_execution_lamports = execution_lamports;
189 self.updated_at = clock.unix_timestamp;
190 self.updated_at_slot = clock.slot;
191 self.bump = bump;
192 self.creator = owner;
194 self.rent_receiver = owner;
196
197 self.set_should_unwrap_native_token(should_unwrap_native_token);
198
199 Ok(())
200 }
201
202 pub(crate) fn unchecked_set_creator(&mut self, creator: Pubkey) {
207 self.creator = creator;
208 }
209
210 pub(crate) fn set_rent_receiver(&mut self, rent_receiver: Pubkey) {
212 self.rent_receiver = rent_receiver;
213 }
214
215 pub(crate) fn updated(&mut self) -> Result<()> {
216 let clock = Clock::get()?;
217 self.updated_at = clock.unix_timestamp;
218 self.updated_at_slot = clock.slot;
219
220 Ok(())
221 }
222
223 pub fn should_unwrap_native_token(&self) -> bool {
225 self.flags.get_flag(ActionFlag::ShouldUnwrapNativeToken)
226 }
227
228 fn set_should_unwrap_native_token(&mut self, should_unwrap: bool) -> bool {
232 self.flags
233 .set_flag(ActionFlag::ShouldUnwrapNativeToken, should_unwrap)
234 }
235}
236
237pub struct ActionSigner {
239 seed: &'static [u8],
240 store: Pubkey,
241 owner: Pubkey,
242 nonce: NonceBytes,
243 bump_bytes: [u8; 1],
244}
245
246impl ActionSigner {
247 pub fn new(
249 seed: &'static [u8],
250 store: Pubkey,
251 owner: Pubkey,
252 nonce: NonceBytes,
253 bump: u8,
254 ) -> Self {
255 Self {
256 seed,
257 store,
258 owner,
259 nonce,
260 bump_bytes: [bump],
261 }
262 }
263
264 pub fn as_seeds(&self) -> [&[u8]; 5] {
266 [
267 self.seed,
268 self.store.as_ref(),
269 self.owner.as_ref(),
270 &self.nonce,
271 &self.bump_bytes,
272 ]
273 }
274}
275
276pub trait Action {
278 const MIN_EXECUTION_LAMPORTS: u64;
280
281 fn header(&self) -> &ActionHeader;
283}
284
285pub trait ActionExt: Action {
287 fn signer(&self) -> ActionSigner
289 where
290 Self: Seed,
291 {
292 self.header().signer(Self::SEED)
293 }
294
295 fn execution_lamports(&self, execution_lamports: u64) -> u64 {
297 execution_lamports.min(self.header().max_execution_lamports)
298 }
299
300 fn validate_balance(account: &AccountLoader<Self>, execution_lamports: u64) -> Result<()>
302 where
303 Self: ZeroCopy + Owner + InitSpace,
304 {
305 require_gte!(
306 execution_lamports,
307 Self::MIN_EXECUTION_LAMPORTS,
308 CoreError::NotEnoughExecutionFee
309 );
310 let balance = account.get_lamports().saturating_sub(execution_lamports);
311 let rent = Rent::get()?;
312 require!(
313 rent.is_exempt(balance, 8 + Self::INIT_SPACE),
314 CoreError::NotEnoughExecutionFee
315 );
316 Ok(())
317 }
318}
319
320impl<T: Action> ActionExt for T {}
321
322pub trait ActionParams {
324 fn execution_lamports(&self) -> u64;
326}
327
328pub trait Closable {
330 type ClosedEvent: Event + InitSpace;
332
333 fn to_closed_event(&self, address: &Pubkey, reason: &str) -> Result<Self::ClosedEvent>;
335}
336
337impl From<ActionError> for CoreError {
338 fn from(err: ActionError) -> Self {
339 msg!("Action error: {}", err);
340 match err {
341 ActionError::PreconditionsAreNotMet(_) => Self::PreconditionsAreNotMet,
342 }
343 }
344}