Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for the simulate RPC (XLS-69d) #793

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [[Unreleased]]

### Added
- Support for the `simulate` RPC

## [4.0.0] - 2024-12-23

### Added
Expand Down
58 changes: 51 additions & 7 deletions xrpl/asyncio/transaction/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@
from xrpl.core.addresscodec import is_valid_xaddress, xaddress_to_classic_address
from xrpl.core.binarycodec import encode, encode_for_multisigning, encode_for_signing
from xrpl.core.keypairs.main import sign as keypairs_sign
from xrpl.models.requests import ServerState, SubmitOnly
from xrpl.models.response import Response
from xrpl.models.transactions import EscrowFinish
from xrpl.models.transactions.transaction import Signer, Transaction
from xrpl.models import (
EscrowFinish,
Response,
ServerState,
Signer,
Simulate,
SubmitOnly,
Transaction,
)
from xrpl.models.transactions.transaction import (
transaction_json_to_binary_codec_form as model_transaction_to_binary_codec,
)
Expand Down Expand Up @@ -152,9 +157,9 @@ async def submit(
Submits a transaction to the ledger.

Args:
transaction: the Transaction to be submitted.
client: the network client with which to submit the transaction.
fail_hard: an optional boolean. If True, and the transaction fails for
transaction: The Transaction to be submitted.
client: The network client with which to submit the transaction.
fail_hard: An optional boolean. If True, and the transaction fails for
the initial server, do not retry or relay the transaction to other
servers. Defaults to False.

Expand All @@ -174,6 +179,45 @@ async def submit(
raise XRPLRequestFailureException(response.result)


async def simulate(
transaction: Transaction,
client: Client,
*,
binary: bool = False,
) -> Response:
"""
Simulates a transaction without actually submitting it to the network.

Args:
transaction: The transaction to simulate.
client: The network client with which to submit the transaction.
binary: Whether the return data should be encoded in the XRPL's binary format.
Defaults to False.

Raises:
XRPLRequestFailureException: If the transaction fails in the simulated scenario.

Returns:
The response from the ledger.
"""
# autofill the network ID
transaction_json = transaction.to_dict()
await get_network_id_and_build_version(client)
if "network_id" not in transaction_json and _tx_needs_networkID(client):
transaction_json["network_id"] = client.network_id
final_tx = Transaction.from_dict(transaction_json)

# send the `simulate` request
transaction_blob = encode(final_tx.to_xrpl())
response = await client._request_impl(
Simulate(tx_blob=transaction_blob, binary=binary)
)
if response.is_successful():
return response

raise XRPLRequestFailureException(response.result)


def _prepare_transaction(
transaction: Transaction,
wallet: Wallet,
Expand Down
2 changes: 2 additions & 0 deletions xrpl/models/requests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from xrpl.models.requests.sign import Sign
from xrpl.models.requests.sign_and_submit import SignAndSubmit
from xrpl.models.requests.sign_for import SignFor
from xrpl.models.requests.simulate import Simulate
from xrpl.models.requests.submit import Submit
from xrpl.models.requests.submit_multisigned import SubmitMultisigned
from xrpl.models.requests.submit_only import SubmitOnly
Expand Down Expand Up @@ -99,6 +100,7 @@
"Sign",
"SignAndSubmit",
"SignFor",
"Simulate",
"Submit",
"SubmitMultisigned",
"SubmitOnly",
Expand Down
1 change: 1 addition & 0 deletions xrpl/models/requests/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class RequestMethod(str, Enum):
# transaction methods
SIGN = "sign"
SIGN_FOR = "sign_for"
SIMULATE = "simulate"
SUBMIT = "submit"
SUBMIT_MULTISIGNED = "submit_multisigned"
TRANSACTION_ENTRY = "transaction_entry"
Expand Down
35 changes: 35 additions & 0 deletions xrpl/models/requests/simulate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""This request simulates a transaction without submitting it to the network."""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Dict, Optional

from typing_extensions import Self

from xrpl.models.requests.request import Request, RequestMethod
from xrpl.models.transactions.transaction import Transaction
from xrpl.models.utils import KW_ONLY_DATACLASS, require_kwargs_on_init


@require_kwargs_on_init
@dataclass(frozen=True, **KW_ONLY_DATACLASS)
class Simulate(Request):
"""
The `simulate` method simulates a transaction without submitting it to the
network.
"""

tx_blob: Optional[str] = None

tx_json: Optional[Transaction] = None

binary: Optional[bool] = None

method: RequestMethod = field(default=RequestMethod.SIMULATE, init=False)

def _get_errors(self: Self) -> Dict[str, str]:
errors = super()._get_errors()
if (self.tx_blob is None) == (self.tx_json is None):
errors["tx"] = "Must have exactly one of `tx_blob` and `tx_json` fields."
return errors
Loading