use std::time::Duration;
use std::vec;

use crate::error::*;
use crate::server::*;
use crate::soroban_rpc::GetHealthResponse;
use crate::soroban_rpc::GetLatestLedgerResponse;
use crate::soroban_rpc::GetNetworkResponse;
use crate::soroban_rpc::SendTransactionStatus;
use crate::soroban_rpc::StateChangeKind;
use crate::soroban_rpc::TransactionStatus;
use crate::EventFilter;
use crate::Topic;
use base64::Engine;
use serde_json::json;
use stellar_baselib::account::Account;
use stellar_baselib::account::AccountBehavior;
use stellar_baselib::address::Address;
use stellar_baselib::address::AddressTrait;
use stellar_baselib::contract::ContractBehavior;
use stellar_baselib::contract::Contracts;
use stellar_baselib::hashing;
use stellar_baselib::hashing::HashingBehavior;
use stellar_baselib::keypair::Keypair;
use stellar_baselib::keypair::KeypairBehavior;
use stellar_baselib::network::NetworkPassphrase;
use stellar_baselib::network::Networks;
use stellar_baselib::transaction::Transaction;
use stellar_baselib::transaction::TransactionBehavior;
use stellar_baselib::transaction_builder::TransactionBuilder;
use stellar_baselib::transaction_builder::TransactionBuilderBehavior;
use stellar_baselib::xdr::int128_helpers::i128_from_pieces;
use stellar_baselib::xdr::ContractDataEntry;
use stellar_baselib::xdr::ContractEvent;
use stellar_baselib::xdr::ContractEventBody;
use stellar_baselib::xdr::ContractEventType;
use stellar_baselib::xdr::ContractEventV0;
use stellar_baselib::xdr::ContractId;
use stellar_baselib::xdr::ExtensionPoint;
use stellar_baselib::xdr::GeneralizedTransactionSet;
use stellar_baselib::xdr::Hash;
use stellar_baselib::xdr::Int128Parts;
use stellar_baselib::xdr::InvokeHostFunctionResult;
use stellar_baselib::xdr::LedgerCloseMeta;
use stellar_baselib::xdr::LedgerCloseMetaV1;
use stellar_baselib::xdr::LedgerEntryData;
use stellar_baselib::xdr::LedgerHeaderHistoryEntry;
use stellar_baselib::xdr::LedgerKey;
use stellar_baselib::xdr::LedgerKeyAccount;
use stellar_baselib::xdr::LedgerKeyContractData;
use stellar_baselib::xdr::Limits;
use stellar_baselib::xdr::OperationResult;
use stellar_baselib::xdr::OperationResultTr;
use stellar_baselib::xdr::ScString;
use stellar_baselib::xdr::ScSymbol;
use stellar_baselib::xdr::ScVal;
use stellar_baselib::xdr::ScVec;
use stellar_baselib::xdr::SorobanResources;
use stellar_baselib::xdr::SorobanTransactionData;
use stellar_baselib::xdr::TimeBounds;
use stellar_baselib::xdr::TimePoint;
use stellar_baselib::xdr::TransactionEvent;
use stellar_baselib::xdr::TransactionEventStage;

use stellar_baselib::xdr::TransactionResult;
use stellar_baselib::xdr::TransactionResultResult;
use stellar_baselib::xdr::TransactionSetV1;
use stellar_baselib::xdr::TransactionV1Envelope;
use stellar_baselib::xdr::TtlEntry;
use stellar_baselib::xdr::WriteXdr;
use wiremock::matchers;
use wiremock::matchers::method;
use wiremock::matchers::path;
use wiremock::matchers::query_param;
use wiremock::Mock;
use wiremock::MockServer;
use wiremock::ResponseTemplate;

#[test]
fn server_new() {
    let s1 = Server::new("https://rpc", Default::default());
    assert!(s1.is_ok(), "https scheme with allow_http None");

    let s2 = Server::new("file://rpc", Default::default());
    assert!(matches!(
        s2.err(),
        Some(Error::InvalidRpc(InvalidRpcUrl::NotHttpScheme)),
    ));

    let s3 = Server::new(
        "scheme://rpc",
        Options {
            allow_http: true,
            ..Default::default()
        },
    );
    assert!(matches!(
        s3.err(),
        Some(Error::InvalidRpc(InvalidRpcUrl::NotHttpScheme)),
    ));

    let s4 = Server::new(
        "http://rpc",
        Options {
            allow_http: true,
            ..Default::default()
        },
    );
    assert!(s4.is_ok(), "http scheme with allow_http true");

    let s5 = Server::new(
        "",
        Options {
            allow_http: true,
            ..Default::default()
        },
    );
    assert!(matches!(
        s5.err(),
        Some(Error::InvalidRpc(InvalidRpcUrl::InvalidUri)),
    ));

    let s6 = Server::new(
        "http://rpc",
        Options {
            allow_http: false,
            ..Default::default()
        },
    );
    assert!(matches!(
        s6.err(),
        Some(Error::InvalidRpc(InvalidRpcUrl::UnsecureHttpNotAllowed)),
    ));
}

#[tokio::test]
async fn get_health() {
    let request = json!({"method": "getHealth"});
    let response = json!(
    {
      "jsonrpc": "2.0",
      "id": 8675309,
      "result": {
        "status": "healthy",
        "latestLedger": 51583040,
        "oldestLedger": 51565760,
        "ledgerRetentionWindow": 17281
      }
    });
    let (s, _m) = get_mocked_server(request, response).await;
    let result = s.get_health().await.expect("Should not fail");

    let expect = GetHealthResponse {
        status: "healthy".to_string(),
        latest_ledger: 51583040,
        oldest_ledger: 51565760,
        ledger_retention_window: 17281,
    };

    assert_eq!(result, expect);
}

#[tokio::test]
async fn get_latest_ledger() {
    let request = json!({"method": "getLatestLedger"});
    let response = json!(
        {
            "jsonrpc": "2.0",
            "id": 8675309,
            "result": {
            "id": "c73c5eac58a441d4eb733c35253ae85f783e018f7be5ef974258fed067aabb36",
            "protocolVersion": 20,
            "sequence": 2539605
        }
    }
    );

    let (s, _m) = get_mocked_server(request, response).await;
    let result = s.get_latest_ledger().await.expect("Should not fail");
    let expect = GetLatestLedgerResponse {
        id: "c73c5eac58a441d4eb733c35253ae85f783e018f7be5ef974258fed067aabb36".into(),
        sequence: 2539605,
        protocol_version: 20,
    };
    assert_eq!(result, expect);
}

#[tokio::test]
async fn get_network() {
    let request = json!({"method": "getNetwork"});
    let response = json!(
        {
            "jsonrpc": "2.0",
            "id": 8675309,
            "result": {
            "friendbotUrl": "https://friendbot-testnet.stellar.org/",
            "passphrase": "Test SDF Network ; September 2015",
            "protocolVersion": 20
        }
    }
    );

    let (s, _m) = get_mocked_server(request, response).await;
    let result = s.get_network().await.expect("Should not fail");
    let expect = GetNetworkResponse {
        friendbot_url: Some("https://friendbot-testnet.stellar.org/".into()),
        passphrase: Some("Test SDF Network ; September 2015".into()),
        protocol_version: Some(20),
    };
    assert_eq!(result, expect);
}

#[tokio::test]
async fn get_account() {
    let address = "GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI";
    let account_id = Keypair::from_public_key(address)
        .expect("Should not fail")
        .xdr_account_id();
    let key = LedgerKey::Account(LedgerKeyAccount { account_id });
    let account_entry = "AAAAAAAAAABzdv3ojkzWHMD7KUoXhrPx0GH18vHKV0ZfqpMiEblG1gAAAFwVZH3YAAABdgAAAQgAAAAFAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAOZYQAAAAAaJsIJQ==";

    let value = base64::prelude::BASE64_STANDARD.encode(key.to_xdr(Limits::none()).unwrap());
    let request = json!(
        {
            "jsonrpc": "2.0",
            "id": 1,
            "method": "getLedgerEntries",
            "params": { "keys": [value] },
    }
    );
    let response = json!(
        {
            "jsonrpc": "2.0",
            "id": 1,
            "result": {
            "entries": [
            {
                "key": value,
                "xdr": account_entry,
                "lastModifiedLedgerSeq": 2552504
            }
        ],
            "latestLedger": 2552990
        }
    }
    );

    let (s, _m) = get_mocked_server(request, response).await;
    let result = s.get_account(address).await.expect("Should not fail");
    assert_eq!(result.sequence_number(), "1606317768968");
    assert_eq!(result.account_id(), address);
}

#[tokio::test]
async fn get_account_not_found() {
    let address = "GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI";
    let account_id = Keypair::from_public_key(address)
        .expect("Should not fail")
        .xdr_account_id();
    let key = LedgerKey::Account(LedgerKeyAccount { account_id });

    let value = base64::prelude::BASE64_STANDARD.encode(key.to_xdr(Limits::none()).unwrap());
    let request = json!(
        {
            "jsonrpc": "2.0",
            "id": 1,
            "method": "getLedgerEntries",
            "params": { "keys": [value] },
    }
    );
    let response = json!(
        {
            "jsonrpc": "2.0",
            "id": 1,
            "result": {
            "entries": null,
            "latestLedger": 2552990
        }
    }
    );

    let (s, _m) = get_mocked_server(request, response).await;
    let result = s.get_account(address).await;
    assert!(matches!(result, Err(Error::AccountNotFound)));
}

#[tokio::test]
async fn get_ledger_entries() {
    let address = "CCJZ5DGASBWQXR5MPFCJXMBI333XE5U3FSJTNQU7RIKE3P5GN2K2WYD5";
    let key = ScVal::Vec(Some(ScVec(
        [ScVal::Symbol("test".try_into().unwrap())]
            .try_into()
            .unwrap(),
    )));
    let contract = Address::new(address).unwrap().to_sc_address().unwrap();
    let durability = stellar_baselib::xdr::ContractDataDurability::Persistent;
    let ledger_entry = LedgerEntryData::ContractData(ContractDataEntry {
        ext: ExtensionPoint::V0,
        contract: contract.clone(),
        durability,
        key: key.clone(),
        val: key.clone(),
    });
    let ledger_key = LedgerKey::ContractData(LedgerKeyContractData {
        contract,
        key: key.clone(),
        durability,
    });
    let h = hashing::Sha256Hasher::hash(ledger_key.to_xdr(Limits::none()).unwrap());
    let ledger_ttl_entry = TtlEntry {
        key_hash: Hash(h),
        live_until_ledger_seq: 1000,
    };
    let ledger_key_xdr = ledger_key.to_xdr_base64(Limits::none()).unwrap();
    let ledger_entry_xdr = ledger_entry.to_xdr_base64(Limits::none()).unwrap();

    /*
     * ledger entry found, includes ttl meta in response
     */
    {
        let request = json!(
            {
                "jsonrpc": "2.0",
                "id": 1,
                "method": "getLedgerEntries",
                "params": { "keys": [ledger_key_xdr] },
        }
        );
        let response = json!(
            {
                "jsonrpc": "2.0",
                "id": 1,
                "result": {
                "entries": [
            {
              "liveUntilLedgerSeq": ledger_ttl_entry.live_until_ledger_seq,
              "lastModifiedLedgerSeq": 2,
              "key": ledger_key_xdr,
              "xdr": ledger_entry_xdr,
            },
          ],
                "latestLedger": 2552990
            }
        }
        );

        let (s, _m) = get_mocked_server(request, response).await;
        let result = s
            .get_ledger_entries(vec![ledger_key.clone()])
            .await
            .expect("Should not fail");
        if let Some(entries) = result.entries {
            assert_eq!(entries.len(), 1);
            let e = &entries[0];
            assert_eq!(e.last_modified_ledger_seq, Some(2));
            assert_eq!(e.to_key(), ledger_key);
            assert_eq!(e.to_data(), ledger_entry);
            assert_eq!(
                e.live_until_ledger_seq,
                Some(ledger_ttl_entry.live_until_ledger_seq)
            );
        } else {
            panic!("No entry found");
        }
    }

    /*
     * ledger entry found, no ttl in response
     */
    {
        let request = json!(
            {
                "jsonrpc": "2.0",
                "id": 1,
                "method": "getLedgerEntries",
                "params": { "keys": [ledger_key_xdr] },
        }
        );
        let response = json!(
            {
                "jsonrpc": "2.0",
                "id": 1,
                "result": {
                "entries": [
            {
              "lastModifiedLedgerSeq": 2,
              "key": ledger_key_xdr,
              "xdr": ledger_entry_xdr,
            },
          ],
                "latestLedger": 2552990
            }
        }
        );

        let (s, _m) = get_mocked_server(request, response).await;
        let result = s
            .get_ledger_entries(vec![ledger_key.clone()])
            .await
            .expect("Should not fail");
        if let Some(entries) = result.entries {
            assert_eq!(entries.len(), 1);
            let e = &entries[0];
            assert_eq!(e.last_modified_ledger_seq, Some(2));
            assert_eq!(e.to_key(), ledger_key);
            assert_eq!(e.to_data(), ledger_entry);
            assert_eq!(e.live_until_ledger_seq, None);
        } else {
            panic!("No entry found");
        }
        //
    }

    /*
     * throws when invalid rpc response
     */
    {
        let request = json!(
            {
                "jsonrpc": "2.0",
                "id": 1,
                "method": "getLedgerEntries",
                "params": { "keys": [ledger_key_xdr] },
        }
        );
        let response = json!(
        {
        "jsonrpc": "2.0",
        "id": 1,
        "result": {
        "entries": [
            {
                 "lastModifiedLedgerSeq": 2,
            },
            {
                "lastModifiedLedgerSeq": 1,
            },
        ],
        "latestLedger": 2552990
        }
        }
        );

        let (s, _m) = get_mocked_server(request, response).await;
        let result = s.get_ledger_entries(vec![ledger_key.clone()]).await;

        // TODO better error should be used
        assert!(matches!(result, Err(Error::JsonError(_))));
    }
}

#[tokio::test]
async fn get_contract_data() {
    let address = "CCJZ5DGASBWQXR5MPFCJXMBI333XE5U3FSJTNQU7RIKE3P5GN2K2WYD5";
    let key = ScVal::Vec(Some(ScVec(
        [ScVal::Symbol("Admin".try_into().unwrap())]
            .try_into()
            .unwrap(),
    )));
    let contract = Address::new(address).unwrap().to_sc_address().unwrap();
    let durability = stellar_baselib::xdr::ContractDataDurability::Persistent;
    let ledger_entry = LedgerEntryData::ContractData(ContractDataEntry {
        ext: ExtensionPoint::V0,
        contract: contract.clone(),
        durability,
        key: key.clone(),
        val: key.clone(),
    });
    let ledger_key = LedgerKey::ContractData(LedgerKeyContractData {
        contract,
        key: key.clone(),
        durability,
    });
    let h = hashing::Sha256Hasher::hash(ledger_key.to_xdr(Limits::none()).unwrap());
    let ledger_ttl_entry = TtlEntry {
        key_hash: Hash(h),
        live_until_ledger_seq: 1000,
    };
    let ledger_key_xdr = ledger_key.to_xdr_base64(Limits::none()).unwrap();
    let ledger_entry_xdr = ledger_entry.to_xdr_base64(Limits::none()).unwrap();

    /*
     * contract data found
     */
    {
        let request = json!(
            {
                "jsonrpc": "2.0",
                "id": 1,
                "method": "getLedgerEntries",
                "params": { "keys": [ledger_key_xdr] },
        }
        );
        let response = json!(
            {
                "jsonrpc": "2.0",
                "id": 1,
                "result": {
                "entries": [
                {
                    "liveUntilLedgerSeq": ledger_ttl_entry.live_until_ledger_seq,
                    "lastModifiedLedgerSeq": 2,
                    "key": ledger_key_xdr,
                    "xdr": ledger_entry_xdr,
                },
            ],
            "latestLedger": 2552990
        }
        }
        );

        let (s, _m) = get_mocked_server(request, response).await;
        let result = s
            .get_contract_data(address, key.clone(), Durability::Persistent)
            .await
            .expect("Should not fail");

        assert_eq!(result.to_key(), ledger_key);
        assert_eq!(result.to_data(), ledger_entry);
        assert_eq!(
            result.live_until_ledger_seq,
            Some(ledger_ttl_entry.live_until_ledger_seq)
        );
    }
    /*
     * contract data not found
     */
    {
        let request = json!(
            {
                "jsonrpc": "2.0",
                "id": 1,
                "method": "getLedgerEntries",
                "params": { "keys": [ledger_key_xdr] },
        }
        );
        let response = json!(
            {
                "jsonrpc": "2.0",
                "id": 1,
                "result": {
                "entries": [],
            "latestLedger": 2552990
        }
        }
        );

        let (s, _m) = get_mocked_server(request, response).await;
        let result = s
            .get_contract_data(address, key.clone(), Durability::Persistent)
            .await;

        assert!(matches!(result, Err(Error::ContractDataNotFound)));
    }
}

