1use soroban_sdk::{contracttype, Address, Bytes, BytesN, Env, Symbol, Val};
2
3use super::types::GameAction;
4
5#[contracttype]
7#[derive(Clone, Debug, Eq, PartialEq)]
8#[repr(u32)]
9pub enum IntentSigner {
10 Direct = 0,
11 Session = 1,
12 Passkey = 2,
13}
14
15#[contracttype]
17#[derive(Clone, Debug, Eq, PartialEq)]
18pub struct SignerRef {
19 pub kind: IntentSigner,
20 pub session_key_id: BytesN<32>,
21 pub label: Symbol,
22}
23
24#[contracttype]
26#[derive(Clone, Debug, Eq, PartialEq)]
27#[repr(u32)]
28pub enum IntentProofKind {
29 None = 0,
30 Secp256r1 = 1,
31}
32
33#[contracttype]
35#[derive(Clone, Debug, Eq, PartialEq)]
36pub struct IntentProof {
37 pub kind: IntentProofKind,
38 pub signature: BytesN<64>,
39}
40
41#[contracttype]
43#[derive(Clone, Debug, Eq, PartialEq)]
44pub struct SignedIntent {
45 pub account: Address,
46 pub signer: SignerRef,
47 pub action: GameAction,
48 pub nonce: u64,
49 pub expires_at: u64,
50 pub action_hash: BytesN<32>,
51 pub proof: IntentProof,
52}
53
54#[contracttype]
56#[derive(Clone, Debug, Eq, PartialEq)]
57#[repr(u32)]
58pub enum AuthMethod {
59 Direct = 0,
60 Session = 1,
61 Passkey = 2,
62}
63
64#[contracttype]
66#[derive(Clone, Debug, Eq, PartialEq)]
67pub struct AuthResult {
68 pub method: AuthMethod,
69 pub nonce_consumed: u64,
70 pub session_key_id: BytesN<32>,
71 pub remaining_operations: u32,
72}
73
74impl SignerRef {
75 pub fn direct(env: &Env) -> Self {
76 Self {
77 kind: IntentSigner::Direct,
78 session_key_id: BytesN::from_array(env, &[0u8; 32]),
79 label: Symbol::new(env, ""),
80 }
81 }
82
83 pub fn session(env: &Env, key_id: &BytesN<32>) -> Self {
84 Self {
85 kind: IntentSigner::Session,
86 session_key_id: key_id.clone(),
87 label: Symbol::new(env, ""),
88 }
89 }
90
91 pub fn passkey(env: &Env, label: Symbol) -> Self {
92 Self {
93 kind: IntentSigner::Passkey,
94 session_key_id: BytesN::from_array(env, &[0u8; 32]),
95 label,
96 }
97 }
98}
99
100impl IntentProof {
101 pub fn none(env: &Env) -> Self {
102 Self {
103 kind: IntentProofKind::None,
104 signature: BytesN::from_array(env, &[0u8; 64]),
105 }
106 }
107
108 pub fn secp256r1(signature: BytesN<64>) -> Self {
109 Self {
110 kind: IntentProofKind::Secp256r1,
111 signature,
112 }
113 }
114}
115
116impl SignedIntent {
117 pub fn direct(
118 env: &Env,
119 account: Address,
120 action: GameAction,
121 nonce: u64,
122 expires_at: u64,
123 ) -> Self {
124 let signer = SignerRef::direct(env);
125 let action_hash = hash_intent(env, &signer, &action, nonce, expires_at);
126 Self {
127 account,
128 signer,
129 action,
130 nonce,
131 expires_at,
132 action_hash,
133 proof: IntentProof::none(env),
134 }
135 }
136
137 pub fn session(
138 env: &Env,
139 account: Address,
140 key_id: &BytesN<32>,
141 action: GameAction,
142 nonce: u64,
143 expires_at: u64,
144 ) -> Self {
145 let signer = SignerRef::session(env, key_id);
146 let action_hash = hash_intent(env, &signer, &action, nonce, expires_at);
147 Self {
148 account,
149 signer,
150 action,
151 nonce,
152 expires_at,
153 action_hash,
154 proof: IntentProof::none(env),
155 }
156 }
157
158 pub fn passkey(
159 env: &Env,
160 account: Address,
161 label: Symbol,
162 action: GameAction,
163 nonce: u64,
164 expires_at: u64,
165 signature: BytesN<64>,
166 ) -> Self {
167 let signer = SignerRef::passkey(env, label);
168 let action_hash = hash_intent(env, &signer, &action, nonce, expires_at);
169 Self {
170 account,
171 signer,
172 action,
173 nonce,
174 expires_at,
175 action_hash,
176 proof: IntentProof::secp256r1(signature),
177 }
178 }
179
180 pub fn recompute_hash(&self, env: &Env) -> BytesN<32> {
181 hash_intent(env, &self.signer, &self.action, self.nonce, self.expires_at)
182 }
183}
184
185pub fn hash_intent(
186 env: &Env,
187 signer: &SignerRef,
188 action: &GameAction,
189 nonce: u64,
190 expires_at: u64,
191) -> BytesN<32> {
192 let mut bytes = Bytes::new(env);
193 bytes.append(&Bytes::from_slice(env, &nonce.to_be_bytes()));
194 bytes.append(&Bytes::from_slice(env, &expires_at.to_be_bytes()));
195 bytes.append(&Bytes::from_slice(
196 env,
197 &(signer.kind.clone() as u32).to_be_bytes(),
198 ));
199 bytes.append(&Bytes::from_slice(env, &signer.session_key_id.to_array()));
200 let label_bits: Val = signer.label.to_val();
201 bytes.append(&Bytes::from_slice(
202 env,
203 &label_bits.get_payload().to_be_bytes(),
204 ));
205 let action_bits: Val = action.system_name.to_val();
206 bytes.append(&Bytes::from_slice(
207 env,
208 &action_bits.get_payload().to_be_bytes(),
209 ));
210 bytes.append(&action.data);
211 BytesN::from_array(env, &env.crypto().sha256(&bytes).to_array())
212}