chia_sdk_driver/action_system/
action.rs

1use chia_protocol::Bytes32;
2use chia_puzzle_types::{
3    offer::{NotarizedPayment, Payment},
4    Memos,
5};
6use hex_literal::hex;
7
8use crate::{
9    CreateDidAction, Delta, Deltas, DriverError, FeeAction, HashedPtr, Id, IssueCatAction,
10    MeltSingletonAction, MintNftAction, MintOptionAction, OptionType, RunTailAction, SendAction,
11    SettleAction, Spend, SpendContext, Spends, TailIssuance, TransferNftById, UpdateDidAction,
12    UpdateNftAction,
13};
14
15pub const BURN_PUZZLE_HASH: Bytes32 = Bytes32::new(hex!(
16    "000000000000000000000000000000000000000000000000000000000000dead"
17));
18
19#[derive(Debug, Clone)]
20pub enum Action {
21    Send(SendAction),
22    Settle(SettleAction),
23    CreateDid(CreateDidAction),
24    UpdateDid(UpdateDidAction),
25    MintNft(MintNftAction),
26    UpdateNft(UpdateNftAction),
27    IssueCat(IssueCatAction),
28    RunTail(RunTailAction),
29    MintOption(MintOptionAction),
30    MeltSingleton(MeltSingletonAction),
31    Fee(FeeAction),
32}
33
34impl Action {
35    pub fn send(id: Id, puzzle_hash: Bytes32, amount: u64, memos: Memos) -> Self {
36        Self::Send(SendAction::new(id, puzzle_hash, amount, memos))
37    }
38
39    pub fn settle(id: Id, notarized_payment: NotarizedPayment) -> Self {
40        Self::Settle(SettleAction::new(id, notarized_payment))
41    }
42
43    pub fn settle_royalty(
44        ctx: &mut SpendContext,
45        id: Id,
46        launcher_id: Bytes32,
47        royalty_puzzle_hash: Bytes32,
48        royalty_amount: u64,
49    ) -> Result<Self, DriverError> {
50        let hint = ctx.hint(royalty_puzzle_hash)?;
51
52        Ok(Self::settle(
53            id,
54            NotarizedPayment::new(
55                launcher_id,
56                vec![Payment::new(royalty_puzzle_hash, royalty_amount, hint)],
57            ),
58        ))
59    }
60
61    pub fn burn(id: Id, amount: u64, memos: Memos) -> Self {
62        Self::Send(SendAction::new(id, BURN_PUZZLE_HASH, amount, memos))
63    }
64
65    pub fn create_did(
66        recovery_list_hash: Option<Bytes32>,
67        num_verifications_required: u64,
68        metadata: HashedPtr,
69        amount: u64,
70    ) -> Self {
71        Self::CreateDid(CreateDidAction::new(
72            recovery_list_hash,
73            num_verifications_required,
74            metadata,
75            amount,
76        ))
77    }
78
79    pub fn create_empty_did() -> Self {
80        Self::CreateDid(CreateDidAction::default())
81    }
82
83    pub fn update_did(
84        id: Id,
85        new_recovery_list_hash: Option<Option<Bytes32>>,
86        new_num_verifications_required: Option<u64>,
87        new_metadata: Option<HashedPtr>,
88    ) -> Self {
89        Self::UpdateDid(UpdateDidAction::new(
90            id,
91            new_recovery_list_hash,
92            new_num_verifications_required,
93            new_metadata,
94        ))
95    }
96
97    pub fn mint_nft(
98        metadata: HashedPtr,
99        metadata_updater_puzzle_hash: Bytes32,
100        royalty_puzzle_hash: Bytes32,
101        royalty_basis_points: u16,
102        amount: u64,
103    ) -> Self {
104        Self::MintNft(MintNftAction::new(
105            Id::Xch,
106            metadata,
107            metadata_updater_puzzle_hash,
108            royalty_puzzle_hash,
109            royalty_basis_points,
110            amount,
111        ))
112    }
113
114    pub fn mint_nft_from_did(
115        parent_did_id: Id,
116        metadata: HashedPtr,
117        metadata_updater_puzzle_hash: Bytes32,
118        royalty_puzzle_hash: Bytes32,
119        royalty_basis_points: u16,
120        amount: u64,
121    ) -> Self {
122        Self::MintNft(MintNftAction::new(
123            parent_did_id,
124            metadata,
125            metadata_updater_puzzle_hash,
126            royalty_puzzle_hash,
127            royalty_basis_points,
128            amount,
129        ))
130    }
131
132    pub fn mint_empty_nft() -> Self {
133        Self::mint_nft(HashedPtr::NIL, Bytes32::default(), Bytes32::default(), 0, 1)
134    }
135
136    pub fn mint_empty_nft_from_did(parent_did_id: Id) -> Self {
137        Self::mint_nft_from_did(
138            parent_did_id,
139            HashedPtr::NIL,
140            Bytes32::default(),
141            Bytes32::default(),
142            0,
143            1,
144        )
145    }
146
147    pub fn mint_empty_royalty_nft(royalty_puzzle_hash: Bytes32, royalty_basis_points: u16) -> Self {
148        Self::mint_nft(
149            HashedPtr::NIL,
150            Bytes32::default(),
151            royalty_puzzle_hash,
152            royalty_basis_points,
153            1,
154        )
155    }
156
157    pub fn mint_empty_royalty_nft_from_did(
158        parent_did_id: Id,
159        royalty_puzzle_hash: Bytes32,
160        royalty_basis_points: u16,
161    ) -> Self {
162        Self::mint_nft_from_did(
163            parent_did_id,
164            HashedPtr::NIL,
165            Bytes32::default(),
166            royalty_puzzle_hash,
167            royalty_basis_points,
168            1,
169        )
170    }
171
172    pub fn update_nft(
173        id: Id,
174        metadata_update_spends: Vec<Spend>,
175        transfer: Option<TransferNftById>,
176    ) -> Self {
177        Self::UpdateNft(UpdateNftAction::new(id, metadata_update_spends, transfer))
178    }
179
180    pub fn issue_cat(tail_spend: Spend, hidden_puzzle_hash: Option<Bytes32>, amount: u64) -> Self {
181        Self::IssueCat(IssueCatAction::new(
182            TailIssuance::Multiple(tail_spend),
183            hidden_puzzle_hash,
184            amount,
185        ))
186    }
187
188    pub fn single_issue_cat(hidden_puzzle_hash: Option<Bytes32>, amount: u64) -> Self {
189        Self::IssueCat(IssueCatAction::new(
190            TailIssuance::Single,
191            hidden_puzzle_hash,
192            amount,
193        ))
194    }
195
196    pub fn run_tail(id: Id, tail_spend: Spend, supply_delta: Delta) -> Self {
197        Self::RunTail(RunTailAction::new(id, tail_spend, supply_delta))
198    }
199
200    pub fn mint_option(
201        creator_puzzle_hash: Bytes32,
202        seconds: u64,
203        underlying_id: Id,
204        underlying_amount: u64,
205        strike_type: OptionType,
206        amount: u64,
207    ) -> Self {
208        Self::MintOption(MintOptionAction::new(
209            creator_puzzle_hash,
210            seconds,
211            underlying_id,
212            underlying_amount,
213            strike_type,
214            amount,
215        ))
216    }
217
218    pub fn melt_singleton(id: Id, amount: u64) -> Self {
219        Self::MeltSingleton(MeltSingletonAction::new(id, amount))
220    }
221
222    pub fn fee(amount: u64) -> Self {
223        Self::Fee(FeeAction::new(amount))
224    }
225}
226
227pub trait SpendAction {
228    fn calculate_delta(&self, deltas: &mut Deltas, index: usize);
229
230    fn spend(
231        &self,
232        ctx: &mut SpendContext,
233        spends: &mut Spends,
234        index: usize,
235    ) -> Result<(), DriverError>;
236}
237
238impl SpendAction for Action {
239    fn calculate_delta(&self, deltas: &mut Deltas, index: usize) {
240        match self {
241            Action::Send(action) => action.calculate_delta(deltas, index),
242            Action::Settle(action) => action.calculate_delta(deltas, index),
243            Action::CreateDid(action) => action.calculate_delta(deltas, index),
244            Action::UpdateDid(action) => action.calculate_delta(deltas, index),
245            Action::MintNft(action) => action.calculate_delta(deltas, index),
246            Action::UpdateNft(action) => action.calculate_delta(deltas, index),
247            Action::IssueCat(action) => action.calculate_delta(deltas, index),
248            Action::RunTail(action) => action.calculate_delta(deltas, index),
249            Action::MintOption(action) => action.calculate_delta(deltas, index),
250            Action::MeltSingleton(action) => action.calculate_delta(deltas, index),
251            Action::Fee(action) => action.calculate_delta(deltas, index),
252        }
253    }
254
255    fn spend(
256        &self,
257        ctx: &mut SpendContext,
258        spends: &mut Spends,
259        index: usize,
260    ) -> Result<(), DriverError> {
261        match self {
262            Action::Send(action) => action.spend(ctx, spends, index),
263            Action::Settle(action) => action.spend(ctx, spends, index),
264            Action::CreateDid(action) => action.spend(ctx, spends, index),
265            Action::UpdateDid(action) => action.spend(ctx, spends, index),
266            Action::MintNft(action) => action.spend(ctx, spends, index),
267            Action::UpdateNft(action) => action.spend(ctx, spends, index),
268            Action::IssueCat(action) => action.spend(ctx, spends, index),
269            Action::RunTail(action) => action.spend(ctx, spends, index),
270            Action::MintOption(action) => action.spend(ctx, spends, index),
271            Action::MeltSingleton(action) => action.spend(ctx, spends, index),
272            Action::Fee(action) => action.spend(ctx, spends, index),
273        }
274    }
275}