#[tokio::test]
async fn get_transaction() {
    {
        let hash = "6bc97bddc21811c626839baf4ab574f4f9f7ddbebb44d286ae504396d4e752da";
        let request = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "method": "getTransaction",
          "params": {
            "hash": hash
          }
        }
                );
        let response = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "result": {
            "status": "SUCCESS",
            "latestLedger": 2540076,
            "latestLedgerCloseTime": "1700086333",
            "oldestLedger": 2538637,
            "oldestLedgerCloseTime": "1700078796",
            "applicationOrder": 1,
            "envelopeXdr": "AAAAAgAAAADGFY14/R1KD0VGtTbi5Yp4d7LuMW0iQbLM/AUiGKj5owCpsoQAJY3OAAAjqgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAGAAAAAAAAAABhhOwI+RL18Zpk7cqI5pRRf0L96jE8i+0x3ekhuBh2cUAAAARc2V0X2N1cnJlbmN5X3JhdGUAAAAAAAACAAAADwAAAANldXIAAAAACQAAAAAAAAAAAAAAAAARCz4AAAABAAAAAAAAAAAAAAABhhOwI+RL18Zpk7cqI5pRRf0L96jE8i+0x3ekhuBh2cUAAAARc2V0X2N1cnJlbmN5X3JhdGUAAAAAAAACAAAADwAAAANldXIAAAAACQAAAAAAAAAAAAAAAAARCz4AAAAAAAAAAQAAAAAAAAABAAAAB4408vVXuLU3mry897TfPpYjjsSN7n42REos241RddYdAAAAAQAAAAYAAAABhhOwI+RL18Zpk7cqI5pRRf0L96jE8i+0x3ekhuBh2cUAAAAUAAAAAQFvcYAAAImAAAAHxAAAAAAAAAACAAAAARio+aMAAABATbFMyom/TUz87wHex0LoYZA8jbNJkXbaDSgmOdk+wSBFJuMuta+/vSlro0e0vK2+1FqD/zWHZeYig4pKmM3rDA==",
            "resultXdr": "AAAAAAARFy8AAAAAAAAAAQAAAAAAAAAYAAAAAMu8SHUN67hTUJOz3q+IrH9M/4dCVXaljeK6x1Ss20YWAAAAAA==",
            "resultMetaXdr": " ",
            "ledger": 2540064,
            "createdAt": "1700086268"
          }
        }
        );

        let (s, _m) = get_mocked_server(request, response).await;
        let txresult = s.get_transaction(hash).await;

        if let Ok(r) = txresult {
            assert_eq!(r.status, TransactionStatus::Success);
            assert_eq!(r.latest_ledger, 2540076);
            assert_eq!(r.oldest_ledger, 2538637);
            assert_eq!(r.application_order, Some(1));
            let result = r.to_result().unwrap();
            assert_eq!(result.fee_charged, 1120047);
            if let TransactionResultResult::TxSuccess(ops) = result.result {
                let op = ops.first().unwrap();

                if let OperationResult::OpInner(OperationResultTr::InvokeHostFunction(
                    InvokeHostFunctionResult::Success(Hash(h)),
                )) = op
                {
                    let mut expected_h = [0; 32];
                    hex::decode_to_slice(
                        "cbbc48750debb8535093b3deaf88ac7f4cff87425576a58de2bac754acdb4616",
                        &mut expected_h,
                    )
                    .expect("Cannot convert the expected result");
                    assert_eq!(h, &expected_h);
                } else {
                    panic!("InvokeHostFunctionResult not found")
                }
                //
            } else {
                panic!("TransactionResultResult not found")
            }
            let _envelope = r.to_envelope().expect("Should not fail");
            let (_meta, val) = r.to_result_meta().expect("Should not fail");
            assert_eq!(val, Some(ScVal::Void));
            // TODO add more tests
        } else {
            panic!("Error was not expected")
        }
    }

    /*
     * Not found transaction
     */
    {
        let hash = "85f7aa8bfda425b98c0e53ffe56796ffd8865ec2fcc3ad71abf120801e2a14e5";
        let request = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "method": "getTransaction",
          "params": {
            "hash": hash
          }
        }
                );
        let response = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "result": {
            "status": "NOT_FOUND",
            "latestLedger": 2540099,
            "latestLedgerCloseTime": "1700086455",
            "oldestLedger": 2538660,
            "oldestLedgerCloseTime": "1700078913"
          }
        }
                );

        let (s, _m) = get_mocked_server(request, response).await;
        let txresult = s.get_transaction(hash).await;
        if let Ok(r) = txresult {
            assert_eq!(r.status, TransactionStatus::NotFound);
            assert_eq!(r.latest_ledger, 2540099);
            assert_eq!(r.oldest_ledger, 2538660);
            assert_eq!(r.application_order, None);
            assert_eq!(r.to_result(), None);
        } else {
            panic!("Error was not expected")
        }
    }
    /*
     * Failed transaction
     */
    {
        let hash = "2e4c699cbcb8ee83fffb857c9579bcc91f73f0df2a0444292f66e37563785929";
        let request = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "method": "getTransaction",
          "params": {
            "hash": hash
          }
        }
                );
        let response = json!(
        {
          "jsonrpc": "2.0",
          "id": 8675309,
          "result": {
            "status": "FAILED",
            "latestLedger": 2540124,
            "latestLedgerCloseTime": "1700086588",
            "oldestLedger": 2538685,
            "oldestLedgerCloseTime": "1700079044",
            "applicationOrder": 2,
            "envelopeXdr": "AAAAAgAAAABZvyflsZ5FumtSdS+t0/YnWWML3YWdzX1BGk/Qy786aQAAAG4AFyJfAABKvgAAAAIAAAAAAAAAAQAmwlEAJsK1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAE/fr1kk7lqP0srDPW4JExF1MWmpsG49FsRE3b0vMCmzAAAAAUVVUlMAAAAArPm4/+q9j4dl178h2JjrqjgRXnQ1WiGkyVy+nv3nGkEAAAAAAVjZQAAAAAAAAAABy786aQAAAEDybJBtG7V5NrRFpoboRUN/5ecys5wSUgag3CnTtWLmq3JDOxrEjK9noAnu/F5O0E8iXuVzX9BxZSO9JZ+Tw6kK",
            "resultXdr": "AAAAAAAAAGT/////AAAAAQAAAAAAAAAB////+gAAAAA=",
            "resultMetaXdr": "AAAAAwAAAAAAAAACAAAAAwAmwlIAAAAAAAAAAFm/J+WxnkW6a1J1L63T9idZYwvdhZ3NfUEaT9DLvzppAAAAF0g7NXsAFyJfAABKvQAAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAMAAAAAACbCSwAAAABlVULiAAAAAAAAAAEAJsJSAAAAAAAAAABZvyflsZ5FumtSdS+t0/YnWWML3YWdzX1BGk/Qy786aQAAABdIOzV7ABciXwAASr4AAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAmwlIAAAAAZVVDBwAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
            "ledger": 2540114,
            "createdAt": "1700086535"
          }
        }
            );

        let (s, _m) = get_mocked_server(request, response).await;
        let txresult = s.get_transaction(hash).await;
        if let Ok(r) = txresult {
            assert_eq!(r.status, TransactionStatus::Failed);
            assert_eq!(r.latest_ledger, 2540124);
            assert_eq!(r.oldest_ledger, 2538685);
            assert_eq!(r.application_order, Some(2));
            let result = r.to_result().unwrap();
            if let TransactionResultResult::TxFailed(ops) = result.result {
                let op = ops.first().unwrap();
                assert_eq!(
                    op,
                    &OperationResult::OpInner(OperationResultTr::Payment(
                        stellar_baselib::xdr::PaymentResult::NoTrust
                    ))
                );
            } else {
                panic!("Expect a failed transaction")
            }
        }
    }
    /*
     * Events
     */
    {
        let hash = "2e4c699cbcb8ee83fffb857c9579bcc91f73f0df2a0444292f66e37563785929";
        let request = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "method": "getTransaction",
          "params": {
            "hash": hash
          }
        }
                );
        let response = json!(
        {
          "jsonrpc": "2.0",
          "id": 8675309,
          "result": {
            "latestLedger": 1317536,
            "latestLedgerCloseTime": "1748999063",
            "oldestLedger": 1297535,
            "oldestLedgerCloseTime": "1748898975",
            "status": "SUCCESS",
            "txHash": hash,
            "applicationOrder": 1,
            "feeBump": false,
            "envelopeXdr": "AAAAAgAAAADte5nJrehJq/pu3qlV/bASRSOiJVXdNC+gQW/nxVNWuQBY644AEETyAAAOngAAAAAAAAAAAAAAAQAAAAAAAAAYAAAAAAAAAAHf65G24dyt1q+Xu3xFX5fzdHcKf3j2lXO5n11b+EnOfAAAAA1jcmVhdGVfZXNjcm93AAAAAAAABQAAAAUAAAAAAAaRmQAAABIAAAAAAAAAACcMY2GvjF3igK326WyiU8hv107p9YxvAS29gt1fml2WAAAAEgAAAAAAAAAAyewwXk7lqpxiQNYP3VlZ1EEprNK+dSBV4KQ9iluwbx8AAAASAAAAAAAAAAAY2Rm1IXXndEI0rYg2bt1/rw2mi1SYOUT2qeKPvf56cgAAABIAAAABusKzizgXRsUWKJQRrpWHAWG/yujQ6LBT/pMDljEiAegAAAAAAAAAAQAAAAAAAAACAAAABgAAAAHf65G24dyt1q+Xu3xFX5fzdHcKf3j2lXO5n11b+EnOfAAAABQAAAABAAAABw92WUOXbPOCn5SPHsgIOq8K1UypMpJe18Eh5s6eH8KeAAAAAQAAAAYAAAAB3+uRtuHcrdavl7t8RV+X83R3Cn949pVzuZ9dW/hJznwAAAAQAAAAAQAAAAIAAAAPAAAABVN0YXRlAAAAAAAABQAAAAAABpGZAAAAAQA1p5gAAEC0AAABuAAAAAAAWOsqAAAAAcVTVrkAAABAkR3EyCbHmZqEzQ1hvb1u2zY8PMqfhm7Z8zULGlpdNV0rWSbchA/NDudYEYrQKdA0qy647T+ojtdMfwLrfHELCA==",
            "resultXdr": "AAAAAABM5iEAAAAAAAAAAQAAAAAAAAAYAAAAANkPSp3CD6fXFropzD1Dse4sGrxEO/NPfv6SvhMR1kNkAAAAAA==",
            "resultMetaXdr": " ",
            "diagnosticEventsXdr": [
              "AAAAAQAAAAAAAAAAAAAAAgAAAAAAAAADAAAADwAAAAdmbl9jYWxsAAAAAA0AAAAg3+uRtuHcrdavl7t8RV+X83R3Cn949pVzuZ9dW/hJznwAAAAPAAAADWNyZWF0ZV9lc2Nyb3cAAAAAAAAQAAAAAQAAAAUAAAAFAAAAAAAGkZkAAAASAAAAAAAAAAAnDGNhr4xd4oCt9ulsolPIb9dO6fWMbwEtvYLdX5pdlgAAABIAAAAAAAAAAMnsMF5O5aqcYkDWD91ZWdRBKazSvnUgVeCkPYpbsG8fAAAAEgAAAAAAAAAAGNkZtSF153RCNK2INm7df68NpotUmDlE9qnij73+enIAAAASAAAAAbrCs4s4F0bFFiiUEa6VhwFhv8ro0OiwU/6TA5YxIgHo",
              "AAAAAQAAAAAAAAAB3+uRtuHcrdavl7t8RV+X83R3Cn949pVzuZ9dW/hJznwAAAABAAAAAAAAAAEAAAAPAAAABGluaXQAAAAQAAAAAQAAAAUAAAAFAAAAAAAGkZkAAAASAAAAAAAAAAAnDGNhr4xd4oCt9ulsolPIb9dO6fWMbwEtvYLdX5pdlgAAABIAAAAAAAAAAMnsMF5O5aqcYkDWD91ZWdRBKazSvnUgVeCkPYpbsG8fAAAAEgAAAAAAAAAAGNkZtSF153RCNK2INm7df68NpotUmDlE9qnij73+enIAAAASAAAAAbrCs4s4F0bFFiiUEa6VhwFhv8ro0OiwU/6TA5YxIgHo",

              "AAAAAQAAAAAAAAAB3+uRtuHcrdavl7t8RV+X83R3Cn949pVzuZ9dW/hJznwAAAACAAAAAAAAAAIAAAAPAAAACWZuX3JldHVybgAAAAAAAA8AAAANY3JlYXRlX2VzY3JvdwAAAAAAAAE=",
              "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAACnJlYWRfZW50cnkAAAAAAAUAAAAAAAAAAw==",
              "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAC3dyaXRlX2VudHJ5AAAAAAUAAAAAAAAAAQ==",
              "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAEGxlZGdlcl9yZWFkX2J5dGUAAAAFAAAAAAAAQLQ=",
              "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAEWxlZGdlcl93cml0ZV9ieXRlAAAAAAAABQAAAAAAAAG4",
              "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAADXJlYWRfa2V5X2J5dGUAAAAAAAAFAAAAAAAAAKg=",
              "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAADndyaXRlX2tleV9ieXRlAAAAAAAFAAAAAAAAAAA=",
              "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAADnJlYWRfZGF0YV9ieXRlAAAAAAAFAAAAAAAAAGg=",

              "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAD3dyaXRlX2RhdGFfYnl0ZQAAAAAFAAAAAAAAAbg=",
              "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAADnJlYWRfY29kZV9ieXRlAAAAAAAFAAAAAAAAQEw=",
              "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAD3dyaXRlX2NvZGVfYnl0ZQAAAAAFAAAAAAAAAAA=",
              "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAACmVtaXRfZXZlbnQAAAAAAAUAAAAAAAAAAQ==",
              "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAD2VtaXRfZXZlbnRfYnl0ZQAAAAAFAAAAAAAAAQQ=",
              "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAACGNwdV9pbnNuAAAABQAAAAAAM2BC",
              "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAACG1lbV9ieXRlAAAABQAAAAAAGwer",
              "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAEWludm9rZV90aW1lX25zZWNzAAAAAAAABQAAAAAACD99",

              "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAD21heF9yd19rZXlfYnl0ZQAAAAAFAAAAAAAAAFQ=",
              "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAEG1heF9yd19kYXRhX2J5dGUAAAAFAAAAAAAAAbg=",
              "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAEG1heF9yd19jb2RlX2J5dGUAAAAFAAAAAAAAQEw=",
              "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAE21heF9lbWl0X2V2ZW50X2J5dGUAAAAABQAAAAAAAAEE",

              "AAAAAQAAAAAAAAAB15KLcsJwPM/q9+uf9O9NUEpVqLl5/JtFDqLIQrTRzmEAAAABAAAAAAAAAAIAAAAPAAAAA2ZlZQAAAAASAAAAAAAAAADte5nJrehJq/pu3qlV/bASRSOiJVXdNC+gQW/nxVNWuQAAAAoAAAAAAAAAAAAAAAAAWOuO",
              "AAAAAQAAAAAAAAAB15KLcsJwPM/q9+uf9O9NUEpVqLl5/JtFDqLIQrTRzmEAAAABAAAAAAAAAAIAAAAPAAAAA2ZlZQAAAAASAAAAAAAAAADte5nJrehJq/pu3qlV/bASRSOiJVXdNC+gQW/nxVNWuQAAAAr/////////////////8/qT"
            ],
            "events": {
              "diagnosticEventsXdr": [
                "AAAAAQAAAAAAAAAAAAAAAgAAAAAAAAADAAAADwAAAAdmbl9jYWxsAAAAAA0AAAAg3+uRtuHcrdavl7t8RV+X83R3Cn949pVzuZ9dW/hJznwAAAAPAAAADWNyZWF0ZV9lc2Nyb3cAAAAAAAAQAAAAAQAAAAUAAAAFAAAAAAAGkZkAAAASAAAAAAAAAAAnDGNhr4xd4oCt9ulsolPIb9dO6fWMbwEtvYLdX5pdlgAAABIAAAAAAAAAAMnsMF5O5aqcYkDWD91ZWdRBKazSvnUgVeCkPYpbsG8fAAAAEgAAAAAAAAAAGNkZtSF153RCNK2INm7df68NpotUmDlE9qnij73+enIAAAASAAAAAbrCs4s4F0bFFiiUEa6VhwFhv8ro0OiwU/6TA5YxIgHo",
                "AAAAAQAAAAAAAAAB3+uRtuHcrdavl7t8RV+X83R3Cn949pVzuZ9dW/hJznwAAAACAAAAAAAAAAIAAAAPAAAACWZuX3JldHVybgAAAAAAAA8AAAANY3JlYXRlX2VzY3JvdwAAAAAAAAE=",
                "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAACnJlYWRfZW50cnkAAAAAAAUAAAAAAAAAAw==",
                "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAC3dyaXRlX2VudHJ5AAAAAAUAAAAAAAAAAQ==",
                "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAEGxlZGdlcl9yZWFkX2J5dGUAAAAFAAAAAAAAQLQ=",
                "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAEWxlZGdlcl93cml0ZV9ieXRlAAAAAAAABQAAAAAAAAG4",

                "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAADXJlYWRfa2V5X2J5dGUAAAAAAAAFAAAAAAAAAKg=",
                "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAADndyaXRlX2tleV9ieXRlAAAAAAAFAAAAAAAAAAA=",

                "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAADnJlYWRfZGF0YV9ieXRlAAAAAAAFAAAAAAAAAGg=",
                "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAD3dyaXRlX2RhdGFfYnl0ZQAAAAAFAAAAAAAAAbg=",
                "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAADnJlYWRfY29kZV9ieXRlAAAAAAAFAAAAAAAAQEw=",
                "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAD3dyaXRlX2NvZGVfYnl0ZQAAAAAFAAAAAAAAAAA=",
                "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAACmVtaXRfZXZlbnQAAAAAAAUAAAAAAAAAAQ==",
                "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAD2VtaXRfZXZlbnRfYnl0ZQAAAAAFAAAAAAAAAQQ=",
                "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAACGNwdV9pbnNuAAAABQAAAAAAM2BC",
                "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAACG1lbV9ieXRlAAAABQAAAAAAGwer",
                "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAEWludm9rZV90aW1lX25zZWNzAAAAAAAABQAAAAAACD99",
                "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAD21heF9yd19rZXlfYnl0ZQAAAAAFAAAAAAAAAFQ=",

                "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAEG1heF9yd19kYXRhX2J5dGUAAAAFAAAAAAAAAbg=",
                "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAEG1heF9yd19jb2RlX2J5dGUAAAAFAAAAAAAAQEw=",
                "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAE21heF9lbWl0X2V2ZW50X2J5dGUAAAAABQAAAAAAAAEE"
              ],

              "transactionEventsXdr": [
                "AAAAAAAAAAAAAAAB15KLcsJwPM/q9+uf9O9NUEpVqLl5/JtFDqLIQrTRzmEAAAABAAAAAAAAAAIAAAAPAAAAA2ZlZQAAAAASAAAAAAAAAADte5nJrehJq/pu3qlV/bASRSOiJVXdNC+gQW/nxVNWuQAAAAoAAAAAAAAAAAAAAAAAWOuO",

                "AAAAAQAAAAAAAAAB15KLcsJwPM/q9+uf9O9NUEpVqLl5/JtFDqLIQrTRzmEAAAABAAAAAAAAAAIAAAAPAAAAA2ZlZQAAAAASAAAAAAAAAADte5nJrehJq/pu3qlV/bASRSOiJVXdNC+gQW/nxVNWuQAAAAr/////////////////8/qT"
              ],
              "contractEventsXdr": [

                [
                  "AAAAAAAAAAHf65G24dyt1q+Xu3xFX5fzdHcKf3j2lXO5n11b+EnOfAAAAAEAAAAAAAAAAQAAAA8AAAAEaW5pdAAAABAAAAABAAAABQAAAAUAAAAAAAaRmQAAABIAAAAAAAAAACcMY2GvjF3igK326WyiU8hv107p9YxvAS29gt1fml2WAAAAEgAAAAAAAAAAyewwXk7lqpxiQNYP3VlZ1EEprNK+dSBV4KQ9iluwbx8AAAASAAAAAAAAAAAY2Rm1IXXndEI0rYg2bt1/rw2mi1SYOUT2qeKPvf56cgAAABIAAAABusKzizgXRsUWKJQRrpWHAWG/yujQ6LBT/pMDljEiAeg="
                ]

              ]
            },
            "ledger": 1303424,
            "createdAt": "1748928444"

          }
        }
        );

        let (s, _m) = get_mocked_server(request, response).await;
        let txresult = s.get_transaction(hash).await;
        if let Ok(r) = txresult {
            if let Some((tx_events, contract_events)) = r.to_events() {
                //
                let TransactionEvent { stage, event: _ } = &tx_events[0];
                assert_eq!(stage, &TransactionEventStage::BeforeAllTxs);

                let TransactionEvent { stage, event: _ } = &tx_events[1];
                assert_eq!(stage, &TransactionEventStage::AfterTx);

                let ContractEvent {
                    ext: _,
                    contract_id,
                    type_: _,
                    body: _,
                } = &contract_events[0][0];

                assert_eq!(
                    contract_id,
                    &Some(ContractId(Hash(
                        *hex::decode(
                            "dfeb91b6e1dcadd6af97bb7c455f97f374770a7f78f69573b99f5d5bf849ce7c"
                        )
                        .unwrap()
                        .last_chunk::<32>()
                        .unwrap()
                    )))
                );
            } else {
                panic!("fail")
            }
        }
    }
}

