1#![expect(
4 clippy::too_long_first_doc_paragraph,
5 reason = "Mysten-inherited docs sometimes have long first paragraphs."
6)]
7
8use std::str::FromStr;
9
10use eyre::eyre;
11use fastcrypto::encoding::decode_bytes_hex;
12use serde::{Deserialize, Serialize};
13use serde_repr::{Deserialize_repr, Serialize_repr};
14use sui_sdk_types::bcs::FromBcs;
15
16#[derive(Serialize_repr, Deserialize_repr, Copy, Clone, PartialEq, Eq, Debug, Hash)]
20#[repr(u8)]
21pub enum IntentVersion {
22 V0 = 0,
23}
24
25impl TryFrom<u8> for IntentVersion {
26 type Error = eyre::Report;
27 fn try_from(value: u8) -> Result<Self, Self::Error> {
28 FromBcs::from_bcs(&[value]).map_err(|_| eyre!("Invalid IntentVersion"))
29 }
30}
31
32#[derive(Serialize_repr, Deserialize_repr, Copy, Clone, PartialEq, Eq, Debug, Hash, Default)]
37#[repr(u8)]
38pub enum AppId {
39 #[default]
40 Sui = 0,
41 Narwhal = 1,
42 Consensus = 2,
43}
44
45impl TryFrom<u8> for AppId {
46 type Error = eyre::Report;
47 fn try_from(value: u8) -> Result<Self, Self::Error> {
48 FromBcs::from_bcs(&[value]).map_err(|_| eyre!("Invalid AppId"))
49 }
50}
51
52#[derive(Serialize_repr, Deserialize_repr, Copy, Clone, PartialEq, Eq, Debug, Hash)]
56#[repr(u8)]
57pub enum IntentScope {
58 TransactionData = 0, TransactionEffects = 1, CheckpointSummary = 2, PersonalMessage = 3, SenderSignedTransaction = 4, ProofOfPossession = 5, HeaderDigest = 6, BridgeEventUnused = 7, ConsensusBlock = 8, }
68
69impl TryFrom<u8> for IntentScope {
70 type Error = eyre::Report;
71 fn try_from(value: u8) -> Result<Self, Self::Error> {
72 FromBcs::from_bcs(&[value]).map_err(|_| eyre!("Invalid IntentScope"))
73 }
74}
75
76#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Hash)]
82pub struct Intent {
83 pub scope: IntentScope,
84 pub version: IntentVersion,
85 pub app_id: AppId,
86}
87
88impl FromStr for Intent {
89 type Err = eyre::Report;
90 fn from_str(s: &str) -> Result<Self, Self::Err> {
91 let s: Vec<u8> = decode_bytes_hex(s).map_err(|_| eyre!("Invalid Intent"))?;
92 if s.len() != 3 {
93 return Err(eyre!("Invalid Intent"));
94 }
95 Ok(Self {
96 scope: s[0].try_into()?,
97 version: s[1].try_into()?,
98 app_id: s[2].try_into()?,
99 })
100 }
101}
102
103impl Intent {
104 pub const fn sui_app(scope: IntentScope) -> Self {
105 Self {
106 version: IntentVersion::V0,
107 scope,
108 app_id: AppId::Sui,
109 }
110 }
111
112 pub const fn sui_transaction() -> Self {
113 Self {
114 scope: IntentScope::TransactionData,
115 version: IntentVersion::V0,
116 app_id: AppId::Sui,
117 }
118 }
119
120 pub const fn personal_message() -> Self {
121 Self {
122 scope: IntentScope::PersonalMessage,
123 version: IntentVersion::V0,
124 app_id: AppId::Sui,
125 }
126 }
127
128 pub const fn narwhal_app(scope: IntentScope) -> Self {
129 Self {
130 scope,
131 version: IntentVersion::V0,
132 app_id: AppId::Narwhal,
133 }
134 }
135
136 pub const fn consensus_app(scope: IntentScope) -> Self {
137 Self {
138 scope,
139 version: IntentVersion::V0,
140 app_id: AppId::Consensus,
141 }
142 }
143}
144
145#[derive(Debug, PartialEq, Eq, Serialize, Clone, Hash, Deserialize)]
154pub struct IntentMessage<T> {
155 pub intent: Intent,
156 pub value: T,
157}
158
159impl<T> IntentMessage<T> {
160 pub const fn new(intent: Intent, value: T) -> Self {
161 Self { intent, value }
162 }
163}