soroban_rs/mock/
transaction.rs

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