simperby_settlement/
execution.rs1use super::*;
2use simperby_core::*;
3
4#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
5pub struct Execution {
6 pub target_chain: String,
8 pub contract_sequence: u128,
10 pub message: ExecutionMessage,
12}
13
14#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
15pub enum ExecutionMessage {
16 Dummy { msg: String },
18 TransferFungibleToken(TransferFungibleToken),
20 TransferNonFungibleToken(TransferNonFungibleToken),
22}
23
24#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
25pub struct TransferFungibleToken {
26 pub token_address: HexSerializedVec,
27 pub amount: Decimal,
28 pub receiver_address: HexSerializedVec,
29}
30
31#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
32pub struct TransferNonFungibleToken {
33 pub collection_address: HexSerializedVec,
34 pub token_index: HexSerializedVec,
35 pub receiver_address: HexSerializedVec,
36}
37
38pub fn create_execution_transaction(
40 execution: &Execution,
41 author: MemberName,
42 timestamp: Timestamp,
43) -> Result<Transaction, String> {
44 let head = match &execution.message {
45 ExecutionMessage::Dummy { .. } => format!("ex-dummy: {}", execution.target_chain),
46 ExecutionMessage::TransferFungibleToken(_) => {
47 format!("ex-transfer-ft: {}", execution.target_chain)
48 }
49 ExecutionMessage::TransferNonFungibleToken(_) => {
50 format!("ex-transfer-nft: {}", execution.target_chain)
51 }
52 };
53 let body = format!(
54 "{}\n---\n{}",
55 serde_spb::to_string(&execution).unwrap(),
56 Hash256::hash(serde_spb::to_vec(&execution).unwrap())
57 );
58 Ok(Transaction {
59 author,
60 timestamp,
61 head,
62 body,
63 diff: Diff::None,
64 })
65}
66
67pub fn convert_transaction_to_execution(transaction: &Transaction) -> Result<Execution, String> {
69 let segments = transaction.body.split("\n---\n").collect::<Vec<_>>();
70 if segments.len() != 2 {
71 return Err(format!(
72 "Invalid body: expected 2 segments, got {}",
73 segments.len()
74 ));
75 }
76 let execution: Execution = serde_spb::from_str(segments[0]).map_err(|e| e.to_string())?;
77 let hash = Hash256::hash(serde_spb::to_vec(&execution).unwrap());
78 if format!("{hash}") != segments[1] {
79 return Err(format!(
80 "Invalid body: expected hash {hash}, got {}",
81 segments[1]
82 ));
83 }
84
85 if !transaction.head.starts_with("ex-") {
86 return Err("Invalid head".to_string());
87 }
88 let execution_message =
89 transaction.head.split(": ").next().ok_or("Invalid head")?[3..].to_owned();
90 let target_chain = transaction.head.split(": ").nth(1).ok_or("Invalid head")?;
91 if execution.target_chain != target_chain {
92 return Err("Invalid target chain".to_string());
93 }
94 match execution_message.as_str() {
95 "dummy" => {
96 if !matches!(execution.message, ExecutionMessage::Dummy { .. }) {
97 return Err("Invalid message".to_string());
98 }
99 }
100 "transfer-ft" => {
101 if !matches!(
102 execution.message,
103 ExecutionMessage::TransferFungibleToken { .. }
104 ) {
105 return Err("Invalid message".to_string());
106 }
107 }
108 "transfer-nft" => {
109 if !matches!(
110 execution.message,
111 ExecutionMessage::TransferNonFungibleToken { .. }
112 ) {
113 return Err("Invalid message".to_string());
114 }
115 }
116 _ => return Err("Invalid message".to_string()),
117 }
118 Ok(execution)
119}