1use std::collections::BTreeMap;
9
10use serde::{Deserialize, Serialize};
11use serde_json::Value;
12
13use crate::{HashId, ProgramRelease, ProgramSpec, StackManifest};
14
15pub const PROTOCOL_SPEC_SCHEMA_V1: &str = "arete.protocol-spec/v1";
16pub const PROTOCOL_HANDLER_ABI_V1: &str = "arete.protocol-handler/v1";
17pub const PROTOCOL_CTX_V1: &str = "arete.protocol-ctx/v1";
18
19#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
20#[serde(rename_all = "camelCase", deny_unknown_fields)]
21pub struct ProtocolSpecV1 {
22 pub schema: String,
23 pub name: String,
24 pub version: String,
25 #[serde(default, skip_serializing_if = "Option::is_none")]
26 pub description: Option<String>,
27 #[serde(default, skip_serializing_if = "Option::is_none")]
28 pub handler_abi: Option<String>,
29 #[serde(default, skip_serializing_if = "Option::is_none")]
30 pub ctx_version: Option<String>,
31 pub programs: BTreeMap<String, ProtocolProgramV1>,
32 pub stacks: BTreeMap<String, ProtocolStackDependencyV1>,
33 pub params: ObjectSchemaV1,
34 pub roles: BTreeMap<String, RoleV1>,
35 pub resources: BTreeMap<String, ResourceDeclV1>,
36 pub instance_visibility: InstanceVisibility,
37 pub reads: BTreeMap<String, ReadV1>,
38 pub machines: MachinesV1,
39 pub steps: BTreeMap<String, StepV1>,
40 pub reactions: Vec<ReactionV1>,
41 pub events: BTreeMap<String, EventV1>,
42 pub metadata: MetadataV1,
43 pub stack: StackSurfaceV1,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
47#[serde(tag = "kind", rename_all = "camelCase", deny_unknown_fields)]
48pub enum ProtocolProgramV1 {
49 #[serde(rename_all = "camelCase")]
51 ProgramSpec {
52 program_id: String,
53 program_spec_hash: HashId<ProgramSpec>,
54 #[serde(default, skip_serializing_if = "Option::is_none")]
55 program_release_hash: Option<HashId<ProgramRelease>>,
56 },
57 Builtin { builtin: BuiltinProgram },
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
62#[serde(rename_all = "camelCase")]
63pub enum BuiltinProgram {
64 System,
65 SplToken,
66 AssociatedToken,
67 ComputeBudget,
68 Memo,
69}
70
71impl BuiltinProgram {
72 pub const ALL: [&'static str; 5] = [
73 "system",
74 "splToken",
75 "associatedToken",
76 "computeBudget",
77 "memo",
78 ];
79
80 pub const fn program_id(self) -> &'static str {
81 match self {
82 Self::System => "11111111111111111111111111111111",
83 Self::SplToken => "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
84 Self::AssociatedToken => "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL",
85 Self::ComputeBudget => "ComputeBudget111111111111111111111111111111",
86 Self::Memo => "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr",
87 }
88 }
89}
90
91#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
92#[serde(rename_all = "camelCase", deny_unknown_fields)]
93pub struct ProtocolStackDependencyV1 {
94 pub stack_manifest_hash: HashId<StackManifest>,
95 #[serde(default, skip_serializing_if = "Option::is_none")]
96 pub package: Option<String>,
97}
98
99#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
101#[serde(rename_all = "camelCase", deny_unknown_fields)]
102pub struct ObjectSchemaV1 {
103 #[serde(rename = "type")]
104 pub schema_type: ObjectType,
105 pub properties: BTreeMap<String, FieldSchemaV1>,
106 pub required: Vec<String>,
107 pub additional_properties: bool,
108}
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
111#[serde(rename_all = "camelCase")]
112pub enum ObjectType {
113 Object,
114}
115
116#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
117#[serde(rename_all = "camelCase", deny_unknown_fields)]
118pub struct FieldSchemaV1 {
119 #[serde(rename = "type")]
120 pub field_type: FieldType,
121 #[serde(default, skip_serializing_if = "Option::is_none")]
122 pub format: Option<StringFormat>,
123 #[serde(default, rename = "enum", skip_serializing_if = "Option::is_none")]
124 pub enum_values: Option<Vec<String>>,
125 #[serde(default, skip_serializing_if = "Option::is_none")]
126 pub max_length: Option<u32>,
127 #[serde(default, skip_serializing_if = "Option::is_none")]
128 pub minimum: Option<i64>,
129 #[serde(default, skip_serializing_if = "Option::is_none")]
130 pub maximum: Option<i64>,
131 #[serde(default, skip_serializing_if = "Option::is_none")]
132 pub items: Option<Box<FieldSchemaV1>>,
133 #[serde(default, skip_serializing_if = "Option::is_none")]
134 pub max_items: Option<u32>,
135 #[serde(default, skip_serializing_if = "Option::is_none")]
136 pub properties: Option<BTreeMap<String, FieldSchemaV1>>,
137 #[serde(default, skip_serializing_if = "Option::is_none")]
138 pub required: Option<Vec<String>>,
139 #[serde(default, skip_serializing_if = "Option::is_none")]
140 pub additional_properties: Option<bool>,
141 #[serde(default, skip_serializing_if = "Option::is_none")]
142 pub description: Option<String>,
143}
144
145#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
146#[serde(rename_all = "camelCase")]
147pub enum FieldType {
148 String,
149 Boolean,
150 Integer,
151 Array,
152 Object,
153}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
157#[serde(rename_all = "camelCase")]
158pub enum StringFormat {
159 Pubkey,
160 U8,
161 U16,
162 U32,
163 U64,
164 U128,
165 I64,
166 UnixTimestamp,
168 Uri,
169}
170
171impl StringFormat {
172 pub const ALL: [&'static str; 9] = [
173 "pubkey",
174 "u8",
175 "u16",
176 "u32",
177 "u64",
178 "u128",
179 "i64",
180 "unixTimestamp",
181 "uri",
182 ];
183}
184
185#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
186#[serde(rename_all = "camelCase", deny_unknown_fields)]
187pub struct RoleV1 {
188 #[serde(default, skip_serializing_if = "Option::is_none")]
189 pub description: Option<String>,
190}
191
192#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
193#[serde(rename_all = "camelCase", deny_unknown_fields)]
194pub struct ResourceDeclV1 {
195 pub schema: FieldSchemaV1,
196 #[serde(default, skip_serializing_if = "Option::is_none")]
197 pub description: Option<String>,
198}
199
200#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
201#[serde(rename_all = "camelCase")]
202pub enum InstanceVisibility {
203 Shared,
205 Account,
207}
208
209#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
212#[serde(tag = "kind", rename_all = "camelCase", deny_unknown_fields)]
213pub enum ReadV1 {
214 Account {
216 program: String,
217 account: String,
218 address: String,
219 optional: bool,
220 },
221 TokenAccount { address: String, optional: bool },
223 Lamports { address: String },
225}
226
227#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
228#[serde(rename_all = "camelCase", deny_unknown_fields)]
229pub struct MachinesV1 {
230 pub instance: MachineV1,
231 #[serde(default, skip_serializing_if = "Option::is_none")]
232 pub participant: Option<MachineV1>,
233}
234
235#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
236#[serde(rename_all = "camelCase")]
237pub enum MachineKind {
238 Instance,
239 Participant,
240}
241
242impl MachineKind {
243 pub const fn as_str(self) -> &'static str {
244 match self {
245 Self::Instance => "instance",
246 Self::Participant => "participant",
247 }
248 }
249}
250
251#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
252#[serde(rename_all = "camelCase", deny_unknown_fields)]
253pub struct MachineV1 {
254 pub initial: String,
255 pub states: BTreeMap<String, StateV1>,
256 pub derive: Vec<DeriveRuleV1>,
260 pub reads: Vec<String>,
262}
263
264#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
265#[serde(rename_all = "camelCase", deny_unknown_fields)]
266pub struct StateV1 {
267 pub kind: StateKind,
268 pub terminal: bool,
269 #[serde(default, skip_serializing_if = "Option::is_none")]
270 pub description: Option<String>,
271}
272
273#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
274#[serde(rename_all = "camelCase")]
275pub enum StateKind {
276 Recorded,
278 Derived,
280}
281
282#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
283#[serde(rename_all = "camelCase", deny_unknown_fields)]
284pub struct DeriveRuleV1 {
285 pub from: Vec<String>,
286 pub to: String,
287 pub when: String,
288 #[serde(default, skip_serializing_if = "Option::is_none")]
291 pub at: Option<String>,
292}
293
294#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
295#[serde(rename_all = "camelCase")]
296pub enum StepScope {
297 Create,
298 Instance,
299 Participant,
300}
301
302#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
303#[serde(tag = "kind", rename_all = "camelCase", deny_unknown_fields)]
304pub enum AuthorityV1 {
305 Creator,
307 Participant,
309 Role { role: String },
312 Keeper,
314 #[serde(rename_all = "camelCase")]
316 SquadsProposer {
317 multisig: String,
318 #[serde(default, skip_serializing_if = "Option::is_none")]
321 vault_index: Option<u8>,
322 },
323}
324
325impl AuthorityV1 {
326 pub fn signer_class(&self) -> SignerClass {
327 match self {
328 Self::Creator | Self::Participant | Self::Role { .. } => SignerClass::Wallet,
329 Self::Keeper => SignerClass::Keeper,
330 Self::SquadsProposer { .. } => SignerClass::SquadsProposer,
331 }
332 }
333
334 pub fn squads_vault_index(&self) -> Option<u8> {
337 match self {
338 Self::SquadsProposer { vault_index, .. } => Some(vault_index.unwrap_or(0)),
339 _ => None,
340 }
341 }
342
343 pub fn is_client_callable(&self) -> bool {
345 matches!(self.signer_class(), SignerClass::Wallet)
346 }
347}
348
349#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
350#[serde(rename_all = "camelCase")]
351pub enum SignerClass {
352 Wallet,
353 Ephemeral,
354 Attestation,
355 Keeper,
356 SquadsProposer,
357}
358
359impl SignerClass {
360 pub const ALL: [&'static str; 5] = [
361 "wallet",
362 "ephemeral",
363 "attestation",
364 "keeper",
365 "squadsProposer",
366 ];
367}
368
369#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
370#[serde(rename_all = "camelCase")]
371pub enum StepAccess {
372 Public,
373 Secret,
374}
375
376#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
377#[serde(rename_all = "camelCase", deny_unknown_fields)]
378pub struct StepV1 {
379 pub scope: StepScope,
380 #[serde(default, skip_serializing_if = "Option::is_none")]
381 pub title: Option<String>,
382 #[serde(default, skip_serializing_if = "Option::is_none")]
383 pub description: Option<String>,
384 pub authority: AuthorityV1,
385 #[serde(default, skip_serializing_if = "Option::is_none")]
387 pub access: Option<StepAccess>,
388 pub args: ObjectSchemaV1,
389 pub guards: Vec<GuardV1>,
390 pub reads: Vec<String>,
391 pub ephemeral: Vec<String>,
393 pub fee: FeeBasisV1,
394 pub transitions: Vec<TransitionV1>,
395 #[serde(default, skip_serializing_if = "Option::is_none")]
396 pub bind: Option<BindV1>,
397 pub glue: GlueV1,
398 pub build: BuildV1,
399}
400
401#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
402#[serde(rename_all = "camelCase", deny_unknown_fields)]
403pub struct GuardV1 {
404 pub code: String,
406 pub expr: String,
407 #[serde(default, skip_serializing_if = "Option::is_none")]
408 pub message: Option<String>,
409}
410
411#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
412#[serde(tag = "kind", rename_all = "camelCase", deny_unknown_fields)]
413pub enum FeeBasisV1 {
414 None,
415 #[serde(rename_all = "camelCase")]
418 Input {
419 amount_arg: String,
420 asset: FeeAssetV1,
421 },
422 Fixed,
424}
425
426#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
427#[serde(tag = "kind", rename_all = "camelCase", deny_unknown_fields)]
428pub enum FeeAssetV1 {
429 Native,
430 #[serde(rename_all = "camelCase")]
431 Spl {
432 mint: String,
433 source_arg: String,
434 },
435}
436
437#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
438#[serde(rename_all = "camelCase", deny_unknown_fields)]
439pub struct TransitionV1 {
440 pub machine: MachineKind,
441 pub from: Vec<String>,
442 pub to: String,
443}
444
445#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
446#[serde(rename_all = "camelCase", deny_unknown_fields)]
447pub struct BindV1 {
448 pub accounts: BTreeMap<String, String>,
451 pub roles: BTreeMap<String, String>,
453}
454
455#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
456#[serde(rename_all = "camelCase", deny_unknown_fields)]
457pub struct GlueV1 {
458 pub atas: Vec<AtaGlueV1>,
460 #[serde(default, skip_serializing_if = "Option::is_none")]
462 pub wrap_sol: Option<WrapSolV1>,
463 pub unwrap_sol: bool,
465}
466
467#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
468#[serde(rename_all = "camelCase", deny_unknown_fields)]
469pub struct AtaGlueV1 {
470 pub owner: String,
471 pub mint: String,
472}
473
474#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
475#[serde(rename_all = "camelCase", deny_unknown_fields)]
476pub struct WrapSolV1 {
477 pub amount: String,
478}
479
480#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
481#[serde(tag = "kind", rename_all = "camelCase", deny_unknown_fields)]
482pub enum BuildV1 {
483 Template {
484 instructions: Vec<TemplateInstructionV1>,
485 },
486 Handler {
489 export: String,
490 programs: Vec<String>,
491 },
492}
493
494#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
495#[serde(rename_all = "camelCase", deny_unknown_fields)]
496pub struct TemplateInstructionV1 {
497 pub program: String,
498 pub instruction: String,
499 pub args: BTreeMap<String, String>,
500 pub accounts: BTreeMap<String, String>,
501 #[serde(default, skip_serializing_if = "Vec::is_empty")]
504 pub remaining_accounts: Vec<RemainingAccountV1>,
505}
506
507#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
510#[serde(rename_all = "camelCase", deny_unknown_fields)]
511pub struct RemainingAccountV1 {
512 pub pubkey: String,
513 pub is_writable: bool,
514 pub is_signer: bool,
515}
516
517#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
518#[serde(rename_all = "camelCase", deny_unknown_fields)]
519pub struct ReactionV1 {
520 pub id: String,
521 pub trigger: TriggerV1,
522 pub action: ActionV1,
523}
524
525#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
526#[serde(tag = "kind", rename_all = "camelCase", deny_unknown_fields)]
527pub enum TriggerV1 {
528 At {
530 machine: MachineKind,
531 #[serde(rename = "in")]
532 in_states: Vec<String>,
533 time: String,
534 },
535 When {
537 machine: MachineKind,
538 #[serde(rename = "in")]
539 in_states: Vec<String>,
540 condition: String,
541 },
542 OnTransition {
544 machine: MachineKind,
545 from: Vec<String>,
546 to: Vec<String>,
547 },
548 OnConfirmed { step: String },
550}
551
552#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
553#[serde(tag = "kind", rename_all = "camelCase", deny_unknown_fields)]
554pub enum ActionV1 {
555 RunKeeperStep { step: String },
556 ProposeSquads { step: String },
557 Emit { event: String },
558}
559
560#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
561#[serde(rename_all = "camelCase", deny_unknown_fields)]
562pub struct EventV1 {
563 pub machine: MachineKind,
564 #[serde(default, skip_serializing_if = "Option::is_none")]
565 pub description: Option<String>,
566}
567
568#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
569#[serde(rename_all = "camelCase", deny_unknown_fields)]
570pub struct MetadataV1 {
571 #[serde(default, skip_serializing_if = "Option::is_none")]
572 pub instance: Option<MetadataSchemaV1>,
573 #[serde(default, skip_serializing_if = "Option::is_none")]
574 pub participant: Option<MetadataSchemaV1>,
575}
576
577#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
578#[serde(rename_all = "camelCase", deny_unknown_fields)]
579pub struct MetadataSchemaV1 {
580 pub fields: BTreeMap<String, MetadataFieldV1>,
581 pub max_bytes: u32,
582}
583
584#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
585#[serde(rename_all = "camelCase", deny_unknown_fields)]
586pub struct MetadataFieldV1 {
587 pub schema: FieldSchemaV1,
588 pub writer: MetadataWriter,
589 pub filterable: bool,
590}
591
592#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
593#[serde(rename_all = "camelCase")]
594pub enum MetadataWriter {
595 Creator,
597 Participant,
599}
600
601#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
603#[serde(rename_all = "camelCase", deny_unknown_fields)]
604pub struct StackSurfaceV1 {
605 pub instance_accounts: BTreeMap<String, StackInstanceAccountV1>,
607 pub instance_fields: BTreeMap<String, StackInstanceFieldV1>,
609 pub activity: Vec<StackActivityV1>,
611}
612
613#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
614#[serde(rename_all = "camelCase", deny_unknown_fields)]
615pub struct StackInstanceAccountV1 {
616 pub program: String,
617 pub account: String,
618}
619
620#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
621#[serde(rename_all = "camelCase", deny_unknown_fields)]
622pub struct StackInstanceFieldV1 {
623 pub account: String,
625 pub field: String,
627}
628
629#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
630#[serde(rename_all = "camelCase", deny_unknown_fields)]
631pub struct StackActivityV1 {
632 pub step: String,
634 pub program: String,
635 pub instruction: String,
636 pub instance_account: String,
638 pub wallet_account: String,
640 #[serde(default, skip_serializing_if = "Option::is_none")]
642 pub amount_arg: Option<String>,
643}
644
645pub type ContractValue = Value;