#[tokio::test]
async fn wait_transaction() {
    let hash = "85f7aa8bfda425b98c0e53ffe56796ffd8865ec2fcc3ad71abf120801e2a14e5";
    let request = json!(
    {
      "jsonrpc": "2.0",
      "id": 1,
      "method": "getTransaction",
      "params": {
        "hash": hash
      }
    }
            );
    let response = json!(
    {
      "jsonrpc": "2.0",
      "id": 1,
      "result": {
        "status": "NOT_FOUND",
        "latestLedger": 2540099,
        "latestLedgerCloseTime": "1700086455",
        "oldestLedger": 2538660,
        "oldestLedgerCloseTime": "1700078913"
      }
    }
            );

    let (s, _m) = get_mocked_server(request, response).await;
    let test_timeout = 3;
    let txresult = s
        .wait_transaction(hash, Duration::from_secs(test_timeout))
        .await;
    dbg!(&txresult);
    if let Err((err, tx)) = txresult {
        if let Error::WaitTransactionTimeout(timeout, waited) = err {
            assert_eq!(timeout, test_timeout);
            assert!(waited >= timeout);
        } else {
            panic!("Not a WaitTransactionTimeout error")
        }

        if let Some(r) = tx {
            assert_eq!(r.status, TransactionStatus::NotFound);
        } else {
            panic!("No result found")
        }
    }
}

