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}