soroban_rs/mock/
transaction.rs

1use std::convert::TryInto;
2use stellar_rpc_client::{GetTransactionResponse, SimulateTransactionResponse};
3use stellar_xdr::curr::{
4    AccountEntry, AccountId, ExtensionPoint, Hash, LedgerEntry, LedgerEntryChange, LedgerEntryData,
5    LedgerEntryExt, Memo, MuxedAccount, Operation, OperationBody, OperationMeta, OperationResult,
6    Preconditions, ScAddress, ScVal, SequenceNumber, SetOptionsOp, SorobanTransactionMeta,
7    SorobanTransactionMetaExt, Transaction, TransactionEnvelope, TransactionExt, TransactionMeta,
8    TransactionMetaV3, TransactionResult, TransactionResultExt, TransactionResultResult,
9    TransactionV1Envelope, Uint256, VecM,
10};
11
12pub struct MockTransactionResult {
13    pub success: bool,
14}
15
16pub struct MockTransactionMeta {
17    pub return_value: Option<ScVal>,
18    pub account_entry: Option<AccountEntry>,
19}
20
21pub struct MockGetTransactionResponse {
22    pub tx_result: Option<MockTransactionResult>,
23    pub tx_meta: Option<MockTransactionMeta>,
24    pub tx_envelope: Option<TransactionEnvelope>,
25}
26
27enum MockResponseType {
28    Basic,
29    WithReturnValue(ScVal),
30    WithAccountEntry(AccountEntry),
31}
32
33#[allow(dead_code)]
34pub fn mock_transaction(account_id: AccountId) -> Transaction {
35    Transaction {
36        fee: 100,
37        seq_num: SequenceNumber::from(1),
38        source_account: account_id.into(),
39        cond: Preconditions::None,
40        memo: Memo::None,
41        operations: VecM::default(),
42        ext: TransactionExt::V0,
43    }
44}
45
46#[allow(dead_code)]
47pub fn mock_transaction_envelope(account_id: AccountId) -> TransactionEnvelope {
48    TransactionEnvelope::Tx(TransactionV1Envelope {
49        tx: mock_transaction(account_id),
50        signatures: Default::default(),
51    })
52}
53
54#[allow(dead_code)]
55pub fn create_contract_id_val() -> ScVal {
56    let contract_hash = Hash([1; 32]);
57    ScVal::Address(ScAddress::Contract(contract_hash))
58}
59
60#[allow(dead_code)]
61pub fn create_mock_set_options_tx_envelope() -> TransactionEnvelope {
62    // Create a mock account source
63    let source_account = MuxedAccount::Ed25519(Uint256([0; 32]));
64
65    // Create a SetOptions operation
66    let set_options_op = Operation {
67        source_account: None,
68        body: OperationBody::SetOptions(SetOptionsOp {
69            inflation_dest: None,
70            clear_flags: None,
71            set_flags: None,
72            master_weight: None,
73            low_threshold: None,
74            med_threshold: None,
75            high_threshold: None,
76            home_domain: None,
77            signer: None,
78        }),
79    };
80
81    // Create the operations vector
82    let operations = vec![set_options_op].try_into().unwrap_or_default();
83
84    // Create and return the transaction envelope
85    TransactionEnvelope::Tx(TransactionV1Envelope {
86        tx: Transaction {
87            source_account,
88            fee: 100,
89            seq_num: 1.into(),
90            cond: Preconditions::None,
91            memo: Memo::None,
92            operations,
93            ext: TransactionExt::V0,
94        },
95        signatures: Default::default(),
96    })
97}
98
99#[allow(dead_code)]
100pub fn mock_simulate_tx_response(min_resource_fee: Option<u64>) -> SimulateTransactionResponse {
101    SimulateTransactionResponse {
102        min_resource_fee: min_resource_fee.unwrap_or(100),
103        transaction_data: "test".to_string(),
104        ..Default::default()
105    }
106}
107
108#[allow(dead_code)]
109fn mock_transaction_response_impl(response_type: MockResponseType) -> GetTransactionResponse {
110    let mut response = GetTransactionResponse {
111        status: "SUCCESS".to_string(),
112        envelope: None,
113        result: Some(create_success_tx_result()),
114        result_meta: None,
115    };
116
117    match response_type {
118        MockResponseType::Basic => {}
119        MockResponseType::WithReturnValue(val) => {
120            response.result_meta = Some(create_soroban_tx_meta_with_return_value(val));
121        }
122        MockResponseType::WithAccountEntry(account) => {
123            let ledger_entry = LedgerEntry {
124                last_modified_ledger_seq: 1,
125                data: LedgerEntryData::Account(account),
126                ext: LedgerEntryExt::V0,
127            };
128
129            let change = LedgerEntryChange::Updated(ledger_entry);
130            let changes = VecM::try_from(vec![change]).unwrap_or_default();
131            let op_meta = OperationMeta {
132                changes: stellar_xdr::curr::LedgerEntryChanges(changes),
133            };
134
135            let operations = VecM::try_from(vec![op_meta]).unwrap_or_default();
136            let meta = TransactionMeta::V3(TransactionMetaV3 {
137                ext: ExtensionPoint::V0,
138                soroban_meta: None,
139                tx_changes_before: Default::default(),
140                tx_changes_after: Default::default(),
141                operations,
142            });
143
144            response.result_meta = Some(meta);
145        }
146    }
147
148    response
149}
150
151#[allow(dead_code)]
152pub fn mock_transaction_response() -> GetTransactionResponse {
153    mock_transaction_response_impl(MockResponseType::Basic)
154}
155
156#[allow(dead_code)]
157pub fn mock_transaction_response_with_return_value(return_val: ScVal) -> GetTransactionResponse {
158    mock_transaction_response_impl(MockResponseType::WithReturnValue(return_val))
159}
160
161#[allow(dead_code)]
162pub fn mock_transaction_response_with_account_entry(
163    account: AccountEntry,
164) -> GetTransactionResponse {
165    mock_transaction_response_impl(MockResponseType::WithAccountEntry(account))
166}
167
168#[allow(dead_code)]
169fn create_success_tx_result() -> TransactionResult {
170    // Create empty operation results
171    let empty_vec: Vec<OperationResult> = Vec::new();
172    let op_results = empty_vec.try_into().unwrap_or_default();
173
174    TransactionResult {
175        fee_charged: 100,
176        result: TransactionResultResult::TxSuccess(op_results),
177        ext: TransactionResultExt::V0,
178    }
179}
180
181#[allow(dead_code)]
182fn create_soroban_tx_meta_with_return_value(return_val: ScVal) -> TransactionMeta {
183    TransactionMeta::V3(TransactionMetaV3 {
184        ext: ExtensionPoint::V0,
185        soroban_meta: Some(SorobanTransactionMeta {
186            ext: SorobanTransactionMetaExt::V0,
187            events: Default::default(),
188            return_value: return_val,
189            diagnostic_events: Default::default(),
190        }),
191        tx_changes_before: Default::default(),
192        tx_changes_after: Default::default(),
193        operations: Default::default(),
194    })
195}
196
197#[allow(dead_code)]
198fn create_tx_meta_from_mock(mock: &MockTransactionMeta) -> TransactionMeta {
199    // Check if we have a return value
200    if let Some(return_val) = &mock.return_value {
201        return create_soroban_tx_meta_with_return_value(return_val.clone());
202    }
203
204    // Check if we have an account entry
205    if let Some(account) = &mock.account_entry {
206        // Create a ledger entry for the account
207        let ledger_entry = LedgerEntry {
208            last_modified_ledger_seq: 1,
209            data: LedgerEntryData::Account(account.clone()),
210            ext: LedgerEntryExt::V0,
211        };
212
213        let change = LedgerEntryChange::Updated(ledger_entry);
214        let changes = VecM::try_from(vec![change]).unwrap_or_default();
215        let op_meta = OperationMeta {
216            changes: stellar_xdr::curr::LedgerEntryChanges(changes),
217        };
218
219        let operations = VecM::try_from(vec![op_meta]).unwrap_or_default();
220
221        return TransactionMeta::V3(TransactionMetaV3 {
222            ext: ExtensionPoint::V0,
223            soroban_meta: None,
224            tx_changes_before: Default::default(),
225            tx_changes_after: Default::default(),
226            operations,
227        });
228    }
229
230    // Default empty meta if neither return value nor account entry is present
231    TransactionMeta::V3(TransactionMetaV3 {
232        ext: ExtensionPoint::V0,
233        soroban_meta: None,
234        tx_changes_before: Default::default(),
235        tx_changes_after: Default::default(),
236        operations: Default::default(),
237    })
238}