#[tokio::test]
async fn simulate_transaction() {
    /*
     * Success (no auth)
     */
    {
        let tx_xdr = "AAAAAgAAAAAg4dbAxsGAGICfBG3iT2cKGYQ6hK4sJWzZ6or1C5v6GAAAAGQAJsOiAAAAEQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAGAAAAAAAAAABzAP+dP0PsNzYvFF1pv7a8RQXwH5eg3uZBbbWjE9PwAsAAAAJaW5jcmVtZW50AAAAAAAAAgAAABIAAAAAAAAAACDh1sDGwYAYgJ8EbeJPZwoZhDqEriwlbNnqivULm/oYAAAAAwAAAAMAAAAAAAAAAAAAAAA=";
        let request = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "method": "simulateTransaction",
          "params": {
            "transaction": tx_xdr,
            "authMode": "record"
          },
        }
        );
        let response = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "result": {
            "transactionData": "AAAAAAAAAAIAAAAGAAAAAcwD/nT9D7Dc2LxRdab+2vEUF8B+XoN7mQW21oxPT8ALAAAAFAAAAAEAAAAHy8vNUZ8vyZ2ybPHW0XbSrRtP7gEWsJ6zDzcfY9P8z88AAAABAAAABgAAAAHMA/50/Q+w3Ni8UXWm/trxFBfAfl6De5kFttaMT0/ACwAAABAAAAABAAAAAgAAAA8AAAAHQ291bnRlcgAAAAASAAAAAAAAAAAg4dbAxsGAGICfBG3iT2cKGYQ6hK4sJWzZ6or1C5v6GAAAAAEAHfKyAAAFiAAAAIgAAAAAAAAAAw==",
            "minResourceFee": "90353",
            "events": [
              "AAAAAQAAAAAAAAAAAAAAAgAAAAAAAAADAAAADwAAAAdmbl9jYWxsAAAAAA0AAAAgzAP+dP0PsNzYvFF1pv7a8RQXwH5eg3uZBbbWjE9PwAsAAAAPAAAACWluY3JlbWVudAAAAAAAABAAAAABAAAAAgAAABIAAAAAAAAAACDh1sDGwYAYgJ8EbeJPZwoZhDqEriwlbNnqivULm/oYAAAAAwAAAAM=",
              "AAAAAQAAAAAAAAABzAP+dP0PsNzYvFF1pv7a8RQXwH5eg3uZBbbWjE9PwAsAAAACAAAAAAAAAAIAAAAPAAAACWZuX3JldHVybgAAAAAAAA8AAAAJaW5jcmVtZW50AAAAAAAAAwAAAAw="
            ],
            "results": [
              {
                "auth": [],
                "xdr": "AAAAAwAAAAw="
              }
            ],
            "cost": {
              "cpuInsns": "1635562",
              "memBytes": "1295756"
            },
            "latestLedger": 2552139
          }
        }
                );
        let mut source_account = Account::new(
            "GAQODVWAY3AYAGEAT4CG3YSPM4FBTBB2QSXCYJLM3HVIV5ILTP5BRXCD",
            "10911149667123216",
        )
        .unwrap();
        let network = Networks::testnet();
        let time_bounds = TimeBounds {
            min_time: TimePoint(0),
            max_time: TimePoint(0),
        };

        let contract =
            Contracts::new("CDGAH7TU7UH3BXGYXRIXLJX63LYRIF6APZPIG64ZAW3NNDCPJ7AAWVTZ").unwrap();
        let op = contract.call(
            "increment",
            Some(vec![
                Address::new("GAQODVWAY3AYAGEAT4CG3YSPM4FBTBB2QSXCYJLM3HVIV5ILTP5BRXCD")
                    .unwrap()
                    .to_sc_val()
                    .unwrap(),
                ScVal::U32(3),
            ]),
        );

        let mut tx_builder =
            TransactionBuilder::new(&mut source_account, network, Some(time_bounds));
        tx_builder.add_operation(op);
        tx_builder.fee(100u32);

        let tx = tx_builder.build();
        let xdr = tx
            .to_envelope()
            .unwrap()
            .to_xdr_base64(Limits::none())
            .unwrap();
        assert_eq!(xdr, tx_xdr);

        let (s, _m) = get_mocked_server(request, response).await;
        let txresult = s
            .simulate_transaction(
                &tx,
                Some(SimulationOptions {
                    auth_mode: Some(AuthMode::Record),
                    ..Default::default()
                }),
            )
            .await
            .unwrap();

        if let Some((ret_val, _auth)) = txresult.to_result() {
            assert_eq!(ret_val, ScVal::U32(12));
        } else {
            panic!("Simulation failed")
        }

        if let Some(tx_data) = txresult.to_transaction_data().as_ref() {
            assert_eq!(tx_data.resource_fee, 3);
            assert_eq!(tx_data.resources.instructions, 1962674);
            assert_eq!(tx_data.resources.disk_read_bytes, 1416);
            assert_eq!(tx_data.resources.write_bytes, 136);
        } else {
            panic!("Simulation failed")
        }
    }

    /*
     * Failed transaction
     * */
    {
        let tx_xdr = "AAAAAgAAAAAg4dbAxsGAGICfBG3iT2cKGYQ6hK4sJWzZ6or1C5v6GAAAAGQAJsOiAAAADwAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAGAAAAAAAAAABzAP+dP0PsNzYvFF1pv7a8RQXwH5eg3uZBbbWjE9PwAsAAAAJaW5jcmVtZW50AAAAAAAAAQAAAAMAAAADAAAAAAAAAAAAAAAA";
        let request = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "method": "simulateTransaction",
          "params": {
            "transaction": tx_xdr,
            "resourceConfig": {
              "instructionLeeway": 3000000
            },
            "authMode": "enforce"
          }
        }
                );
        let expected_err_str = "host invocation failed\n\nCaused by:\n    HostError: Error(WasmVm, InternalError)\n    \n    Event log (newest first):\n       0: [Diagnostic Event] contract:cc03fe74fd0fb0dcd8bc5175a6fedaf11417c07e5e837b9905b6d68c4f4fc00b, topics:[error, Error(WasmVm, InternalError)], data:[\"VM call failed: Func(MismatchingParameterLen)\", increment]\n       1: [Diagnostic Event] topics:[fn_call, Bytes(cc03fe74fd0fb0dcd8bc5175a6fedaf11417c07e5e837b9905b6d68c4f4fc00b), increment], data:3\n    \n    Backtrace (newest first):\n       0: soroban_env_host::vm::Vm::invoke_function_raw\n       1: soroban_env_host::host::frame::<impl soroban_env_host::host::Host>::with_frame\n       2: soroban_env_host::host::frame::<impl soroban_env_host::host::Host>::call_n_internal\n       3: soroban_env_host::host::frame::<impl soroban_env_host::host::Host>::invoke_function\n       4: preflight::preflight::preflight_invoke_hf_op\n       5: preflight::preflight_invoke_hf_op::{{closure}}\n       6: core::ops::function::FnOnce::call_once{{vtable.shim}}\n       7: preflight::catch_preflight_panic\n       8: _cgo_0b49d6ed4a0b_Cfunc_preflight_invoke_hf_op\n                 at tmp/go-build/cgo-gcc-prolog:103:11\n       9: runtime.asmcgocall\n                 at ./runtime/asm_amd64.s:848\n    \n    ";
        let response = json!(
        {
                   "jsonrpc": "2.0",
                   "id": 1,
                   "result": {
                     "error": expected_err_str,
                     "events": [
                       "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAADAAAADwAAAAdmbl9jYWxsAAAAAA0AAAAgzAP+dP0PsNzYvFF1pv7a8RQXwH5eg3uZBbbWjE9PwAsAAAAPAAAACWluY3JlbWVudAAAAAAAAAMAAAAD",
                       "AAAAAAAAAAAAAAABzAP+dP0PsNzYvFF1pv7a8RQXwH5eg3uZBbbWjE9PwAsAAAACAAAAAAAAAAIAAAAPAAAABWVycm9yAAAAAAAAAgAAAAEAAAAHAAAAEAAAAAEAAAACAAAADgAAAC1WTSBjYWxsIGZhaWxlZDogRnVuYyhNaXNtYXRjaGluZ1BhcmFtZXRlckxlbikAAAAAAAAPAAAACWluY3JlbWVudAAAAA=="
                     ],
                     "cost": {
                       "cpuInsns": "0",
                       "memBytes": "0"
                     },
                     "latestLedger": 2552013
                   }
          }
                         );
        let mut source_account = Account::new(
            "GAQODVWAY3AYAGEAT4CG3YSPM4FBTBB2QSXCYJLM3HVIV5ILTP5BRXCD",
            "10911149667123214",
        )
        .unwrap();
        let network = Networks::testnet();
        let time_bounds = TimeBounds {
            min_time: TimePoint(0),
            max_time: TimePoint(0),
        };

        let contract =
            Contracts::new("CDGAH7TU7UH3BXGYXRIXLJX63LYRIF6APZPIG64ZAW3NNDCPJ7AAWVTZ").unwrap();
        let op = contract.call("increment", Some(vec![ScVal::U32(3)]));

        let mut tx_builder =
            TransactionBuilder::new(&mut source_account, network, Some(time_bounds));
        tx_builder.add_operation(op);
        tx_builder.fee(100u32);

        let tx = tx_builder.build();
        let xdr = tx
            .to_envelope()
            .unwrap()
            .to_xdr_base64(Limits::none())
            .unwrap();
        assert_eq!(xdr, tx_xdr);

        let (s, _m) = get_mocked_server(request, response).await;
        let txresult = s
            .simulate_transaction(
                &tx,
                Some(SimulationOptions {
                    cpu_instructions: 3000000,
                    auth_mode: Some(AuthMode::Enforce),
                }),
            )
            .await
            .unwrap();

        if let Some(err_str) = txresult.error.clone() {
            assert_eq!(err_str, expected_err_str);
            let diag_events = txresult.to_events().unwrap();
            assert_eq!(diag_events.len(), 2);
            let stellar_baselib::xdr::ContractEventBody::V0(ContractEventV0 { topics, data }) =
                &diag_events[0].event.body;

            assert_eq!(
                topics.to_vec()[0],
                ScVal::Symbol(ScSymbol("fn_call".try_into().unwrap()))
            );
            assert_eq!(data, &ScVal::U32(3));

            let stellar_baselib::xdr::ContractEventBody::V0(ContractEventV0 { topics, data }) =
                &diag_events[1].event.body;
            assert_eq!(
                topics.to_vec(),
                vec![
                    ScVal::Symbol(ScSymbol("error".try_into().unwrap())),
                    ScVal::Error(stellar_baselib::xdr::ScError::WasmVm(
                        stellar_baselib::xdr::ScErrorCode::InternalError
                    ))
                ]
            );
            if let ScVal::Vec(Some(data_v)) = data {
                assert_eq!(
                    data_v.to_vec(),
                    vec![
                        ScVal::String(ScString(
                            "VM call failed: Func(MismatchingParameterLen)"
                                .try_into()
                                .unwrap()
                        )),
                        ScVal::Symbol(ScSymbol("increment".try_into().unwrap()))
                    ]
                )
            } else {
                panic!("Missing diag event")
            }
        } else {
            panic!("Simulation failed")
        }
    }
    /*
     * State changes
     */
    {
        let tx_xdr = "AAAAAgAAAAD/RDKqj6Kdnakmnlac+iWBROMjeE9F/5bQmfT4G8DlcwX14QAAD1DYAAAAAgAAAAAAAAAAAAAAAQAAAAAAAAAYAAAAAAAAAAEkIK54Itc3IZGRtBind27TuweJ+klDiPyK5NXu67CaaAAAAAhpbmNfYXV0aAAAAAEAAAASAAAAAAAAAAD/RDKqj6Kdnakmnlac+iWBROMjeE9F/5bQmfT4G8DlcwAAAAAAAAAAAAAAAA==";
        let request = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "method": "simulateTransaction",
          "params": {
            "transaction": tx_xdr,
            "authMode": "record_allow_nonroot"
          }
        });
        let response = json!(
        {
          "jsonrpc": "2.0",
          "id": 8675309,
          "result": {
            "transactionData": "AAAAAAAAAAIAAAAGAAAAASQgrngi1zchkZG0GKd3btO7B4n6SUOI/Irk1e7rsJpoAAAAFAAAAAEAAAAHcOiuro2Kjk7NwMT6FDrXvb/h7SFI2ZYIxVt7UQy0M6EAAAABAAAABgAAAAEkIK54Itc3IZGRtBind27TuweJ+klDiPyK5NXu67CaaAAAABAAAAABAAAAAgAAAA8AAAALQ291bnRlckF1dGgAAAAAEgAAAAAAAAAA/0Qyqo+inZ2pJp5WnPolgUTjI3hPRf+W0Jn0+BvA5XMAAAAAAA0kKAAABrAAAACMAAAAAAABxsc=",
            "minResourceFee": "116423",
            "events": [
                    "AAAAAQAAAAAAAAAAAAAAAgAAAAAAAAADAAAADwAAAAdmbl9jYWxsAAAAAA0AAAAgJCCueCLXNyGRkbQYp3du07sHifpJQ4j8iuTV7uuwmmgAAAAPAAAACGluY19hdXRoAAAAEgAAAAAAAAAA/0Qyqo+inZ2pJp5WnPolgUTjI3hPRf+W0Jn0+BvA5XM=",
                    "AAAAAQAAAAAAAAABJCCueCLXNyGRkbQYp3du07sHifpJQ4j8iuTV7uuwmmgAAAABAAAAAAAAAAEAAAAPAAAAB2NvdW50ZXIAAAAAEAAAAAEAAAACAAAAEAAAAAEAAAACAAAADwAAAAtDb3VudGVyQXV0aAAAAAASAAAAAAAAAAD/RDKqj6Kdnakmnlac+iWBROMjeE9F/5bQmfT4G8DlcwAAAAMAAAAB",
                    "AAAAAQAAAAAAAAABJCCueCLXNyGRkbQYp3du07sHifpJQ4j8iuTV7uuwmmgAAAACAAAAAAAAAAIAAAAPAAAACWZuX3JldHVybgAAAAAAAA8AAAAIaW5jX2F1dGgAAAADAAAAAQ==",
            ],
            "results": [
              {
                "auth": [
                            "AAAAAAAAAAAAAAABJCCueCLXNyGRkbQYp3du07sHifpJQ4j8iuTV7uuwmmgAAAAIaW5jX2F1dGgAAAABAAAAEgAAAAAAAAAA/0Qyqo+inZ2pJp5WnPolgUTjI3hPRf+W0Jn0+BvA5XMAAAAA",
                        ],
                "xdr": "AAAAAwAAAAE=",
              }
            ],
            "stateChanges":
                [
                    {
                        "type": "created",
                        "key": "AAAABgAAAAEkIK54Itc3IZGRtBind27TuweJ+klDiPyK5NXu67CaaAAAABAAAAABAAAAAgAAAA8AAAALQ291bnRlckF1dGgAAAAAEgAAAAAAAAAA/0Qyqo+inZ2pJp5WnPolgUTjI3hPRf+W0Jn0+BvA5XMAAAAA",
                        "before": null,
                        "after":
                            "AAAAAAAAAAYAAAAAAAAAASQgrngi1zchkZG0GKd3btO7B4n6SUOI/Irk1e7rsJpoAAAAEAAAAAEAAAACAAAADwAAAAtDb3VudGVyQXV0aAAAAAASAAAAAAAAAAD/RDKqj6Kdnakmnlac+iWBROMjeE9F/5bQmfT4G8DlcwAAAAAAAAADAAAAAQAAAAA=",
                    },
                ],
            "latestLedger": 2552139
          }
        });

        let mut source_account = Account::new(
            "GD7UIMVKR6RJ3HNJE2PFNHH2EWAUJYZDPBHUL74W2CM7J6A3YDSXGPJN",
            "4311013293817857",
        )
        .unwrap();
        let network = Networks::testnet();

        let contract =
            Contracts::new("CASCBLTYELLTOIMRSG2BRJ3XN3J3WB4J7JEUHCH4RLSNL3XLWCNGRTCR").unwrap();
        let op = contract.call(
            "inc_auth",
            Some(vec![Address::new(
                "GD7UIMVKR6RJ3HNJE2PFNHH2EWAUJYZDPBHUL74W2CM7J6A3YDSXGPJN",
            )
            .unwrap()
            .to_sc_val()
            .unwrap()]),
        );

        let mut tx_builder = TransactionBuilder::new(&mut source_account, network, None);
        tx_builder.add_operation(op);
        tx_builder.fee(100000000u32);

        let tx = tx_builder.build();
        let xdr = tx
            .to_envelope()
            .unwrap()
            .to_xdr_base64(Limits::none())
            .unwrap();
        assert_eq!(xdr, tx_xdr);

        let (s, _m) = get_mocked_server(request, response).await;
        let txresult = s
            .simulate_transaction(
                &tx,
                Some(SimulationOptions {
                    auth_mode: Some(AuthMode::RecordAllowNonRoot),
                    ..Default::default()
                }),
            )
            .await
            .unwrap();
        let state_changes = txresult.to_state_changes();
        assert_eq!(state_changes.len(), 1);
        assert!(matches!(&state_changes[0].kind, StateChangeKind::Created));
    }
    /*
     * Updated
     */
    {
        let tx_xdr = "AAAAAgAAAAD/RDKqj6Kdnakmnlac+iWBROMjeE9F/5bQmfT4G8DlcwX14QAAD1DYAAAAAwAAAAAAAAAAAAAAAQAAAAAAAAAYAAAAAAAAAAEkIK54Itc3IZGRtBind27TuweJ+klDiPyK5NXu67CaaAAAAAhpbmNfYXV0aAAAAAEAAAASAAAAAAAAAAD/RDKqj6Kdnakmnlac+iWBROMjeE9F/5bQmfT4G8DlcwAAAAAAAAAAAAAAAA==";
        let request = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "method": "simulateTransaction",
          "params": {
            "transaction": tx_xdr,
          }
        });
        let response = json!(
        {
          "jsonrpc": "2.0",
          "id": 8675309,
          "result": {
            "transactionData": "AAAAAAAAAAIAAAAGAAAAASQgrngi1zchkZG0GKd3btO7B4n6SUOI/Irk1e7rsJpoAAAAFAAAAAEAAAAHcOiuro2Kjk7NwMT6FDrXvb/h7SFI2ZYIxVt7UQy0M6EAAAABAAAABgAAAAEkIK54Itc3IZGRtBind27TuweJ+klDiPyK5NXu67CaaAAAABAAAAABAAAAAgAAAA8AAAALQ291bnRlckF1dGgAAAAAEgAAAAAAAAAA/0Qyqo+inZ2pJp5WnPolgUTjI3hPRf+W0Jn0+BvA5XMAAAAAAA0kKAAABrAAAACMAAAAAAABxsc=",
            "minResourceFee": "116423",
            "results": [
              {
                "auth": [
                           "AAAAAAAAAAAAAAABJCCueCLXNyGRkbQYp3du07sHifpJQ4j8iuTV7uuwmmgAAAAIaW5jX2F1dGgAAAABAAAAEgAAAAAAAAAA/0Qyqo+inZ2pJp5WnPolgUTjI3hPRf+W0Jn0+BvA5XMAAAAA",
                        ],
                "xdr": "AAAAAwAAAAI=",
              }
            ],
            "stateChanges":
                [
                    {
                        "type": "updated",
                        "key": "AAAABgAAAAEkIK54Itc3IZGRtBind27TuweJ+klDiPyK5NXu67CaaAAAABAAAAABAAAAAgAAAA8AAAALQ291bnRlckF1dGgAAAAAEgAAAAAAAAAA/0Qyqo+inZ2pJp5WnPolgUTjI3hPRf+W0Jn0+BvA5XMAAAAA",
                        "before":
                            "AA9Q2wAAAAYAAAAAAAAAASQgrngi1zchkZG0GKd3btO7B4n6SUOI/Irk1e7rsJpoAAAAEAAAAAEAAAACAAAADwAAAAtDb3VudGVyQXV0aAAAAAASAAAAAAAAAAD/RDKqj6Kdnakmnlac+iWBROMjeE9F/5bQmfT4G8DlcwAAAAAAAAADAAAAAQAAAAA=",
                        "after":
                            "AA9Q2wAAAAYAAAAAAAAAASQgrngi1zchkZG0GKd3btO7B4n6SUOI/Irk1e7rsJpoAAAAEAAAAAEAAAACAAAADwAAAAtDb3VudGVyQXV0aAAAAAASAAAAAAAAAAD/RDKqj6Kdnakmnlac+iWBROMjeE9F/5bQmfT4G8DlcwAAAAAAAAADAAAAAgAAAAA=",
                    },
                ],
            "latestLedger": 2552139
          }
        });

        let mut source_account = Account::new(
            "GD7UIMVKR6RJ3HNJE2PFNHH2EWAUJYZDPBHUL74W2CM7J6A3YDSXGPJN",
            "4311013293817858",
        )
        .unwrap();
        let network = Networks::testnet();

        let contract =
            Contracts::new("CASCBLTYELLTOIMRSG2BRJ3XN3J3WB4J7JEUHCH4RLSNL3XLWCNGRTCR").unwrap();
        let op = contract.call(
            "inc_auth",
            Some(vec![Address::new(
                "GD7UIMVKR6RJ3HNJE2PFNHH2EWAUJYZDPBHUL74W2CM7J6A3YDSXGPJN",
            )
            .unwrap()
            .to_sc_val()
            .unwrap()]),
        );

        let mut tx_builder = TransactionBuilder::new(&mut source_account, network, None);
        tx_builder.add_operation(op);
        tx_builder.fee(100000000u32);

        let tx = tx_builder.build();
        let xdr = tx
            .to_envelope()
            .unwrap()
            .to_xdr_base64(Limits::none())
            .unwrap();
        assert_eq!(xdr, tx_xdr);

        let (s, _m) = get_mocked_server(request, response).await;
        let txresult = s
            .simulate_transaction(
                &tx,
                Some(SimulationOptions {
                    auth_mode: None,
                    ..Default::default()
                }),
            )
            .await
            .unwrap();
        let state_changes = txresult.to_state_changes();
        assert_eq!(state_changes.len(), 1);
        assert!(matches!(&state_changes[0].kind, StateChangeKind::Updated));
    }
    /*
     * Deleted
     */
    {
        let tx_xdr = "AAAAAgAAAAD/RDKqj6Kdnakmnlac+iWBROMjeE9F/5bQmfT4G8DlcwX14QAAD1DYAAAABAAAAAAAAAAAAAAAAQAAAAAAAAAYAAAAAAAAAAEkIK54Itc3IZGRtBind27TuweJ+klDiPyK5NXu67CaaAAAAAhpbmNfYXV0aAAAAAEAAAASAAAAAAAAAAD/RDKqj6Kdnakmnlac+iWBROMjeE9F/5bQmfT4G8DlcwAAAAAAAAAAAAAAAA==";
        let request = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "method": "simulateTransaction",
          "params": {
            "transaction": tx_xdr,
          }
        });
        let response = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "result": {
            "transactionData": "AAAAAAAAAAIAAAAGAAAAASQgrngi1zchkZG0GKd3btO7B4n6SUOI/Irk1e7rsJpoAAAAFAAAAAEAAAAHcOiuro2Kjk7NwMT6FDrXvb/h7SFI2ZYIxVt7UQy0M6EAAAABAAAABgAAAAEkIK54Itc3IZGRtBind27TuweJ+klDiPyK5NXu67CaaAAAABAAAAABAAAAAgAAAA8AAAALQ291bnRlckF1dGgAAAAAEgAAAAAAAAAA/0Qyqo+inZ2pJp5WnPolgUTjI3hPRf+W0Jn0+BvA5XMAAAAAAA0H8gAABzwAAAAAAAAAAAABdsc=",
            "minResourceFee": "116423",
            "results": [
              {
                "auth": [
                            "AAAAAAAAAAAAAAABJCCueCLXNyGRkbQYp3du07sHifpJQ4j8iuTV7uuwmmgAAAAIaW5jX2F1dGgAAAABAAAAEgAAAAAAAAAA/0Qyqo+inZ2pJp5WnPolgUTjI3hPRf+W0Jn0+BvA5XMAAAAA",
                        ],
                "xdr": "AAAAAwAAAAM=",
              }
            ],
            "stateChanges":
                [
                    {
                        "type": "deleted",
                        "key": "AAAABgAAAAEkIK54Itc3IZGRtBind27TuweJ+klDiPyK5NXu67CaaAAAABAAAAABAAAAAgAAAA8AAAALQ291bnRlckF1dGgAAAAAEgAAAAAAAAAA/0Qyqo+inZ2pJp5WnPolgUTjI3hPRf+W0Jn0+BvA5XMAAAAA",
                        "before":
                            "AA9Q3AAAAAYAAAAAAAAAASQgrngi1zchkZG0GKd3btO7B4n6SUOI/Irk1e7rsJpoAAAAEAAAAAEAAAACAAAADwAAAAtDb3VudGVyQXV0aAAAAAASAAAAAAAAAAD/RDKqj6Kdnakmnlac+iWBROMjeE9F/5bQmfT4G8DlcwAAAAAAAAADAAAAAgAAAAA=",
                        "after": null,
                    },
                ],
            "latestLedger": 2552139
          }
        });

        let mut source_account = Account::new(
            "GD7UIMVKR6RJ3HNJE2PFNHH2EWAUJYZDPBHUL74W2CM7J6A3YDSXGPJN",
            "4311013293817859",
        )
        .unwrap();
        let network = Networks::testnet();

        let contract =
            Contracts::new("CASCBLTYELLTOIMRSG2BRJ3XN3J3WB4J7JEUHCH4RLSNL3XLWCNGRTCR").unwrap();
        let op = contract.call(
            "inc_auth",
            Some(vec![Address::new(
                "GD7UIMVKR6RJ3HNJE2PFNHH2EWAUJYZDPBHUL74W2CM7J6A3YDSXGPJN",
            )
            .unwrap()
            .to_sc_val()
            .unwrap()]),
        );

        let mut tx_builder = TransactionBuilder::new(&mut source_account, network, None);
        tx_builder.add_operation(op);
        tx_builder.fee(100000000u32);

        let tx = tx_builder.build();
        let xdr = tx
            .to_envelope()
            .unwrap()
            .to_xdr_base64(Limits::none())
            .unwrap();
        assert_eq!(xdr, tx_xdr);

        let (s, _m) = get_mocked_server(request, response).await;
        let txresult = s.simulate_transaction(&tx, None).await.unwrap();
        let state_changes = txresult.to_state_changes();
        assert_eq!(state_changes.len(), 1);
        assert!(matches!(&state_changes[0].kind, StateChangeKind::Deleted));
        // TODO more test
    }
}

