Repository: StellarCN/py-stellar-base (language: python)

Issue:
Add support for new fields in Soroban RPC's `sendTransaction` endpoint
## Soroban RPC Changes

With the introduction of [stellar/soroban-tools#1152](https://github.com/stellar/soroban-tools/pull/1152), the Soroban RPC server introduces the following changes:

### `sendTransaction`

The response will include a new, optional field in its response schema:

* `diagnosticEventsXdr: string[]` - If the transaction status is `"ERROR"`, this field may be present with an array of base64-encoded strings. Each string will decode to a raw `DiagnosticEvent` XDR struct containing details on why stellar-core rejected the transaction.

| SDKs should add support for this field. |
|:---------------------------------------:|

### References
 * The documentation PR: [soroban-docs#700](https://github.com/stellar/soroban-docs/pull/700).
 * An example implementation in JavaScript: [stellar-sdk#905](https://github.com/stellar/js-stellar-sdk/pull/905).


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f0ddabf4..53e0bfe3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@ Release History
 ### Pending
 #### Update
 - feat: add `scv.to_void` and `scv.from_void`. ([#863](https://github.com/StellarCN/py-stellar-base/pull/863))
+- feat: Support for the new, optional `diagnostic_events_xdr` field on the `SorobanServer.send_transaction` method. ([#866](https://github.com/stellar/java-stellar-sdk/pull/866))
 
 ### Version 9.1.3
 
diff --git a/stellar_sdk/soroban_rpc.py b/stellar_sdk/soroban_rpc.py
index 25f3c3b1..7c5e7f4c 100644
--- a/stellar_sdk/soroban_rpc.py
+++ b/stellar_sdk/soroban_rpc.py
@@ -299,6 +299,9 @@ class SendTransactionResponse(BaseModel):
     more information."""
 
     error_result_xdr: Optional[str] = Field(alias="errorResultXdr", default=None)
+    diagnostic_events_xdr: Optional[List[str]] = Field(
+        alias="diagnosticEventsXdr", default=None
+    )
     status: SendTransactionStatus = Field(alias="status")
     hash: str = Field(alias="hash")
     latest_ledger: int = Field(alias="latestLedger")


Test patch:
diff --git a/tests/test_soroban_server_async.py b/tests/test_soroban_server_async.py
index 81443d94..25767b8c 100644
--- a/tests/test_soroban_server_async.py
+++ b/tests/test_soroban_server_async.py
@@ -711,6 +711,38 @@ async def test_send_transaction(self):
         assert request_data["params"] == {"transaction": transaction.to_xdr()}
         assert result["hash"] == transaction.hash_hex()
 
+    async def test_send_transaction_error(self):
+        result = {
+            "status": "ERROR",
+            "errorResultXdr": "AAAAAAAAf67////6AAAAAA==",
+            "diagnosticEventsXdr": [
+                "AAAAAQAAAAAAAAAAAAAAAgAAAAAAAAADAAAADwAAAAdmbl9jYWxsAAAAAA0AAAAgr/p6gt6h8MrmSw+WNJnu3+sCP9dHXx7jR8IH0sG6Cy0AAAAPAAAABWhlbGxvAAAAAAAADwAAAAVBbG9oYQAAAA=="
+            ],
+            "hash": "64977cc4bb7f8bf75bdc47570548a994667899d3319b72f95cb2a64e567ad52c",
+            "latestLedger": "1479",
+            "latestLedgerCloseTime": "1690594566",
+        }
+        data = {
+            "jsonrpc": "2.0",
+            "id": "688dfcf3bcd04f52af4866e98dffe387",
+            "result": result,
+        }
+
+        transaction = _build_soroban_transaction(None, [])
+        with aioresponses() as m:
+            m.post(PRC_URL, payload=data)
+            async with SorobanServerAsync(PRC_URL) as client:
+                assert (
+                    await client.send_transaction(transaction)
+                ) == SendTransactionResponse.model_validate(result)
+
+        request_data = m.requests[("POST", URL(PRC_URL))][0].kwargs["json"]
+        assert len(request_data["id"]) == 32
+        assert request_data["jsonrpc"] == "2.0"
+        assert request_data["method"] == "sendTransaction"
+        assert request_data["params"] == {"transaction": transaction.to_xdr()}
+        assert result["hash"] == transaction.hash_hex()
+
     async def test_soroban_rpc_error_response_raise(self):
         data = {
             "jsonrpc": "2.0",
diff --git a/tests/test_soroban_server_sync.py b/tests/test_soroban_server_sync.py
index c3a9b3a0..6a80e0a6 100644
--- a/tests/test_soroban_server_sync.py
+++ b/tests/test_soroban_server_sync.py
@@ -689,6 +689,37 @@ def test_send_transaction(self):
         assert request_data["params"] == {"transaction": transaction.to_xdr()}
         assert result["hash"] == transaction.hash_hex()
 
+    def test_send_transaction_error(self):
+        result = {
+            "status": "ERROR",
+            "errorResultXdr": "AAAAAAAAf67////6AAAAAA==",
+            "diagnosticEventsXdr": [
+                "AAAAAQAAAAAAAAAAAAAAAgAAAAAAAAADAAAADwAAAAdmbl9jYWxsAAAAAA0AAAAgr/p6gt6h8MrmSw+WNJnu3+sCP9dHXx7jR8IH0sG6Cy0AAAAPAAAABWhlbGxvAAAAAAAADwAAAAVBbG9oYQAAAA=="
+            ],
+            "hash": "64977cc4bb7f8bf75bdc47570548a994667899d3319b72f95cb2a64e567ad52c",
+            "latestLedger": "1479",
+            "latestLedgerCloseTime": "1690594566",
+        }
+        data = {
+            "jsonrpc": "2.0",
+            "id": "688dfcf3bcd04f52af4866e98dffe387",
+            "result": result,
+        }
+
+        transaction = _build_soroban_transaction(None, [])
+        with requests_mock.Mocker() as m:
+            m.post(PRC_URL, json=data)
+            assert SorobanServer(PRC_URL).send_transaction(
+                transaction
+            ) == SendTransactionResponse.model_validate(result)
+
+        request_data = m.last_request.json()
+        assert len(request_data["id"]) == 32
+        assert request_data["jsonrpc"] == "2.0"
+        assert request_data["method"] == "sendTransaction"
+        assert request_data["params"] == {"transaction": transaction.to_xdr()}
+        assert result["hash"] == transaction.hash_hex()
+
     def test_soroban_rpc_error_response_raise(self):
         data = {
             "jsonrpc": "2.0",