1use super::*;
4
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6#[serde(deny_unknown_fields)]
7pub struct PreparedEvmCall {
8 pub from_address: String,
9 pub to_address: String,
10 pub data: String,
11 #[serde(default, skip_serializing_if = "Option::is_none")]
12 pub gas_limit: Option<u64>,
13}
14
15#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
16#[serde(deny_unknown_fields)]
17pub struct PreparedErc20Approval {
18 pub owner_address: String,
19 pub token_address: String,
20 pub spender_address: String,
21 pub amount_minor_units: u128,
22 pub(super) call: PreparedEvmCall,
23}
24
25#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
26#[serde(deny_unknown_fields)]
27pub struct PreparedRootPublication {
28 pub(super) call: PreparedEvmCall,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
32#[serde(deny_unknown_fields)]
33pub struct EscrowDispatchRequest {
34 pub dispatch_id: String,
35 pub issued_at: u64,
36 pub trust_profile_id: String,
37 pub contract_package_id: String,
38 pub capability_id: String,
39 pub depositor_address: String,
40 pub beneficiary_address: String,
41 pub capital_instruction: SignedCapitalExecutionInstruction,
42 pub settlement_path: Web3SettlementPath,
43 pub oracle_evidence_required_for_fx: bool,
44 pub note: Option<String>,
45}
46
47#[derive(Debug, Clone, Serialize)]
48#[serde(deny_unknown_fields)]
49pub struct PreparedEscrowCreate {
50 pub expected_escrow_id: String,
51 pub capability_commitment: String,
52 pub settlement_amount_minor_units: u128,
53 pub dispatch: Web3SettlementDispatchArtifact,
54 pub(super) call: PreparedEvmCall,
55}
56
57impl PreparedEscrowCreate {
58 #[must_use]
59 pub fn commitment(&self) -> SettlementCommitment {
60 SettlementCommitment {
61 chain_id: self.dispatch.chain_id.clone(),
62 lane_kind: match self.dispatch.settlement_path {
63 Web3SettlementPath::DualSignature => "evm_dual_signature",
64 Web3SettlementPath::MerkleProof => "evm_merkle_proof",
65 }
66 .to_string(),
67 capability_commitment: self.capability_commitment.clone(),
68 receipt_reference: self.dispatch.escrow_id.clone(),
69 operator_identity: self.dispatch.beneficiary_address.clone(),
70 settlement_amount: self.dispatch.settlement_amount.clone(),
71 }
72 }
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
76#[serde(rename_all = "snake_case")]
77pub enum EscrowExecutionAmount {
78 Full,
79 Partial(MonetaryAmount),
80}
81
82#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
83#[serde(deny_unknown_fields)]
84pub struct PreparedMerkleRelease {
85 pub escrow_id: String,
86 pub chain_id: String,
87 pub receipt_hash: String,
88 pub receipt_leaf_hash: String,
89 pub merkle_root: String,
90 pub partial: bool,
91 pub settlement_amount_minor_units: u128,
92 pub observed_amount: MonetaryAmount,
93 pub(super) call: PreparedEvmCall,
94}
95
96impl PreparedMerkleRelease {
97 #[must_use]
98 pub fn commitment(
99 &self,
100 capability_commitment: String,
101 operator_identity: String,
102 ) -> SettlementCommitment {
103 SettlementCommitment {
104 chain_id: self.chain_id.clone(),
105 lane_kind: "evm_merkle_proof".to_string(),
106 capability_commitment,
107 receipt_reference: self.receipt_hash.clone(),
108 operator_identity,
109 settlement_amount: self.observed_amount.clone(),
110 }
111 }
112}
113
114#[derive(Debug, Clone)]
115pub struct PreparedAuthorizedChannelMerkleReleaseV1 {
116 pub(super) release: PreparedMerkleRelease,
117 pub(super) authorization: crate::channel::ChannelReleaseAuthorizationBindingV1,
118 pub(super) authorization_digest: String,
119}
120
121impl PreparedAuthorizedChannelMerkleReleaseV1 {
122 #[must_use]
123 pub const fn authorization(&self) -> &crate::channel::ChannelReleaseAuthorizationBindingV1 {
124 &self.authorization
125 }
126
127 #[must_use]
128 pub fn authorization_digest(&self) -> &str {
129 &self.authorization_digest
130 }
131
132 #[must_use]
133 pub fn publication_root(&self) -> &str {
134 &self.release.merkle_root
135 }
136
137 pub fn canonical_call(&self) -> Result<Vec<u8>, SettlementError> {
138 canonical_json_bytes(&self.release.call)
139 .map_err(|error| SettlementError::Serialization(error.to_string()))
140 }
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
144#[serde(deny_unknown_fields)]
145pub struct EvmSignature {
146 pub v: u8,
147 pub r: String,
148 pub s: String,
149}
150
151#[derive(Clone, PartialEq, Eq)]
152pub struct DualSignReleaseInput {
153 pub(super) operator_private_key_hex: zeroize::Zeroizing<String>,
154 pub observed_amount: MonetaryAmount,
155}
156
157impl DualSignReleaseInput {
158 #[must_use]
159 pub fn new(
160 operator_private_key_hex: impl Into<String>,
161 observed_amount: MonetaryAmount,
162 ) -> Self {
163 Self {
164 operator_private_key_hex: zeroize::Zeroizing::new(operator_private_key_hex.into()),
165 observed_amount,
166 }
167 }
168}
169
170impl std::fmt::Debug for DualSignReleaseInput {
171 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172 formatter
173 .debug_struct("DualSignReleaseInput")
174 .field("operator_private_key_hex", &"[REDACTED]")
175 .field("observed_amount", &self.observed_amount)
176 .finish()
177 }
178}
179
180#[cfg(test)]
181mod dual_sign_release_input_tests {
182 use super::*;
183
184 #[test]
185 fn debug_redacts_operator_private_key() {
186 let input = DualSignReleaseInput::new(
187 "private-key-material",
188 MonetaryAmount {
189 units: 1,
190 currency: "USD".to_string(),
191 },
192 );
193
194 let debug = format!("{input:?}");
195 assert!(debug.contains("[REDACTED]"));
196 assert!(!debug.contains("private-key-material"));
197 }
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
201#[serde(deny_unknown_fields)]
202pub struct DualSignRegistryEvidence {
203 pub chain_id: String,
204 pub identity_registry_contract: String,
205 pub operator_address: String,
206 pub block_number: u64,
207 pub block_hash: String,
208 pub observed_at: u64,
209 pub operator_key_hash: String,
210 pub settlement_key: String,
211 pub registered_at: u64,
212 pub operator_epoch: u64,
213 pub active: bool,
214}
215
216impl From<DualSignRegistryEvidence>
217 for chio_core::web3::settlement::Web3SettlementIdentityRegistryEvidence
218{
219 fn from(evidence: DualSignRegistryEvidence) -> Self {
220 Self {
221 chain_id: evidence.chain_id,
222 identity_registry_contract: evidence.identity_registry_contract,
223 operator_address: evidence.operator_address,
224 block_number: evidence.block_number,
225 block_hash: evidence.block_hash,
226 observed_at: evidence.observed_at,
227 operator_key_hash: evidence.operator_key_hash,
228 settlement_key: evidence.settlement_key,
229 registered_at: evidence.registered_at,
230 operator_epoch: evidence.operator_epoch,
231 active: evidence.active,
232 }
233 }
234}
235
236#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
237#[serde(deny_unknown_fields)]
238pub struct DualSignRegistryEvidenceBinding {
239 pub identity_registry_contract: String,
240 pub operator_address: String,
241 pub settlement_key: String,
242}
243
244impl From<DualSignRegistryEvidenceBinding>
245 for chio_core::web3::settlement::Web3SettlementIdentityRegistryEvidenceBinding
246{
247 fn from(binding: DualSignRegistryEvidenceBinding) -> Self {
248 Self {
249 identity_registry_contract: binding.identity_registry_contract,
250 operator_address: binding.operator_address,
251 settlement_key: binding.settlement_key,
252 }
253 }
254}
255
256#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
257#[serde(deny_unknown_fields)]
258pub struct PreparedDualSignRelease {
259 pub escrow_id: String,
260 pub chain_id: String,
261 pub receipt_hash: String,
262 pub digest: String,
263 pub operator_epoch: u64,
264 pub identity_registry_evidence: DualSignRegistryEvidence,
265 pub identity_registry_evidence_binding: DualSignRegistryEvidenceBinding,
266 pub settlement_amount_minor_units: u128,
267 pub observed_amount: MonetaryAmount,
268 pub signature: EvmSignature,
269 pub(super) call: PreparedEvmCall,
270}
271
272impl PreparedDualSignRelease {
273 #[must_use]
274 pub fn commitment(
275 &self,
276 capability_commitment: String,
277 operator_identity: String,
278 ) -> SettlementCommitment {
279 SettlementCommitment {
280 chain_id: self.chain_id.clone(),
281 lane_kind: "evm_dual_signature".to_string(),
282 capability_commitment,
283 receipt_reference: self.receipt_hash.clone(),
284 operator_identity,
285 settlement_amount: self.observed_amount.clone(),
286 }
287 }
288}
289
290#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
291#[serde(deny_unknown_fields)]
292pub struct PreparedEscrowRefund {
293 pub escrow_id: String,
294 pub chain_id: String,
295 pub(super) call: PreparedEvmCall,
296}
297
298#[derive(Debug, Clone, Serialize, Deserialize)]
299#[serde(deny_unknown_fields)]
300pub struct BondLockRequest {
301 pub principal_address: String,
302 pub bond: SignedCreditBond,
303}
304
305#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
306#[serde(deny_unknown_fields)]
307pub struct PreparedBondLock {
308 pub vault_id: String,
309 pub bond_id_hash: String,
310 pub facility_id_hash: String,
311 pub operator_key_hash: String,
312 pub collateral_minor_units: u128,
313 pub reserve_requirement_minor_units: u128,
314 pub(super) call: PreparedEvmCall,
315}
316
317#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
318#[serde(deny_unknown_fields)]
319pub struct PreparedBondRelease {
320 pub vault_id: String,
321 pub chain_id: String,
322 pub operator_key_hash: String,
323 pub evidence_hash: String,
324 pub merkle_root: String,
325 pub(super) call: PreparedEvmCall,
326}
327
328#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
329#[serde(deny_unknown_fields)]
330pub struct PreparedBondImpair {
331 pub vault_id: String,
332 pub chain_id: String,
333 pub operator_key_hash: String,
334 pub evidence_hash: String,
335 pub merkle_root: String,
336 pub slash_amount_minor_units: u128,
337 pub(super) call: PreparedEvmCall,
338}
339
340#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
341#[serde(deny_unknown_fields)]
342pub struct PreparedBondExpiry {
343 pub vault_id: String,
344 pub chain_id: String,
345 pub(super) call: PreparedEvmCall,
346}
347
348mod sealed {
349 use super::PreparedEvmCall;
350
351 pub trait Sealed {
352 fn call(&self) -> &PreparedEvmCall;
353 }
354}
355
356pub trait PreparedEvmSubmission: sealed::Sealed {}
357
358pub(super) fn prepared_submission_call<T: PreparedEvmSubmission + ?Sized>(
359 prepared: &T,
360) -> &PreparedEvmCall {
361 sealed::Sealed::call(prepared)
362}
363
364macro_rules! prepared_evm_call_access {
365 ($($type:ty),+ $(,)?) => {
366 $(
367 impl $type {
368 #[must_use]
369 pub const fn call(&self) -> &PreparedEvmCall {
370 &self.call
371 }
372 }
373
374 )+
375 };
376}
377
378macro_rules! prepared_evm_submission {
379 ($($type:ty),+ $(,)?) => {
380 $(
381
382 impl sealed::Sealed for $type {
383 fn call(&self) -> &PreparedEvmCall {
384 &self.call
385 }
386 }
387
388 impl PreparedEvmSubmission for $type {}
389 )+
390 };
391}
392
393prepared_evm_call_access!(
394 PreparedErc20Approval,
395 PreparedRootPublication,
396 PreparedEscrowCreate,
397 PreparedMerkleRelease,
398 PreparedDualSignRelease,
399 PreparedEscrowRefund,
400 PreparedBondLock,
401 PreparedBondRelease,
402 PreparedBondImpair,
403 PreparedBondExpiry,
404);
405
406prepared_evm_submission!(
407 PreparedErc20Approval,
408 PreparedEscrowCreate,
409 PreparedBondLock,
410);
411
412#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
413#[serde(deny_unknown_fields)]
414pub struct EvmLogEntry {
415 pub address: String,
416 pub topics: Vec<String>,
417 pub data: String,
418 #[serde(default, skip_serializing_if = "Option::is_none")]
419 pub log_index: Option<u64>,
420}
421
422#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
423#[serde(deny_unknown_fields)]
424pub struct EvmTransactionReceipt {
425 pub tx_hash: String,
426 pub block_number: u64,
427 pub block_hash: String,
428 pub status: bool,
429 pub from_address: String,
430 pub to_address: String,
431 pub gas_used: u64,
432 pub observed_at: u64,
433 #[serde(default, skip_serializing_if = "Vec::is_empty")]
434 pub logs: Vec<EvmLogEntry>,
435}
436
437#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
438#[serde(deny_unknown_fields)]
439pub struct EscrowSnapshot {
440 pub escrow_id: String,
441 pub depositor_address: String,
442 pub beneficiary_address: String,
443 pub deadline: u64,
444 pub deposited_minor_units: u128,
445 pub released_minor_units: u128,
446 pub refunded: bool,
447 pub remaining_minor_units: u128,
448}
449
450#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
451#[serde(deny_unknown_fields)]
452pub struct EvmBondSnapshot {
453 pub vault_id: String,
454 pub principal_address: String,
455 pub operator_key_hash: String,
456 pub expires_at: u64,
457 pub observed_at: u64,
458 pub locked_minor_units: u128,
459 pub reserve_requirement_minor_units: u128,
460 pub reserve_requirement_ratio_bps: u16,
461 pub slashed_minor_units: u128,
462 pub released: bool,
463 pub expired: bool,
464}
465
466#[derive(Debug, Deserialize)]
467pub(super) struct JsonRpcEnvelope {
468 #[serde(rename = "jsonrpc")]
469 pub(super) _jsonrpc: String,
470 #[serde(rename = "id")]
471 pub(super) _id: u64,
472 pub(super) result: Option<Value>,
473 pub(super) error: Option<JsonRpcError>,
474}
475
476#[derive(Debug, Deserialize)]
477pub(super) struct JsonRpcError {
478 pub(super) code: i64,
479 pub(super) message: String,
480 pub(super) data: Option<Value>,
481}