#[tokio::test]
async fn send_transaction() {
    /*
     * Pending transaction
     */
    {
        let tx_xdr = "AAAAAgAAAAAg4dbAxsGAGICfBG3iT2cKGYQ6hK4sJWzZ6or1C5v6GAAAAGQAJsOiAAAADQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAACgAAAAVIZWxsbwAAAAAAAAEAAAAMU29yb2JhbiBEb2NzAAAAAAAAAAELm/oYAAAAQATr6Ghp/DNO7S6JjEFwcJ9a+dvI6NJr7I/2eQttvoovjQ8te4zKKaapC3mbmx6ld6YKL5T81mxs45TjzdG5zw0=";
        let request = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "method": "sendTransaction",
          "params": {
            "transaction": tx_xdr,
          }
        }
            );
        let hash = "d8ec9b68780314ffdfdfc2194b1b35dd27d7303c3bceaef6447e31631a1419dc";
        let response = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "result": {
            "status": "PENDING",
            "hash": hash,
            "latestLedger": 2553978,
            "latestLedgerCloseTime": "1700159337"
          }
        }
            );

        let network = Networks::testnet();
        let tx = Transaction::from_xdr_envelope(tx_xdr, network);
        let xdr = tx
            .to_envelope()
            .unwrap()
            .to_xdr_base64(Limits::none())
            .unwrap();

        assert_eq!(xdr, tx_xdr);

        let (s, _m) = get_mocked_server(request, response).await;
        let txresult = s.send_transaction(tx).await.unwrap();
        assert_eq!(txresult.status, SendTransactionStatus::Pending);
        assert_eq!(txresult.hash, hash);
        assert_eq!(txresult.latest_ledger, 2553978);
        assert_eq!(txresult.latest_ledger_close_time, "1700159337");
        assert_eq!(txresult.to_error_result(), None);
        assert_eq!(txresult.to_diagnostic_events(), None);
    }

    /*
     * Duplicate transaction
     */
    {
        let tx_xdr = "AAAAAgAAAAAg4dbAxsGAGICfBG3iT2cKGYQ6hK4sJWzZ6or1C5v6GAAAAGQAJsOiAAAADQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAACgAAAAVIZWxsbwAAAAAAAAEAAAAMU29yb2JhbiBEb2NzAAAAAAAAAAELm/oYAAAAQATr6Ghp/DNO7S6JjEFwcJ9a+dvI6NJr7I/2eQttvoovjQ8te4zKKaapC3mbmx6ld6YKL5T81mxs45TjzdG5zw0=";
        let request = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "method": "sendTransaction",
          "params": {
            "transaction": tx_xdr,
          }
        }
                    );
        let hash = "d8ec9b68780314ffdfdfc2194b1b35dd27d7303c3bceaef6447e31631a1419dc";
        let response = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "result": {
            "status": "DUPLICATE",
            "hash": hash,
            "latestLedger": 2553978,
            "latestLedgerCloseTime": "1700159337"
          }
        }
            );

        let network = Networks::testnet();
        let tx = Transaction::from_xdr_envelope(tx_xdr, network);
        let xdr = tx
            .to_envelope()
            .unwrap()
            .to_xdr_base64(Limits::none())
            .unwrap();

        assert_eq!(xdr, tx_xdr);

        let (s, _m) = get_mocked_server(request, response).await;
        let txresult = s.send_transaction(tx).await.unwrap();
        assert_eq!(txresult.status, SendTransactionStatus::Duplicate);
        assert_eq!(txresult.hash, hash);
        assert_eq!(txresult.latest_ledger, 2553978);
        assert_eq!(txresult.latest_ledger_close_time, "1700159337");
        assert_eq!(txresult.to_error_result(), None);
        assert_eq!(txresult.to_diagnostic_events(), None);
    }
    /*
     * Try again transaction
     */
    {
        let tx_xdr = "AAAAAgAAAAAg4dbAxsGAGICfBG3iT2cKGYQ6hK4sJWzZ6or1C5v6GAAAAGQAJsOiAAAADQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAACgAAAAVIZWxsbwAAAAAAAAEAAAAMU29yb2JhbiBEb2NzAAAAAAAAAAELm/oYAAAAQATr6Ghp/DNO7S6JjEFwcJ9a+dvI6NJr7I/2eQttvoovjQ8te4zKKaapC3mbmx6ld6YKL5T81mxs45TjzdG5zw0=";
        let request = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "method": "sendTransaction",
          "params": {
            "transaction": tx_xdr,
          }
        }
                    );
        let hash = "d8ec9b68780314ffdfdfc2194b1b35dd27d7303c3bceaef6447e31631a1419dc";
        let response = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "result": {
            "status": "TRY_AGAIN_LATER",
            "hash": hash,
            "latestLedger": 2553978,
            "latestLedgerCloseTime": "1700159337"
          }
        }
            );

        let network = Networks::testnet();
        let tx = Transaction::from_xdr_envelope(tx_xdr, network);
        let xdr = tx
            .to_envelope()
            .unwrap()
            .to_xdr_base64(Limits::none())
            .unwrap();

        assert_eq!(xdr, tx_xdr);

        let (s, _m) = get_mocked_server(request, response).await;
        let txresult = s.send_transaction(tx).await.unwrap();
        assert_eq!(txresult.status, SendTransactionStatus::TryAgainLater);
        assert_eq!(txresult.hash, hash);
        assert_eq!(txresult.latest_ledger, 2553978);
        assert_eq!(txresult.latest_ledger_close_time, "1700159337");
        assert_eq!(txresult.to_error_result(), None);
        assert_eq!(txresult.to_diagnostic_events(), None);
    }

    /*
     * Error transaction
     */
    {
        let tx_xdr = "AAAAAgAAAAAg4dbAxsGAGICfBG3iT2cKGYQ6hK4sJWzZ6or1C5v6GAAAAGQAAAAAAAAACgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAACgAAAAVIZWxsbwAAAAAAAAEAAAAMU29yb2JhbiBEb2NzAAAAAAAAAAELm/oYAAAAQMQkfl8sdCYQIOdJB0TyazJ126y2TFRjL8yNHSb4TTsH5Ym6qM6gkTx1ENRZ0PFprVGusMTHISzdPHYJ4njBZAQ=";
        let request = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "method": "sendTransaction",
          "params": {
            "transaction": tx_xdr,
          }
        }
                            );
        let hash = "84a5f62bff422581dda019811daed0868a3db41833ad6e90a12f0d7db1be8167";
        let response = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "result": {
            "errorResultXdr": "AAAAAAAAAGT////7AAAAAA==",
            "status": "ERROR",
            "hash": hash,
            "latestLedger": 2553978,
            "latestLedgerCloseTime": "1700159337"
          }
        }
                    );

        let network = Networks::testnet();
        let tx = Transaction::from_xdr_envelope(tx_xdr, network);
        let xdr = tx
            .to_envelope()
            .unwrap()
            .to_xdr_base64(Limits::none())
            .unwrap();

        assert_eq!(xdr, tx_xdr);

        let (s, _m) = get_mocked_server(request, response).await;
        let txresult = s.send_transaction(tx).await.unwrap();
        assert_eq!(txresult.status, SendTransactionStatus::Error);
        assert_eq!(txresult.hash, hash);
        assert_eq!(txresult.latest_ledger, 2553978);
        assert_eq!(txresult.latest_ledger_close_time, "1700159337");
        assert_eq!(txresult.to_diagnostic_events(), None);

        let tx_error = TransactionResult {
            fee_charged: 100,
            result: TransactionResultResult::TxBadSeq,
            ext: stellar_baselib::xdr::TransactionResultExt::V0,
        };
        assert_eq!(txresult.to_error_result(), Some(tx_error));
    }
}

#[tokio::test]
async fn prepare_transaction() {
    {
        let tx_xdr = "AAAAAgAAAAAg4dbAxsGAGICfBG3iT2cKGYQ6hK4sJWzZ6or1C5v6GAAAAGQAJsOiAAAAEgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAGAAAAAAAAAABzAP+dP0PsNzYvFF1pv7a8RQXwH5eg3uZBbbWjE9PwAsAAAAJaW5jcmVtZW50AAAAAAAAAgAAABIAAAAAAAAAACDh1sDGwYAYgJ8EbeJPZwoZhDqEriwlbNnqivULm/oYAAAAAwAAAAMAAAAAAAAAAAAAAAA=";
        let request = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "method": "simulateTransaction",
          "params": {
            "transaction": tx_xdr,
          }
        }
        );
        let response = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "result": {
            "transactionData": "AAAAAAAAAAIAAAAGAAAAAcwD/nT9D7Dc2LxRdab+2vEUF8B+XoN7mQW21oxPT8ALAAAAFAAAAAEAAAAHy8vNUZ8vyZ2ybPHW0XbSrRtP7gEWsJ6zDzcfY9P8z88AAAABAAAABgAAAAHMA/50/Q+w3Ni8UXWm/trxFBfAfl6De5kFttaMT0/ACwAAABAAAAABAAAAAgAAAA8AAAAHQ291bnRlcgAAAAASAAAAAAAAAAAg4dbAxsGAGICfBG3iT2cKGYQ6hK4sJWzZ6or1C5v6GAAAAAEAHfKyAAAFiAAAAIgAAAAAAAAAAw==",
            "minResourceFee": "90353",
            "events": [
              "AAAAAQAAAAAAAAAAAAAAAgAAAAAAAAADAAAADwAAAAdmbl9jYWxsAAAAAA0AAAAgzAP+dP0PsNzYvFF1pv7a8RQXwH5eg3uZBbbWjE9PwAsAAAAPAAAACWluY3JlbWVudAAAAAAAABAAAAABAAAAAgAAABIAAAAAAAAAACDh1sDGwYAYgJ8EbeJPZwoZhDqEriwlbNnqivULm/oYAAAAAwAAAAM=",
              "AAAAAQAAAAAAAAABzAP+dP0PsNzYvFF1pv7a8RQXwH5eg3uZBbbWjE9PwAsAAAACAAAAAAAAAAIAAAAPAAAACWZuX3JldHVybgAAAAAAAA8AAAAJaW5jcmVtZW50AAAAAAAAAwAAAAw="
            ],
            "results": [
              {
                "auth": [],
                "xdr": "AAAAAwAAAAw="
              }
            ],
            "cost": {
              "cpuInsns": "1635562",
              "memBytes": "1295756"
            },
            "latestLedger": 2552139
          }
        }
        );
        let mut source_account = Account::new(
            "GAQODVWAY3AYAGEAT4CG3YSPM4FBTBB2QSXCYJLM3HVIV5ILTP5BRXCD",
            "10911149667123217",
        )
        .unwrap();
        let network = Networks::testnet();
        let time_bounds = TimeBounds {
            min_time: TimePoint(0),
            max_time: TimePoint(0),
        };

        let contract =
            Contracts::new("CDGAH7TU7UH3BXGYXRIXLJX63LYRIF6APZPIG64ZAW3NNDCPJ7AAWVTZ").unwrap();
        let op = contract.call(
            "increment",
            Some(vec![
                Address::new("GAQODVWAY3AYAGEAT4CG3YSPM4FBTBB2QSXCYJLM3HVIV5ILTP5BRXCD")
                    .unwrap()
                    .to_sc_val()
                    .unwrap(),
                ScVal::U32(3),
            ]),
        );

        let mut tx_builder =
            TransactionBuilder::new(&mut source_account, network, Some(time_bounds));
        tx_builder.add_operation(op);
        tx_builder.fee(100u32);

        let tx = tx_builder.build();
        dbg!(&tx);
        let xdr = tx
            .to_envelope()
            .unwrap()
            .to_xdr_base64(Limits::none())
            .unwrap();
        assert_eq!(xdr, tx_xdr);

        let (s, _m) = get_mocked_server(request, response).await;

        let simulation = s.simulate_transaction(&tx, None).await.unwrap();
        let txresult = s.prepare_transaction(&tx).await.unwrap();

        assert_eq!(txresult.fee, tx.fee + 90353);
        assert_eq!(txresult.soroban_data, simulation.to_transaction_data());
    }
    {
        let mut source_account = Account::new(
            "GAQODVWAY3AYAGEAT4CG3YSPM4FBTBB2QSXCYJLM3HVIV5ILTP5BRXCD",
            "10911149667123217",
        )
        .unwrap();
        let network = Networks::testnet();
        let time_bounds = TimeBounds {
            min_time: TimePoint(0),
            max_time: TimePoint(0),
        };

        let contract =
            Contracts::new("CDGAH7TU7UH3BXGYXRIXLJX63LYRIF6APZPIG64ZAW3NNDCPJ7AAWVTZ").unwrap();
        let op = contract.call(
            "increment",
            Some(vec![
                Address::new("GAQODVWAY3AYAGEAT4CG3YSPM4FBTBB2QSXCYJLM3HVIV5ILTP5BRXCD")
                    .unwrap()
                    .to_sc_val()
                    .unwrap(),
                ScVal::U32(3),
            ]),
        );

        /*
         * This is some fake data
         */
        let key_to_restore = ScVal::Vec(Some(ScVec(
            [ScVal::Symbol("to_restore".try_into().unwrap())]
                .try_into()
                .unwrap(),
        )));
        let contract_data = LedgerKey::ContractData(LedgerKeyContractData {
            contract: contract.address().to_sc_address().unwrap(),
            key: key_to_restore,
            durability: stellar_baselib::xdr::ContractDataDurability::Persistent,
        });
        let soroban_data = SorobanTransactionData {
            ext: stellar_baselib::xdr::SorobanTransactionDataExt::V0,
            resources: SorobanResources {
                footprint: stellar_baselib::xdr::LedgerFootprint {
                    read_only: Vec::new().try_into().unwrap(),
                    read_write: vec![contract_data].try_into().unwrap(),
                },
                instructions: 0,
                disk_read_bytes: 0,
                write_bytes: 0,
            },
            resource_fee: 0,
        };
        let restore_data_xdr = soroban_data.to_xdr_base64(Limits::none()).unwrap();

        let tx_xdr = "AAAAAgAAAAAg4dbAxsGAGICfBG3iT2cKGYQ6hK4sJWzZ6or1C5v6GAAAAGQAJsOiAAAAEgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAGAAAAAAAAAABzAP+dP0PsNzYvFF1pv7a8RQXwH5eg3uZBbbWjE9PwAsAAAAJaW5jcmVtZW50AAAAAAAAAgAAABIAAAAAAAAAACDh1sDGwYAYgJ8EbeJPZwoZhDqEriwlbNnqivULm/oYAAAAAwAAAAMAAAAAAAAAAAAAAAA=";
        let request = json!(
                {
                  "jsonrpc": "2.0",
                  "id": 1,
                  "method": "simulateTransaction",
                  "params": {
                    "transaction": tx_xdr,
                  }
                }
                /*
                    "resourceConfig": {
                      "instructionLeeway": 3000000
                    }
        */
                                );
        let response = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "result": {
            "transactionData": "AAAAAAAAAAIAAAAGAAAAAcwD/nT9D7Dc2LxRdab+2vEUF8B+XoN7mQW21oxPT8ALAAAAFAAAAAEAAAAHy8vNUZ8vyZ2ybPHW0XbSrRtP7gEWsJ6zDzcfY9P8z88AAAABAAAABgAAAAHMA/50/Q+w3Ni8UXWm/trxFBfAfl6De5kFttaMT0/ACwAAABAAAAABAAAAAgAAAA8AAAAHQ291bnRlcgAAAAASAAAAAAAAAAAg4dbAxsGAGICfBG3iT2cKGYQ6hK4sJWzZ6or1C5v6GAAAAAEAHfKyAAAFiAAAAIgAAAAAAAAAAw==",
            "minResourceFee": "90353",
            "events": [
              "AAAAAQAAAAAAAAAAAAAAAgAAAAAAAAADAAAADwAAAAdmbl9jYWxsAAAAAA0AAAAgzAP+dP0PsNzYvFF1pv7a8RQXwH5eg3uZBbbWjE9PwAsAAAAPAAAACWluY3JlbWVudAAAAAAAABAAAAABAAAAAgAAABIAAAAAAAAAACDh1sDGwYAYgJ8EbeJPZwoZhDqEriwlbNnqivULm/oYAAAAAwAAAAM=",
              "AAAAAQAAAAAAAAABzAP+dP0PsNzYvFF1pv7a8RQXwH5eg3uZBbbWjE9PwAsAAAACAAAAAAAAAAIAAAAPAAAACWZuX3JldHVybgAAAAAAAA8AAAAJaW5jcmVtZW50AAAAAAAAAwAAAAw="
            ],
            "results": [
              {
                "auth": [],
                "xdr": "AAAAAwAAAAw="
              }
            ],
            "restorePreamble": {
                "minResourceFee" : "12345",
                "transactionData": restore_data_xdr,
            },
            "cost": {
              "cpuInsns": "1635562",
              "memBytes": "1295756"
            },
            "latestLedger": 2552139
          }
        }
                );

        let mut tx_builder =
            TransactionBuilder::new(&mut source_account, network, Some(time_bounds));
        tx_builder.add_operation(op);
        tx_builder.fee(100u32);

        let tx = tx_builder.build();
        let xdr = tx
            .to_envelope()
            .unwrap()
            .to_xdr_base64(Limits::none())
            .unwrap();
        assert_eq!(xdr, tx_xdr);

        let (s, _m) = get_mocked_server(request, response).await;
        let txresult = s.prepare_transaction(&tx).await;

        if let Err(Error::RestorationRequired(min_fee, transaction_data)) = txresult {
            assert_eq!(min_fee, 12345);
            assert_eq!(transaction_data, soroban_data);
        } else {
            panic!("Expecting a restore preamble")
        }
    }
}

