def from_dict(cls: Type[Transaction], value: Dict[str, Any]) -> Transaction: """ Construct a new Transaction from a dictionary of parameters. Args: value: The value to construct the Transaction from. Returns: A new Transaction object, constructed using the given parameters. Raises: XRPLModelException: If the dictionary provided is invalid. """ if cls.__name__ == "Transaction": # using `Transaction.from_dict` and not a subclass if "transaction_type" not in value: raise XRPLModelException( "Transaction does not include transaction_type.") correct_type = cls.get_transaction_type(value["transaction_type"]) return correct_type.from_dict(value) else: if "transaction_type" in value: if value["transaction_type"] != cls.__name__: transaction_type = value["transaction_type"] raise XRPLModelException( f"Using wrong constructor: using f{cls.__name__} constructor " f"with transaction type f{transaction_type}.") value = {**value} del value["transaction_type"] return cast(Transaction, super(Transaction, cls).from_dict(value))
def from_dict(cls: Type[Request], value: Dict[str, Any]) -> Request: """ Construct a new Request from a dictionary of parameters. Args: value: The value to construct the Request from. Returns: A new Request object, constructed using the given parameters. Raises: XRPLModelException: If the dictionary provided is invalid. """ if cls.__name__ == "Request": if "method" not in value: raise XRPLModelException("Request does not include method.") correct_type = cls.get_method(value["method"]) return correct_type.from_dict(value) if "method" in value: method = value["method"] if _method_to_class_name(method) != cls.__name__ and not ( method == "submit" and cls.__name__ in ("SignAndSubmit", "SubmitOnly")): raise XRPLModelException( f"Using wrong constructor: using {cls.__name__} constructor " f"with Request method {method}.") value = {**value} del value["method"] return cast(Request, super(Request, cls).from_dict(value))
def _from_dict_special_cases( cls: Type[BaseModel], param: str, param_type: Type[Any], param_value: Dict[str, Any], ) -> Union[str, Enum, BaseModel, Dict[str, Any]]: """Handles all the recursive/more complex cases for `from_dict`.""" from xrpl.models.amounts import Amount, IssuedCurrencyAmount from xrpl.models.currencies import XRP, Currency, IssuedCurrency from xrpl.models.transactions.transaction import Transaction # TODO: figure out how to make Unions work generically (if possible) if param_type == Amount: # special case, Union if isinstance(param_value, str): return param_value if not isinstance(param_value, dict): raise XRPLModelException( f"{param_type} requires a dictionary of params") return IssuedCurrencyAmount.from_dict(param_value) if param_type == Currency: # special case, Union if not isinstance(param_value, dict): raise XRPLModelException( f"{param_type} requires a dictionary of params") if "currency" in param_value and "issuer" in param_value: return IssuedCurrency.from_dict(param_value) if "currency" in param_value: param_value_copy = {**param_value} del param_value_copy["currency"] return XRP.from_dict(param_value_copy) raise XRPLModelException(f"No valid type for {param}") if param_type == Transaction: # special case, multiple options (could be any Transaction type) if "transaction_type" not in param_value: raise XRPLModelException( f"{param} not a valid parameter for {cls.__name__}") type_str = param_value["transaction_type"] # safely convert type string into the actual type transaction_type = Transaction.get_transaction_type(type_str) param_value_copy = {**param_value} del param_value_copy["transaction_type"] return transaction_type.from_dict(param_value_copy) if param_type in BaseModel.__subclasses__(): # any other BaseModel if not isinstance(param_value, dict): raise XRPLModelException( f"{param_type} requires a dictionary of params") # mypy doesn't know that the If checks that it's a subclass of BaseModel return param_type.from_dict(param_value) # type: ignore if param_type in Enum.__subclasses__(): # mypy doesn't know that the If checks that it's a subclass of Enum return param_type(param_value) # type: ignore return param_value
def _from_dict_single_param( cls: Type[BaseModel], param: str, param_type: Type[Any], param_value: Dict[str, Any], ) -> Any: """Recursively handles each individual param in `from_dict`.""" if type(param_value) == param_type: # the type of the param provided matches the type expected for the param return param_value if "xrpl.models" in param_type.__module__: # any model defined in xrpl.models if not isinstance(param_value, dict): raise XRPLModelException( f"{param_type} requires a dictionary of params") return cast(BaseModel, param_type).from_dict(param_value) if param_type in Enum.__subclasses__(): # an Enum return param_type(param_value) # param_type must be something from typing - e.g. List, Union, Any # there are no models that have Dict params if param_type == Any: # param_type is Any return param_value if param_type.__reduce__()[1][0] == List: # param_type is a List if not isinstance(param_value, List): raise XRPLModelException( f"{param} expected a List, received a {type(param_value)}") list_type = param_type.__reduce__()[1][1] new_list = [] for item in param_value: new_list.append( cls._from_dict_single_param(param, list_type, item)) return new_list if param_type.__reduce__()[1][0] == Union: # param_type is a Union for param_type_option in param_type.__args__: # iterate through the types Union-ed together try: # try to use this Union-ed type to process param_value return cls._from_dict_single_param(param, param_type_option, param_value) except XRPLModelException: # this Union-ed type did not work # move onto the next one continue raise XRPLModelException( f"{param} expected a {param_type}, received a {type(param_value)}")
def from_dict(cls: Type[BaseModel], value: Dict[str, Any]) -> BaseModel: """ Construct a new BaseModel from a dictionary of parameters. Args: value: The value to construct the BaseModel from. Returns: A new BaseModel object, constructed using the given parameters. Raises: XRPLModelException: If the dictionary provided is invalid. """ # returns a dictionary mapping class params to their types class_types = get_type_hints(cls) args = {} for param in value: if param not in class_types: raise XRPLModelException( f"{param} not a valid parameter for {cls.__name__}" ) args[param] = cls._from_dict_single_param( param, class_types[param], value[param] ) init = cls._get_only_init_args(args) # Ignore type-checking on this for now to simplify subclass constructors # which might pass non kwargs. return cls(**init) # type: ignore
def get_transaction_type(cls: Type[Transaction], transaction_type: str) -> Type[Transaction]: """ Returns the correct transaction type based on the string name. Args: transaction_type: The String name of the Transaction object. Returns: The transaction class with the given name. Raises: XRPLModelException: If `transaction_type` is not a valid Transaction type. """ import xrpl.models.transactions as transaction_models transaction_types: Dict[str, Type[Transaction]] = { t.value: getattr(transaction_models, t) for t in transaction_models.transaction.TransactionType } if transaction_type in transaction_types: return transaction_types[transaction_type] raise XRPLModelException( f"{transaction_type} is not a valid Transaction type")
def new_init(self: _Self, *args: List[Any], **kwargs: Dict[str, Any]) -> None: if len(args) > 0: raise XRPLModelException( f"{type(self).__name__}.__init__ only allows keyword arguments. " f"Found the following positional arguments: {args}") original_init(self, **kwargs) # type: ignore
def validate(self: BaseModel) -> None: """ Raises if this object is invalid. Raises: XRPLModelException: if this object is invalid. """ errors = self._get_errors() if len(errors) > 0: raise XRPLModelException(str(errors))
def get_hash(self: Transaction) -> str: """ Hashes the Transaction object as the ledger does. Only valid for signed Transaction objects. Returns: The hash of the Transaction object. Raises: XRPLModelException: if the Transaction is unsigned. """ if self.txn_signature is None: raise XRPLModelException( "Cannot get the hash from an unsigned Transaction.") prefix = hex(_TRANSACTION_HASH_PREFIX)[2:].upper() encoded_str = bytes.fromhex(prefix + encode(self.to_xrpl())) return sha512(encoded_str).digest().hex().upper()[:64]
def from_dict(cls: Type[XRP], value: Dict[str, Any]) -> XRP: """ Construct a new XRP from a dictionary of parameters. Args: value: The value to construct the XRP from. Returns: A new XRP object, constructed using the given parameters. Raises: XRPLModelException: If the dictionary provided is invalid. """ if len(value ) != 1 or "currency" not in value or value["currency"] != "XRP": raise XRPLModelException("Not a valid XRP type") return XRP()
def get_method(cls: Type[Request], method: str) -> Type[Request]: """ Returns the correct request method based on the string name. Args: method: The String name of the Request object. Returns: The request class with the given name. Raises: XRPLModelException: If `method` is not a valid Request method. """ import xrpl.models.requests as request_models request_methods: Dict[str, Type[Request]] = {} for r in RequestMethod: method_name = _method_to_class_name(r.name) request_methods[r.value] = getattr(request_models, method_name) if method in request_methods: return request_methods[method] raise XRPLModelException(f"{method} is not a valid Request method")
def get_transaction_type(cls: Type[Transaction], transaction_type: str) -> Type[Transaction]: """ Returns the correct transaction type based on the string name. Args: transaction_type: The String name of the Transaction object. Returns: The transaction class with the given name. Raises: XRPLModelException: If `transaction_type` is not a valid Transaction type. """ if transaction_type == TransactionType.ACCOUNT_DELETE: from xrpl.models.transactions import AccountDelete return AccountDelete if transaction_type == TransactionType.ACCOUNT_SET: from xrpl.models.transactions import AccountSet return AccountSet if transaction_type == TransactionType.CHECK_CANCEL: from xrpl.models.transactions import CheckCancel return CheckCancel if transaction_type == TransactionType.CHECK_CASH: from xrpl.models.transactions import CheckCash return CheckCash if transaction_type == TransactionType.CHECK_CREATE: from xrpl.models.transactions import CheckCreate return CheckCreate if transaction_type == TransactionType.DEPOSIT_PREAUTH: from xrpl.models.transactions import DepositPreauth return DepositPreauth if transaction_type == TransactionType.ESCROW_CANCEL: from xrpl.models.transactions import EscrowCancel return EscrowCancel if transaction_type == TransactionType.ESCROW_CREATE: from xrpl.models.transactions import EscrowCreate return EscrowCreate if transaction_type == TransactionType.ESCROW_FINISH: from xrpl.models.transactions import EscrowFinish return EscrowFinish if transaction_type == TransactionType.OFFER_CANCEL: from xrpl.models.transactions import OfferCancel return OfferCancel if transaction_type == TransactionType.OFFER_CREATE: from xrpl.models.transactions import OfferCreate return OfferCreate if transaction_type == TransactionType.PAYMENT: from xrpl.models.transactions import Payment return Payment if transaction_type == TransactionType.PAYMENT_CHANNEL_CLAIM: from xrpl.models.transactions import PaymentChannelClaim return PaymentChannelClaim if transaction_type == TransactionType.PAYMENT_CHANNEL_CREATE: from xrpl.models.transactions import PaymentChannelCreate return PaymentChannelCreate if transaction_type == TransactionType.PAYMENT_CHANNEL_FUND: from xrpl.models.transactions import PaymentChannelFund return PaymentChannelFund if transaction_type == TransactionType.SET_REGULAR_KEY: from xrpl.models.transactions import SetRegularKey return SetRegularKey if transaction_type == TransactionType.SIGNER_LIST_SET: from xrpl.models.transactions import SignerListSet return SignerListSet if transaction_type == TransactionType.TRUST_SET: from xrpl.models.transactions import TrustSet return TrustSet raise XRPLModelException( f"{transaction_type} is not a valid Transaction type")
def _from_dict_single_param( cls: Type[BaseModel], param: str, param_type: Type[Any], param_value: Union[int, str, bool, BaseModel, Enum, List[Any], Dict[str, Any]], ) -> Any: """Recursively handles each individual param in `from_dict`.""" param_type_origin = get_origin(param_type) # returns `list` if a List, `Union` if a Union, None otherwise if param_type_origin is list and isinstance(param_value, list): # expected a List, received a List list_type = get_args(param_type)[0] return [ cls._from_dict_single_param(param, list_type, item) for item in param_value ] if param_type_origin is Union: for param_type_option in get_args(param_type): # iterate through the types Union-ed together try: # try to use this Union-ed type to process param_value return cls._from_dict_single_param(param, param_type_option, param_value) except XRPLModelException: # this Union-ed type did not work, move onto the next one pass # no more collections (no params expect a Dict) if param_type is Any: # param_type is Any (e.g. will accept anything) return param_value if isinstance(param_type, type) and isinstance(param_value, param_type): # expected an object, received the correct object return param_value if (isinstance(param_type, type) and issubclass(param_type, Enum) and param_value in list(param_type)): # expected an Enum and received a valid value for it. # for some reason required for string enums. return param_value if (isinstance(param_type, type) and issubclass(param_type, BaseModel) and isinstance(param_value, dict)): # expected an XRPL Model, received a Dict return cast(BaseModel, param_type).from_dict(param_value) # received something we didn't expect, raise an error if isinstance(param_type, type) and issubclass(param_type, BaseModel): error_message = ( f"{param} expected a {param_type} or a Dict representing {param_type}, " f"received a {type(param_value)}") else: error_message = ( f"{param} expected a {param_type}, received a {type(param_value)}" ) raise XRPLModelException(error_message)