#[tokio::test]
async fn get_events() {
    /*
     *
     */
    {
        let request = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "method": "getEvents",
          "params": {
            "startLedger": 1190000,
            "filters": [
              {
                "type": "contract",
                "contractIds": [
                  "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC"
                ],
                "topics": [
                  [
                    "AAAADwAAAAh0cmFuc2Zlcg==",
                    "*",
                    "*",
                    "*"
                  ]
                ]
              }
            ],
            "pagination": {
              "limit": 2
            }
          }
        }
                );

        let response = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "result": {
            "events": [
              {
                "type": "contract",
                "ledger": 1192402,
                "ledgerClosedAt": "2024-04-19T06:19:00Z",
                "contractId": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
                "id": "0005121327593697280-0000000002",
                "pagingToken": "0005121327593697280-0000000002",
                "topic": [
                  "AAAADwAAAAh0cmFuc2Zlcg==",
                  "AAAAEgAAAAAAAAAA/ItF3cwsGhkee6lL5X6kicifdluxtsuDq9J/F0zou6Q=",
                  "AAAAEgAAAAAAAAAA2uSCoRk2StNEYAmWOhGXKQg0SLV6avaiN7TdV/uMozQ=",
                  "AAAADgAAAAZuYXRpdmUAAA=="
                ],
                "value": "AAAACgAAAAAAAAAAAAAAAAX14QA=",
                "inSuccessfulContractCall": true,
                "txHash": "bd8a6611a3b44ff7dce76a27fb32fa69c9f6eff3435ca5296f6035d5105617ed",
                "operationIndex": 0,
                "transactionIndex": 1
              },
              {
                "type": "contract",
                "ledger": 1192438,
                "ledgerClosedAt": "2024-04-19T06:22:14Z",
                "contractId": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
                "id": "0005121482212511744-0000000002",
                "pagingToken": "0005121482212511744-0000000002",
                "topic": [
                  "AAAADwAAAAh0cmFuc2Zlcg==",
                  "AAAAEgAAAAAAAAAA/ItF3cwsGhkee6lL5X6kicifdluxtsuDq9J/F0zou6Q=",
                  "AAAAEgAAAAAAAAAA2uSCoRk2StNEYAmWOhGXKQg0SLV6avaiN7TdV/uMozQ=",
                  "AAAADgAAAAZuYXRpdmUAAA=="
                ],
                "value": "AAAACgAAAAAAAAAAAAAAAACYloA=",
                "inSuccessfulContractCall": true,
                "txHash": "3fa1787085a4aa585507751394d70494a02e71d347446095a763ecbfc647937c",
                "operationIndex": 0,
                "transactionIndex": 1
              }
            ],
            "latestLedger": 122834,
            "oldestLedger": 1875,
            "latestLedgerCloseTime": "1755807188",
            "oldestLedgerCloseTime":"1755201565"
          }
        }
                );

        let (s, _m) = get_mocked_server(request, response).await;

        let ledger = crate::Pagination::From(1190000);
        let contract_id = "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC";
        let filters = vec![EventFilter::new(crate::soroban_rpc::EventType::Contract)
            .contract(contract_id)
            .topic(vec![
                Topic::Val(ScVal::Symbol(ScSymbol("transfer".try_into().unwrap()))),
                Topic::Any,
                Topic::Any,
                Topic::Any,
            ])];
        let result = s.get_events(ledger, filters, 2).await.unwrap();

        assert_eq!(result.latest_ledger, 122834);
        assert_eq!(result.oldest_ledger.unwrap(), 1875);
        assert_eq!(result.latest_ledger_close_time.unwrap(), "1755807188");
        assert_eq!(result.oldest_ledger_close_time.unwrap(), "1755201565");
        let events = result.events;
        assert_eq!(events.len(), 2);
        assert_eq!(events[0].contract_id, contract_id);
        assert_eq!(events[0].ledger, 1192402);
        assert_eq!(
            events[0].topic(),
            vec![
                ScVal::Symbol(ScSymbol("transfer".try_into().unwrap())),
                Address::new("GD6IWRO5ZQWBUGI6POUUXZL6USE4RH3WLOY3NS4DVPJH6F2M5C52IYAI")
                    .unwrap()
                    .to_sc_val()
                    .unwrap(),
                Address::new("GDNOJAVBDE3EVU2EMAEZMOQRS4UQQNCIWV5GV5VCG62N2V73RSRTJMP6")
                    .unwrap()
                    .to_sc_val()
                    .unwrap(),
                ScVal::String(ScString("native".try_into().unwrap()))
            ]
        );
        assert_eq!(
            events[0].value(),
            ScVal::I128(Int128Parts {
                hi: 0,
                lo: 100000000
            })
        );

        assert_eq!(
            events[1].topic(),
            vec![
                ScVal::Symbol(ScSymbol("transfer".try_into().unwrap())),
                Address::new("GD6IWRO5ZQWBUGI6POUUXZL6USE4RH3WLOY3NS4DVPJH6F2M5C52IYAI")
                    .unwrap()
                    .to_sc_val()
                    .unwrap(),
                Address::new("GDNOJAVBDE3EVU2EMAEZMOQRS4UQQNCIWV5GV5VCG62N2V73RSRTJMP6")
                    .unwrap()
                    .to_sc_val()
                    .unwrap(),
                ScVal::String(ScString("native".try_into().unwrap()))
            ]
        );
        assert_eq!(events[1].contract_id, contract_id);
        assert_eq!(events[1].ledger, 1192438);
        assert_eq!(
            events[1].value(),
            ScVal::I128(Int128Parts {
                hi: 0,
                lo: 10000000
            })
        );
    }
    /*
     * Too much contracts
     */
    {
        let request = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "method": "getEvents",
          "params": {
            "startLedger": 1190000,
            "filters": [
              {
                "type": "contract",
                "contractIds": [
                  "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
                  "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
                  "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
                  "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
                  "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
                  "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
                ],
              }
            ],
            "pagination": {
              "limit": 2
            }
          }
        }
                );
        let response = json!(
            {
              "jsonrpc": "2.0",
              "id": 1,
        "error":      {
            "code": -32602,
            "message": "filter 1 invalid: maximum 5 contract IDs per filter",
        },
        });

        let (s, _m) = get_mocked_server(request, response).await;

        let ledger = crate::Pagination::From(1190000);
        let contract_id = "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC";
        let filters = vec![EventFilter::new(crate::soroban_rpc::EventType::Contract)
            .contract(contract_id)
            .contract(contract_id)
            .contract(contract_id)
            .contract(contract_id)
            .contract(contract_id)
            .contract(contract_id)];
        let result = s.get_events(ledger, filters, 2).await;

        if let Err(Error::RPCError { code, message }) = result {
            assert_eq!(code, -32602);
            assert_eq!(
                message,
                "filter 1 invalid: maximum 5 contract IDs per filter"
            );
        } else {
            panic!("Expecting an RPCError")
        }
    }
    // TODO more tests
}

#[tokio::test]
async fn get_fee_stats() {
    let request = json!({
      "jsonrpc": "2.0",
      "id": 1,
      "method": "getFeeStats"
    });
    let response = json!({
      "jsonrpc": "2.0",
      "id": 1,
      "result": {
        "sorobanInclusionFee": {
          "max": "210",
          "min": "100",
          "mode": "100",
          "p10": "100",
          "p20": "100",
          "p30": "100",
          "p40": "100",
          "p50": "100",
          "p60": "100",
          "p70": "100",
          "p80": "100",
          "p90": "120",
          "p95": "190",
          "p99": "200",
          "transactionCount": "10",
          "ledgerCount": 50
        },
        "inclusionFee": {
          "max": "100",
          "min": "100",
          "mode": "100",
          "p10": "100",
          "p20": "100",
          "p30": "100",
          "p40": "100",
          "p50": "100",
          "p60": "100",
          "p70": "100",
          "p80": "100",
          "p90": "100",
          "p95": "100",
          "p99": "100",
          "transactionCount": "7",
          "ledgerCount": 10
        },
        "latestLedger": 4519945
      }
    });

    let (s, _m) = get_mocked_server(request, response).await;
    let response = s.get_fee_stats().await.unwrap();

    assert_eq!(response.inclusion_fee.ledger_count, 10);
    assert_eq!(response.inclusion_fee.transaction_count, "7");
    assert_eq!(response.inclusion_fee.max, "100");
    assert_eq!(response.inclusion_fee.min, "100");
    assert_eq!(response.inclusion_fee.mode, "100");
    assert_eq!(response.inclusion_fee.p10, "100");
    assert_eq!(response.inclusion_fee.p20, "100");
    assert_eq!(response.inclusion_fee.p30, "100");
    assert_eq!(response.inclusion_fee.p40, "100");
    assert_eq!(response.inclusion_fee.p50, "100");
    assert_eq!(response.inclusion_fee.p60, "100");
    assert_eq!(response.inclusion_fee.p70, "100");
    assert_eq!(response.inclusion_fee.p80, "100");
    assert_eq!(response.inclusion_fee.p90, "100");
    assert_eq!(response.inclusion_fee.p95, "100");
    assert_eq!(response.inclusion_fee.p99, "100");

    assert_eq!(response.soroban_inclusion_fee.ledger_count, 50);
    assert_eq!(response.soroban_inclusion_fee.transaction_count, "10");
    assert_eq!(response.soroban_inclusion_fee.max, "210");
    assert_eq!(response.soroban_inclusion_fee.min, "100");
    assert_eq!(response.soroban_inclusion_fee.mode, "100");
    assert_eq!(response.soroban_inclusion_fee.p10, "100");
    assert_eq!(response.soroban_inclusion_fee.p20, "100");
    assert_eq!(response.soroban_inclusion_fee.p30, "100");
    assert_eq!(response.soroban_inclusion_fee.p40, "100");
    assert_eq!(response.soroban_inclusion_fee.p50, "100");
    assert_eq!(response.soroban_inclusion_fee.p60, "100");
    assert_eq!(response.soroban_inclusion_fee.p70, "100");
    assert_eq!(response.soroban_inclusion_fee.p80, "100");
    assert_eq!(response.soroban_inclusion_fee.p90, "120");
    assert_eq!(response.soroban_inclusion_fee.p95, "190");
    assert_eq!(response.soroban_inclusion_fee.p99, "200");
}

#[tokio::test]
async fn get_version_info() {
    let request = json!({
      "jsonrpc": "2.0",
      "id": 1,
      "method": "getVersionInfo"
    });
    let response = json!({
      "jsonrpc": "2.0",
      "id": 1,
      "result": {
        "version": "21.1.0",
        "commitHash": "fcd2f0523f04279bae4502f3e3fa00ca627e6f6a",
        "buildTimestamp": "2024-05-10T11:18:38",
        "captiveCoreVersion": "stellar-core 21.0.0.rc2 (c6f474133738ae5f6d11b07963ca841909210273)",
        "protocolVersion": 21
      }
    });

    let (s, _m) = get_mocked_server(request, response).await;
    let response = s.get_version_info().await.unwrap();

    assert_eq!(response.version, "21.1.0");
    assert_eq!(
        response.commit_hash,
        "fcd2f0523f04279bae4502f3e3fa00ca627e6f6a"
    );
    assert_eq!(response.protocol_version, 21);
}

#[tokio::test]
async fn request_airdrop() {
    /*
     * No friendbot on mainnet
     */
    {
        let request = json!({"method": "getNetwork"});
        let response = json!(
            {
                "jsonrpc": "2.0",
                "id": 1,
                "result": {
                    "passphrase": "Public Global Stellar Network ; September 2015",
                    "protocolVersion": 22,
            }
        }
        );
        let (s, _m) = get_mocked_server(request, response).await;
        let kp = Keypair::random().unwrap();
        let account_id = kp.public_key();
        let result = s.request_airdrop(&account_id).await;
        assert!(matches!(result.err(), Some(Error::NoFriendbot)));
    }
    /*
     * Failed tx
     */
    {
        let account_id = "GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI";
        let friendly_server = get_friendbot_server(account_id, json!({"successful": false})).await;
        let uri = friendly_server.uri();
        let request = json!({"method": "getNetwork"});
        let response = json!(
            {
                "jsonrpc": "2.0",
                "id": 1,
                "result": {
                    "friendbotUrl": uri,
                    "passphrase": "Test SDF Network ; September 2015",
                    "protocolVersion": 20
                }
            }
        );
        let (s, _m) = get_mocked_server(request, response).await;
        let result = s.request_airdrop(account_id).await;
        assert!(matches!(result.err(), Some(Error::AccountNotFound)));
    }
    /*
     * Found url from testnet
     */
    {
        let account_id = "GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI";

        let friendly_server = get_friendbot_server(account_id, json!({"successful": true})).await;
        let uri = friendly_server.uri();
        let request = json!({"method": "getNetwork"});
        let response = json!(
            {
                "jsonrpc": "2.0",
                "id": 1,
                "result": {
                    "friendbotUrl": uri,
                    "passphrase": "Test SDF Network ; September 2015",
                    "protocolVersion": 20
                }
            }
        );
        let (s, mock_server) = get_mocked_server(request, response).await;

        let account_id_xdr = Keypair::from_public_key(account_id)
            .expect("Should not fail")
            .xdr_account_id();
        let key = LedgerKey::Account(LedgerKeyAccount {
            account_id: account_id_xdr,
        });
        let account_entry = "AAAAAAAAAABzdv3ojkzWHMD7KUoXhrPx0GH18vHKV0ZfqpMiEblG1gAAAFwVZH3YAAABdgAAAQgAAAAFAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAOZYQAAAAAaJsIJQ==";

        let value = base64::prelude::BASE64_STANDARD.encode(key.to_xdr(Limits::none()).unwrap());
        let request = json!(
            {
                "jsonrpc": "2.0",
                "id": 1,
                "method": "getLedgerEntries",
                "params": { "keys": [value] },
        }
        );
        let response = json!(
            {
                "jsonrpc": "2.0",
                "id": 1,
                "result": {
                "entries": [
                {
                    "key": value,
                    "xdr": account_entry,
                    "lastModifiedLedgerSeq": 2552504
                }
            ],
                "latestLedger": 2552990
            }
        }
        );
        let response = ResponseTemplate::new(200).set_body_json(response);
        Mock::given(method("POST"))
            .and(path("/"))
            .and(matchers::body_partial_json(request))
            .respond_with(response)
            .expect(1..)
            .mount(&mock_server)
            .await;

        let result = s.request_airdrop(account_id).await.unwrap();
        assert_eq!(result.sequence_number(), "1606317768968");
        assert_eq!(result.account_id(), account_id);
    }
}
#[tokio::test]
async fn get_transactions() {
    {
        //
        let request = json!(
        {
          "jsonrpc": "2.0",
          "method": "getTransactions",
          "params": {
            "startLedger": 1888539,
            "pagination": {
              "limit": 5
            }
          }
        }
            );
        let response = json!(
        {
          "jsonrpc": "2.0",
          "id": 1,
          "result": {
            "transactions": [
              {
                "status": "FAILED",
                "applicationOrder": 1,
                "feeBump": false,
                "envelopeXdr": "AAAAAgAAAACDz21Q3CTITlGqRus3/96/05EDivbtfJncNQKt64BTbAAAASwAAKkyAAXlMwAAAAEAAAAAAAAAAAAAAABmWeASAAAAAQAAABR3YWxsZXQ6MTcxMjkwNjMzNjUxMAAAAAEAAAABAAAAAIPPbVDcJMhOUapG6zf/3r/TkQOK9u18mdw1Aq3rgFNsAAAAAQAAAABwOSvou8mtwTtCkysVioO35TSgyRir2+WGqO8FShG/GAAAAAFVQUgAAAAAAO371tlrHUfK+AvmQvHje1jSUrvJb3y3wrJ7EplQeqTkAAAAAAX14QAAAAAAAAAAAeuAU2wAAABAn+6A+xXvMasptAm9BEJwf5Y9CLLQtV44TsNqS8ocPmn4n8Rtyb09SBiFoMv8isYgeQU5nAHsIwBNbEKCerusAQ==",
                "resultXdr": "AAAAAAAAAGT/////AAAAAQAAAAAAAAAB////+gAAAAA=",
                "resultMetaXdr": "AAAAAwAAAAAAAAACAAAAAwAc0RsAAAAAAAAAAIPPbVDcJMhOUapG6zf/3r/TkQOK9u18mdw1Aq3rgFNsAAAAF0YpYBQAAKkyAAXlMgAAAAsAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAMAAAAAABzRGgAAAABmWd/VAAAAAAAAAAEAHNEbAAAAAAAAAACDz21Q3CTITlGqRus3/96/05EDivbtfJncNQKt64BTbAAAABdGKWAUAACpMgAF5TMAAAALAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAc0RsAAAAAZlnf2gAAAAAAAAAAAAAAAAAAAAA=",
                "ledger": 1888539,
                "createdAt": 1717166042
              },
              {
                "status": "SUCCESS",
                "applicationOrder": 2,
                "feeBump": false,
                "envelopeXdr": "AAAAAgAAAAC4EZup+ewCs/doS3hKbeAa4EviBHqAFYM09oHuLtqrGAAPQkAAGgQZAAAANgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAABB90WssODNIgi6BHveqzxTRmIpvAFRyVNM+Hm2GVuCcAAAAAAAAAAAq6aHAHZ2sd9aPbRsskrlXMLWIwqs4Sv2Bk+VwuIR+9wAAABdIdugAAAAAAAAAAAIu2qsYAAAAQERzKOqYYiPXNwsiL8ADAG/f45RBssmf3umGzw4qKkLGlObuPdX0buWmTGrhI13SG38F2V8Mp9DI+eDkcCjMSAOGVuCcAAAAQHnm0o/r+Gsl+6oqBgSbqoSY37gflvQB3zZRghuir0N75UVerd0Q50yG5Zfu08i2crhx6uk+5HYTl8/Sa7uZ+Qc=",
                "resultXdr": "AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA=",
                "resultMetaXdr": "AAAAAwAAAAAAAAACAAAAAwAc0RsAAAAAAAAAALgRm6n57AKz92hLeEpt4BrgS+IEeoAVgzT2ge4u2qsYAAAAADwzS2gAGgQZAAAANQAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAMAAAAAABzPVAAAAABmWdZ2AAAAAAAAAAEAHNEbAAAAAAAAAAC4EZup+ewCs/doS3hKbeAa4EviBHqAFYM09oHuLtqrGAAAAAA8M0toABoEGQAAADYAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAc0RsAAAAAZlnf2gAAAAAAAAABAAAAAwAAAAMAHNEaAAAAAAAAAAAQfdFrLDgzSIIugR73qs8U0ZiKbwBUclTTPh5thlbgnABZJUSd0V2hAAAAawAAAlEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAaBGEAAAAAZkspCwAAAAAAAAABABzRGwAAAAAAAAAAEH3Rayw4M0iCLoEe96rPFNGYim8AVHJU0z4ebYZW4JwAWSUtVVp1oQAAAGsAAAJRAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAwAAAAAAGgRhAAAAAGZLKQsAAAAAAAAAAAAc0RsAAAAAAAAAACrpocAdnax31o9tGyySuVcwtYjCqzhK/YGT5XC4hH73AAAAF0h26AAAHNEbAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
                "ledger": 1888539,
                "createdAt": 1717166042
              },
              {
                "status": "SUCCESS",
                "applicationOrder": 3,
                "feeBump": false,
                "envelopeXdr": "AAAAAgAAAACwtG/IRC5DZE1UdekijEsoQEPM/uOwZ3iY/Y8UZ3b9xAAPQkAAGgRHAAAANgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAABB90WssODNIgi6BHveqzxTRmIpvAFRyVNM+Hm2GVuCcAAAAAAAAAADgdupKeB04lazKXCOb+E1JfxaM3tI4Xsb/qDa1MWOvXgAAABdIdugAAAAAAAAAAAJndv3EAAAAQKcTimw6KKcM0AeCMxXJcEK/hS9ROoj/qpMFppGNAr4W3ifSOSTGAFbA+cIVHmaV4p7xGcR+9JnUN1YjamvJZwSGVuCcAAAAQK9Cp775JbnYA793SXkkWWbmvnEFTiDPiFyTHxTphCwBDB1zqkXqGG6Q5O3dAyqkNJvj1XNRDsmY4pKV41qijQU=",
                "resultXdr": "AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAA=",
                "resultMetaXdr": "AAAAAwAAAAAAAAACAAAAAwAc0RsAAAAAAAAAALC0b8hELkNkTVR16SKMSyhAQ8z+47BneJj9jxRndv3EAAAAADwzS2gAGgRHAAAANQAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAMAAAAAABzPVAAAAABmWdZ2AAAAAAAAAAEAHNEbAAAAAAAAAACwtG/IRC5DZE1UdekijEsoQEPM/uOwZ3iY/Y8UZ3b9xAAAAAA8M0toABoERwAAADYAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAc0RsAAAAAZlnf2gAAAAAAAAABAAAAAwAAAAMAHNEbAAAAAAAAAAAQfdFrLDgzSIIugR73qs8U0ZiKbwBUclTTPh5thlbgnABZJS1VWnWhAAAAawAAAlEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAaBGEAAAAAZkspCwAAAAAAAAABABzRGwAAAAAAAAAAEH3Rayw4M0iCLoEe96rPFNGYim8AVHJU0z4ebYZW4JwAWSUWDOONoQAAAGsAAAJRAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAwAAAAAAGgRhAAAAAGZLKQsAAAAAAAAAAAAc0RsAAAAAAAAAAOB26kp4HTiVrMpcI5v4TUl/Foze0jhexv+oNrUxY69eAAAAF0h26AAAHNEbAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
                "ledger": 1888539,
                "createdAt": 1717166042
              },
              {
                "status": "SUCCESS",
                "applicationOrder": 4,
                "feeBump": false,
                "envelopeXdr": "AAAAAgAAAACxMt2gKYOehEoVbmh9vfvZ4mVzXFSNTbAU5S4a8zorrAA4wrwAHLqRAAAADAAAAAAAAAAAAAAAAQAAAAAAAAAYAAAAAQAAAAAAAAAAAAAAALEy3aApg56EShVuaH29+9niZXNcVI1NsBTlLhrzOiusz3K+BVgRzXig/Bhz1TL5Qy+Ibv6cDvCfdaAtBMMFPcYAAAAAHXUVmJM11pdJSKKV52UJrVYlvxaPLmmg17nMe0HGy0MAAAABAAAAAAAAAAEAAAAAAAAAAAAAAACxMt2gKYOehEoVbmh9vfvZ4mVzXFSNTbAU5S4a8zorrM9yvgVYEc14oPwYc9Uy+UMviG7+nA7wn3WgLQTDBT3GAAAAAB11FZiTNdaXSUiiledlCa1WJb8Wjy5poNe5zHtBxstDAAAAAAAAAAEAAAAAAAAAAQAAAAcddRWYkzXWl0lIopXnZQmtViW/Fo8uaaDXucx7QcbLQwAAAAEAAAAGAAAAAbolCtTsMrJvK0M2SaskFsaMajj3iAZbXxELZHwDyE5dAAAAFAAAAAEABf2jAAAd1AAAAGgAAAAAADjCWAAAAAHzOiusAAAAQM+qaiMKxMoCVNjdRIh3X9CSxkjAm0BpXYDB9Fd+DS0guYKiY3TMaVe243UB008iBn5ynQv724rReXlg7iFqXQA=",
                "resultXdr": "AAAAAAAw3cUAAAAAAAAAAQAAAAAAAAAYAAAAAKg/pGuhtOG27rIpG8xhUIp46CStGWOcsGlNsTQv44UOAAAAAA==",
                "resultMetaXdr": "AAAAAwAAAAAAAAACAAAAAwAc0RsAAAAAAAAAALEy3aApg56EShVuaH29+9niZXNcVI1NsBTlLhrzOiusAAAAFzJtlUYAHLqRAAAACwAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAMAAAAAABzRFAAAAABmWd+1AAAAAAAAAAEAHNEbAAAAAAAAAACxMt2gKYOehEoVbmh9vfvZ4mVzXFSNTbAU5S4a8zorrAAAABcybZVGABy6kQAAAAwAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAc0RsAAAAAZlnf2gAAAAAAAAABAAAAAgAAAAAAHNEbAAAACZ8OtTIDsshAKP7N/eZQd88TVRE6/Zndu5MpJWNEYJnfADx1GgAAAAAAAAAAABzRGwAAAAYAAAAAAAAAAbolCtTsMrJvK0M2SaskFsaMajj3iAZbXxELZHwDyE5dAAAAFAAAAAEAAAATAAAAAB11FZiTNdaXSUiiledlCa1WJb8Wjy5poNe5zHtBxstDAAAAAAAAAAAAAAACAAAAAwAc0RsAAAAAAAAAALEy3aApg56EShVuaH29+9niZXNcVI1NsBTlLhrzOiusAAAAFzJtlUYAHLqRAAAADAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAMAAAAAABzRGwAAAABmWd/aAAAAAAAAAAEAHNEbAAAAAAAAAACxMt2gKYOehEoVbmh9vfvZ4mVzXFSNTbAU5S4a8zorrAAAABcydXo9ABy6kQAAAAwAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAc0RsAAAAAZlnf2gAAAAAAAAABAAAAAQAAAAAAAAAAAADNgQAAAAAAMA/gAAAAAAAwDlkAAAAAAAAAEgAAAAG6JQrU7DKybytDNkmrJBbGjGo494gGW18RC2R8A8hOXQAAABMAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAPAAAADGNvcmVfbWV0cmljcwAAAA8AAAAKcmVhZF9lbnRyeQAAAAAABQAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAC3dyaXRlX2VudHJ5AAAAAAUAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAA8AAAAMY29yZV9tZXRyaWNzAAAADwAAABBsZWRnZXJfcmVhZF9ieXRlAAAABQAAAAAAAB3UAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAEWxlZGdlcl93cml0ZV9ieXRlAAAAAAAABQAAAAAAAABoAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAADXJlYWRfa2V5X2J5dGUAAAAAAAAFAAAAAAAAAFQAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAPAAAADGNvcmVfbWV0cmljcwAAAA8AAAAOd3JpdGVfa2V5X2J5dGUAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAA8AAAAMY29yZV9tZXRyaWNzAAAADwAAAA5yZWFkX2RhdGFfYnl0ZQAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAD3dyaXRlX2RhdGFfYnl0ZQAAAAAFAAAAAAAAAGgAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAPAAAADGNvcmVfbWV0cmljcwAAAA8AAAAOcmVhZF9jb2RlX2J5dGUAAAAAAAUAAAAAAAAd1AAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAA8AAAAMY29yZV9tZXRyaWNzAAAADwAAAA93cml0ZV9jb2RlX2J5dGUAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAACmVtaXRfZXZlbnQAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAA8AAAAMY29yZV9tZXRyaWNzAAAADwAAAA9lbWl0X2V2ZW50X2J5dGUAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAACGNwdV9pbnNuAAAABQAAAAAABTO4AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAACG1lbV9ieXRlAAAABQAAAAAAAPkDAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAEWludm9rZV90aW1lX25zZWNzAAAAAAAABQAAAAAAAmizAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAD21heF9yd19rZXlfYnl0ZQAAAAAFAAAAAAAAADAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAPAAAADGNvcmVfbWV0cmljcwAAAA8AAAAQbWF4X3J3X2RhdGFfYnl0ZQAAAAUAAAAAAAAAaAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAA8AAAAMY29yZV9tZXRyaWNzAAAADwAAABBtYXhfcndfY29kZV9ieXRlAAAABQAAAAAAAB3UAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAE21heF9lbWl0X2V2ZW50X2J5dGUAAAAABQAAAAAAAAAA",
                "diagnosticEventsXdr": [
                  "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAACnJlYWRfZW50cnkAAAAAAAUAAAAAAAAAAg==",
                  "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAC3dyaXRlX2VudHJ5AAAAAAUAAAAAAAAAAQ==",
                  "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAEGxlZGdlcl9yZWFkX2J5dGUAAAAFAAAAAAAAHdQ=",
                  "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAEWxlZGdlcl93cml0ZV9ieXRlAAAAAAAABQAAAAAAAABo",
                  "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAADXJlYWRfa2V5X2J5dGUAAAAAAAAFAAAAAAAAAFQ=",
                  "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAADndyaXRlX2tleV9ieXRlAAAAAAAFAAAAAAAAAAA=",
                  "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAADnJlYWRfZGF0YV9ieXRlAAAAAAAFAAAAAAAAAAA=",
                  "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAD3dyaXRlX2RhdGFfYnl0ZQAAAAAFAAAAAAAAAGg=",
                  "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAADnJlYWRfY29kZV9ieXRlAAAAAAAFAAAAAAAAHdQ=",
                  "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAD3dyaXRlX2NvZGVfYnl0ZQAAAAAFAAAAAAAAAAA=",
                  "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAACmVtaXRfZXZlbnQAAAAAAAUAAAAAAAAAAA==",
                  "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAD2VtaXRfZXZlbnRfYnl0ZQAAAAAFAAAAAAAAAAA=",
                  "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAACGNwdV9pbnNuAAAABQAAAAAABTO4",
                  "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAACG1lbV9ieXRlAAAABQAAAAAAAPkD",
                  "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAEWludm9rZV90aW1lX25zZWNzAAAAAAAABQAAAAAAAmiz",
                  "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAD21heF9yd19rZXlfYnl0ZQAAAAAFAAAAAAAAADA=",
                  "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAEG1heF9yd19kYXRhX2J5dGUAAAAFAAAAAAAAAGg=",
                  "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAEG1heF9yd19jb2RlX2J5dGUAAAAFAAAAAAAAHdQ=",
                  "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAADwAAAAxjb3JlX21ldHJpY3MAAAAPAAAAE21heF9lbWl0X2V2ZW50X2J5dGUAAAAABQAAAAAAAAAA"
                ],
                "ledger": 1888539,
                "createdAt": 1717166042
              },
              {
                "status": "FAILED",
                "applicationOrder": 1,
                "feeBump": false,
                "envelopeXdr": "AAAAAgAAAAAxLMEcxmfUgNzL687Js4sX/jmFQDqTo1Lj4KDoC1PeSQAehIAAAAIJAAtMUQAAAAEAAAAAAAAAAAAAAABmWeAVAAAAAQAAAAlwc3BiOjMyMTcAAAAAAAACAAAAAQAAAACKlutUN5GT3UOoE2BUkNtJEwoipGOinBFsQtXgpIZMxQAAAAEAAAAA433o+yremWU3t88cKpfpHR+JMFR44JHzmBGni6hqCEYAAAACQVRVQUgAAAAAAAAAAAAAAGfK1mN4mg51jbX6by6TWghGynQ463doEDgzriqZo9bzAAAAAAaOd4AAAAABAAAAAIqW61Q3kZPdQ6gTYFSQ20kTCiKkY6KcEWxC1eCkhkzFAAAAAQAAAADjfej7Kt6ZZTe3zxwql+kdH4kwVHjgkfOYEaeLqGoIRgAAAAJBVFVTRAAAAAAAAAAAAAAAZ8rWY3iaDnWNtfpvLpNaCEbKdDjrd2gQODOuKpmj1vMAAAAAADh1IAAAAAAAAAACC1PeSQAAAEBoad/kqj/4Sqq5tC6HyeMm5LJKM1VqKRGZc3e4uvA3ITThwn2nNMRJRegdQrLrPBTSgw51nY8npilXVIds7I0OpIZMxQAAAEDTZNaLjIDMWPDdCxa1ZB28vUxTcS/0xykOFTI/JAz096vX6Y7wI0QvnbPM7KCoL0cJAciD+pJxNqXQ2Aff1hoO",
                "resultXdr": "AAAAAAAAAMj/////AAAAAgAAAAAAAAAB////+wAAAAAAAAAB////+wAAAAA=",
                "resultMetaXdr": "AAAAAwAAAAAAAAACAAAAAwAc0RwAAAAAAAAAADEswRzGZ9SA3Mvrzsmzixf+OYVAOpOjUuPgoOgLU95JAAAAFxzxIbUAAAIJAAtMUAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAMAAAAAABzRGgAAAABmWd/VAAAAAAAAAAEAHNEcAAAAAAAAAAAxLMEcxmfUgNzL687Js4sX/jmFQDqTo1Lj4KDoC1PeSQAAABcc8SG1AAACCQALTFEAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAADAAAAAAAc0RwAAAAAZlnf3wAAAAAAAAAAAAAAAAAAAAA=",
                "ledger": 1888540,
                "createdAt": 1717166047
              }
            ],
            "latestLedger": 1888542,
            "latestLedgerCloseTimestamp": 1717166057,
            "oldestLedger": 1871263,
            "oldestLedgerCloseTimestamp": 1717075350,
            "cursor": "8111217537191937"
          }
        }
            );

        let (s, _m) = get_mocked_server(request, response).await;
        let response = s
            .get_transactions(Pagination::From(1888539), 5)
            .await
            .unwrap();
        assert_eq!(response.latest_ledger, 1888542);
        assert_eq!(response.latest_ledger_close_timestamp, 1717166057);
        assert_eq!(response.oldest_ledger, 1871263);
        assert_eq!(response.oldest_ledger_close_timestamp, 1717075350);
        assert_eq!(response.cursor, "8111217537191937");
        assert_eq!(response.transactions.len(), 5);

        let ts = response.transactions;
        assert_eq!(ts[0].status, TransactionStatus::Failed);
        assert_eq!(ts[0].application_order, Some(1));
        assert_eq!(ts[0].fee_bump, Some(false));
        assert_eq!(ts[0].ledger, Some(1888539));
        assert_eq!(ts[0].created_at, Some(1717166042));
        if let stellar_baselib::xdr::TransactionEnvelope::Tx(TransactionV1Envelope {
            tx,
            signatures: _,
        }) = ts[0].to_envelope().unwrap()
        {
            if let stellar_baselib::xdr::Memo::Text(text) = tx.memo {
                assert_eq!(text, "wallet:1712906336510".try_into().unwrap());
            } else {
                panic!("No memo")
            }
        } else {
            panic!("No enveloppe")
        }
        assert_eq!(ts[0].to_diagnostic_events(), None);

        assert_eq!(ts[3].status, TransactionStatus::Success);
        assert_eq!(ts[3].application_order, Some(4));
        assert_eq!(ts[3].fee_bump, Some(false));
        assert_eq!(ts[3].ledger, Some(1888539));
        assert_eq!(ts[3].created_at, Some(1717166042));
        let events = ts[3].to_diagnostic_events().unwrap();
        assert_eq!(events.len(), 19);

        let ContractEvent {
            ext: _,
            contract_id: _,
            type_,
            body,
        } = events[0].event.clone();

        let ContractEventBody::V0(ContractEventV0 { topics, data }) = body;
        //
        assert!(matches!(type_, ContractEventType::Diagnostic));
        assert_eq!(
            topics,
            vec![
                ScVal::Symbol(ScSymbol("core_metrics".try_into().unwrap())),
                ScVal::Symbol(ScSymbol("read_entry".try_into().unwrap()))
            ]
            .try_into()
            .unwrap()
        );
        assert_eq!(data, ScVal::U64(2));
    }
}
#[tokio::test]
async fn get_ledgers() {
    //
    let request = json!(
    {
      "jsonrpc": "2.0",
      "id": 1,
      "method": "getLedgers",
      "params": {
        "startLedger": 36233,
        "pagination": {
          "limit": 2
        }
      }
    }
                );
    let response = json!(
    {
      "jsonrpc": "2.0",
      "id": 1,
      "result": {
        "ledgers": [
          {
            "hash": "434de11b427aa4b6f8cda259ac2111a6aa148d2ab6b4c7affe864e94a9f4bd80",
            "sequence": 36233,
            "ledgerCloseTime": "1734032457",
            "headerXdr": "Q03hG0J6pLb4zaJZrCERpqoUjSq2tMev/oZOlKn0vYAAAAAWY6AkQmSfV+Rrnpq308LrTR1IZq7dUQr4HkNPlpDpw4/4WH/HGddpIrl1KZiCUKzFOi9sqkX+b56aTGNqhTRHtwAAAABnWzxJAAAAAAAAAAEAAAAAqCTNGLyddQZNKZpbW6ykO8OqLzJpOBU9jC+btctt8DMAAABAjksBzacOp2IR06utjCmZ0QR6kgidREHCceghZapmZdNrS1uBiBG8Zz5Eh7uxMga5IAHPVbkSgFgXzjwRk4i2At8/YZgEqS/bQFcZLcQ910jqd4rcUrxJjOgFJMAUuBEZkxHVMCvc1iIZj7nTD2d91KT24H9FpOIGJLSiTuwys+0AAI2JDeC2s6dkAAAAAAAIewDXaQAAAAAAAAAAAAAB3wAAAGQATEtAAAAAyESlcNBlpidDCvLHH8Nu1MWDHWwUXTfbvGKqvbvlzzrb2GJe+2UfU8rr8mWrJXRTcQhl5UYgKhOQUOWP1nWgbp4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
            "metadataXdr": "AAAAAQAAAABDTeEbQnqktvjNolmsIRGmqhSNKra0x6/+hk6UqfS9gAAAABZjoCRCZJ9X5GuemrfTwutNHUhmrt1RCvgeQ0+WkOnDj/hYf8cZ12kiuXUpmIJQrMU6L2yqRf5vnppMY2qFNEe3AAAAAGdbPEkAAAAAAAAAAQAAAACoJM0YvJ11Bk0pmltbrKQ7w6ovMmk4FT2ML5u1y23wMwAAAECOSwHNpw6nYhHTq62MKZnRBHqSCJ1EQcJx6CFlqmZl02tLW4GIEbxnPkSHu7EyBrkgAc9VuRKAWBfOPBGTiLYC3z9hmASpL9tAVxktxD3XSOp3itxSvEmM6AUkwBS4ERmTEdUwK9zWIhmPudMPZ33UpPbgf0Wk4gYktKJO7DKz7QAAjYkN4Lazp2QAAAAAAAh7ANdpAAAAAAAAAAAAAAHfAAAAZABMS0AAAADIRKVw0GWmJ0MK8scfw27UxYMdbBRdN9u8Yqq9u+XPOtvYYl77ZR9TyuvyZasldFNxCGXlRiAqE5BQ5Y/WdaBungAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFjoCRCZJ9X5GuemrfTwutNHUhmrt1RCvgeQ0+WkOnDjwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADVBewAAAAAAAAAAA=="
          },
          {
            "hash": "201f38ca43cfad09d00287ee70d77edc8c88a61e018683fd466e71688222a217",
            "sequence": 36234,
            "ledgerCloseTime": "1734032462",
            "headerXdr": "IB84ykPPrQnQAofucNd+3IyIph4BhoP9Rm5xaIIiohcAAAAWQ03hG0J6pLb4zaJZrCERpqoUjSq2tMev/oZOlKn0vYB8UE9ZJ4uLDnIu/g7+08D/eYnJjrJD07fGpWaVoLi29wAAAABnWzxOAAAAAAAAAAEAAAAAtV0Qsrt9KE7VwDpHpeG1kMkPV4NVRxzEUHu+ggTSPIsAAABAN6Cc3KxXPM2WmFDtY/yPZM0aha2yhnHFBDaliMqO6BzVQMb9Zlq5DoGrLr/xoFOp8YJ3RjLedF8w/KfN3ogPBuzRjPABveSt1CvvmDdnwUpSXyUsjdjECdbpjuKxaZZR+NeVPfeYT06qs2v4JwBff4WWpIGwEXPcxq3tZKuekH8AAI2KDeC2s6dkAAAAAAAIewDaiQAAAAAAAAAAAAAB3wAAAGQATEtAAAAAyESlcNBlpidDCvLHH8Nu1MWDHWwUXTfbvGKqvbvlzzrb2GJe+2UfU8rr8mWrJXRTcQhl5UYgKhOQUOWP1nWgbp4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
            "metadataXdr": " "
          }
        ],
        "latestLedger": 36379,
        "latestLedgerCloseTime": 1734033188,
        "oldestLedger": 29312,
        "oldestLedgerCloseTime": 1733997822,
        "cursor": "36234"
      }
    }
                );

    let (s, _m) = get_mocked_server(request, response).await;
    let response = s.get_ledgers(Pagination::From(36233), 2).await.unwrap();

    assert_eq!(response.latest_ledger, 36379);
    assert_eq!(response.latest_ledger_close_time, 1734033188);
    assert_eq!(response.oldest_ledger, 29312);
    assert_eq!(response.oldest_ledger_close_time, 1733997822);
    assert_eq!(response.ledgers.len(), 2);

    let l1 = response.ledgers[0].clone();
    let hash1 = "434de11b427aa4b6f8cda259ac2111a6aa148d2ab6b4c7affe864e94a9f4bd80";
    assert_eq!(l1.hash, hash1);
    assert_eq!(l1.sequence, 36233);
    assert_eq!(l1.ledger_close_time, "1734032457");

    if let Some(LedgerHeaderHistoryEntry {
        hash,
        header,
        ext: _,
    }) = l1.to_header()
    {
        assert_eq!(hash.0, hex::decode(hash1).unwrap().as_slice());
        assert_eq!(header.ledger_seq, 36233);
    } else {
        panic!("No header")
    }
    if let Some(LedgerCloseMeta::V1(LedgerCloseMetaV1 {
        ext: _,
        ledger_header: _,
        tx_set,
        tx_processing: _,
        upgrades_processing: _,
        scp_info: _,
        total_byte_size_of_live_soroban_state,
        evicted_keys: _,
        unused: _,
    })) = l1.to_metadata()
    {
        let GeneralizedTransactionSet::V1(TransactionSetV1 {
            previous_ledger_hash,
            phases: _,
        }) = tx_set;
        assert_eq!(
            previous_ledger_hash.0,
            hex::decode("63a02442649f57e46b9e9ab7d3c2eb4d1d4866aedd510af81e434f9690e9c38f")
                .unwrap()
                .as_slice()
        );
        assert_eq!(total_byte_size_of_live_soroban_state, 13960684);
    } else {
        panic!("No metadata")
    }

    let l2 = response.ledgers[1].clone();
    let hash2 = "201f38ca43cfad09d00287ee70d77edc8c88a61e018683fd466e71688222a217";
    assert_eq!(l2.hash, hash2);
    assert_eq!(l2.sequence, 36234);
    assert_eq!(l2.ledger_close_time, "1734032462");

    if let Some(LedgerHeaderHistoryEntry {
        hash,
        header,
        ext: _,
    }) = l2.to_header()
    {
        assert_eq!(hash.0, hex::decode(hash2).unwrap().as_slice());
        assert_eq!(header.ledger_seq, 36234);
    } else {
        panic!("No header")
    }
}

// Create a Server that will reply `response` for a json `request` partially matching
async fn get_mocked_server(
    request: serde_json::Value,
    response: serde_json::Value,
) -> (Server, MockServer) {
    let mock_server = MockServer::start().await;
    let server_url = mock_server.uri();

    let response = ResponseTemplate::new(200).set_body_json(response);
    Mock::given(method("POST"))
        .and(path("/"))
        .and(matchers::body_partial_json(request))
        .respond_with(response)
        .expect(1..)
        .mount(&mock_server)
        .await;

    let server = Server::new(
        &server_url,
        Options {
            allow_http: true,
            ..Default::default()
        },
    )
    .expect("Configuration should not fail");

    (server, mock_server)
}
async fn get_friendbot_server(account_id: &str, response: serde_json::Value) -> MockServer {
    let mock_server = MockServer::start().await;

    let response = ResponseTemplate::new(200).set_body_json(response);
    Mock::given(method("GET"))
        .and(path("/"))
        .and(query_param("addr", account_id))
        .respond_with(response)
        .expect(1..)
        .mount(&mock_server)
        .await;

    mock_server
}

#[tokio::test]
async fn native_check_balance_testnet() {
    let rpc = Server::new("https://soroban-testnet.stellar.org", Options::default()).unwrap();

    let native_id = "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC";
    let native_sac = Contracts::new(native_id).unwrap();

    let kp = Keypair::random().unwrap();
    let account = rpc.request_airdrop(&kp.public_key()).await.unwrap();

    let mut source_account = Account::new(&kp.public_key(), &account.sequence_number()).unwrap();

    let account_address = Address::new(&kp.public_key()).unwrap();
    let tx = TransactionBuilder::new(&mut source_account, Networks::testnet(), None)
        .fee(1000u32)
        .add_operation(native_sac.call("balance", Some(vec![account_address.to_sc_val().unwrap()])))
        .build();

    let response = rpc.simulate_transaction(&tx, None).await.unwrap();
    if let Some((ScVal::I128(Int128Parts { hi, lo }), _auth)) = response.to_result() {
        let balance = i128_from_pieces(hi, lo) / 10000000; // Divide to convert from stroops to XLM
        println!("Account {} has {} XLM", kp.public_key(), balance);
        assert_eq!(balance, 10000);
    } else {
        panic!("Failed")
    }
}
#[tokio::test]
async fn native_events_testnet() {
    let rpc = Server::new(
        "https://soroban-testnet.stellar.org",
        Options {
            timeout: 30,
            ..Default::default()
        },
    )
    .unwrap();

    let native_id = "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC";

    let response = rpc.get_latest_ledger().await.unwrap();
    let ledger = response.sequence;

    let transfer = ScVal::Symbol(ScSymbol("transfer".try_into().unwrap()));
    let native = ScVal::String(ScString("native".try_into().unwrap()));
    let events = rpc
        .get_events(
            crate::Pagination::From(ledger - 10),
            vec![EventFilter::new(crate::soroban_rpc::EventType::All)
                .contract(native_id)
                .topic(vec![
                    Topic::Val(transfer),
                    Topic::Any, // From account
                    Topic::Any, // To account
                    Topic::Val(native),
                ])],
            Some(3),
        )
        .await
        .unwrap();

    println!("{:?}", events);
}
#[tokio::test]
async fn get_version_info_testnet() {
    let rpc = Server::new(
        "https://soroban-testnet.stellar.org",
        Options {
            timeout: 30,
            ..Default::default()
        },
    )
    .unwrap();

    let version = rpc.get_version_info().await.unwrap();

    println!("{:?}", version);
}
