nado-protocol 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. nado_protocol/__init__.py +0 -0
  2. nado_protocol/client/__init__.py +200 -0
  3. nado_protocol/client/apis/__init__.py +26 -0
  4. nado_protocol/client/apis/base.py +42 -0
  5. nado_protocol/client/apis/market/__init__.py +23 -0
  6. nado_protocol/client/apis/market/execute.py +192 -0
  7. nado_protocol/client/apis/market/query.py +310 -0
  8. nado_protocol/client/apis/perp/__init__.py +18 -0
  9. nado_protocol/client/apis/perp/query.py +30 -0
  10. nado_protocol/client/apis/rewards/__init__.py +6 -0
  11. nado_protocol/client/apis/rewards/execute.py +131 -0
  12. nado_protocol/client/apis/rewards/query.py +12 -0
  13. nado_protocol/client/apis/spot/__init__.py +23 -0
  14. nado_protocol/client/apis/spot/base.py +32 -0
  15. nado_protocol/client/apis/spot/execute.py +117 -0
  16. nado_protocol/client/apis/spot/query.py +79 -0
  17. nado_protocol/client/apis/subaccount/__init__.py +24 -0
  18. nado_protocol/client/apis/subaccount/execute.py +54 -0
  19. nado_protocol/client/apis/subaccount/query.py +145 -0
  20. nado_protocol/client/context.py +90 -0
  21. nado_protocol/contracts/__init__.py +377 -0
  22. nado_protocol/contracts/abis/Endpoint.json +636 -0
  23. nado_protocol/contracts/abis/FQuerier.json +1909 -0
  24. nado_protocol/contracts/abis/IClearinghouse.json +876 -0
  25. nado_protocol/contracts/abis/IERC20.json +185 -0
  26. nado_protocol/contracts/abis/IEndpoint.json +250 -0
  27. nado_protocol/contracts/abis/IFoundationRewardsAirdrop.json +76 -0
  28. nado_protocol/contracts/abis/IOffchainBook.json +536 -0
  29. nado_protocol/contracts/abis/IPerpEngine.json +931 -0
  30. nado_protocol/contracts/abis/IProductEngine.json +352 -0
  31. nado_protocol/contracts/abis/ISpotEngine.json +813 -0
  32. nado_protocol/contracts/abis/IStaking.json +288 -0
  33. nado_protocol/contracts/abis/IVrtxAirdrop.json +138 -0
  34. nado_protocol/contracts/abis/MockERC20.json +311 -0
  35. nado_protocol/contracts/deployments/deployment.test.json +18 -0
  36. nado_protocol/contracts/eip712/__init__.py +16 -0
  37. nado_protocol/contracts/eip712/domain.py +36 -0
  38. nado_protocol/contracts/eip712/sign.py +79 -0
  39. nado_protocol/contracts/eip712/types.py +154 -0
  40. nado_protocol/contracts/loader.py +55 -0
  41. nado_protocol/contracts/types.py +141 -0
  42. nado_protocol/engine_client/__init__.py +35 -0
  43. nado_protocol/engine_client/execute.py +416 -0
  44. nado_protocol/engine_client/query.py +481 -0
  45. nado_protocol/engine_client/types/__init__.py +113 -0
  46. nado_protocol/engine_client/types/execute.py +680 -0
  47. nado_protocol/engine_client/types/models.py +247 -0
  48. nado_protocol/engine_client/types/query.py +516 -0
  49. nado_protocol/engine_client/types/stream.py +6 -0
  50. nado_protocol/indexer_client/__init__.py +28 -0
  51. nado_protocol/indexer_client/query.py +466 -0
  52. nado_protocol/indexer_client/types/__init__.py +122 -0
  53. nado_protocol/indexer_client/types/models.py +364 -0
  54. nado_protocol/indexer_client/types/query.py +819 -0
  55. nado_protocol/trigger_client/__init__.py +17 -0
  56. nado_protocol/trigger_client/execute.py +118 -0
  57. nado_protocol/trigger_client/query.py +61 -0
  58. nado_protocol/trigger_client/types/__init__.py +7 -0
  59. nado_protocol/trigger_client/types/execute.py +89 -0
  60. nado_protocol/trigger_client/types/models.py +44 -0
  61. nado_protocol/trigger_client/types/query.py +77 -0
  62. nado_protocol/utils/__init__.py +37 -0
  63. nado_protocol/utils/backend.py +111 -0
  64. nado_protocol/utils/bytes32.py +159 -0
  65. nado_protocol/utils/enum.py +6 -0
  66. nado_protocol/utils/exceptions.py +58 -0
  67. nado_protocol/utils/execute.py +403 -0
  68. nado_protocol/utils/expiration.py +45 -0
  69. nado_protocol/utils/interest.py +66 -0
  70. nado_protocol/utils/math.py +67 -0
  71. nado_protocol/utils/model.py +79 -0
  72. nado_protocol/utils/nonce.py +33 -0
  73. nado_protocol/utils/subaccount.py +18 -0
  74. nado_protocol/utils/time.py +21 -0
  75. nado_protocol-0.1.0.dist-info/METADATA +157 -0
  76. nado_protocol-0.1.0.dist-info/RECORD +78 -0
  77. nado_protocol-0.1.0.dist-info/WHEEL +4 -0
  78. nado_protocol-0.1.0.dist-info/entry_points.txt +11 -0
@@ -0,0 +1,17 @@
1
+ from nado_protocol.trigger_client.types import TriggerClientOpts
2
+ from nado_protocol.trigger_client.execute import TriggerExecuteClient
3
+ from nado_protocol.trigger_client.query import TriggerQueryClient
4
+
5
+
6
+ class TriggerClient(TriggerQueryClient, TriggerExecuteClient): # type: ignore
7
+ def __init__(self, opts: TriggerClientOpts):
8
+ TriggerQueryClient.__init__(self, opts)
9
+ TriggerExecuteClient.__init__(self, opts)
10
+
11
+
12
+ __all__ = [
13
+ "TriggerClient",
14
+ "TriggerClientOpts",
15
+ "TriggerExecuteClient",
16
+ "TriggerQueryClient",
17
+ ]
@@ -0,0 +1,118 @@
1
+ import requests
2
+ from functools import singledispatchmethod
3
+ from typing import Union
4
+ from nado_protocol.contracts.types import NadoExecuteType
5
+ from nado_protocol.trigger_client.types.execute import (
6
+ TriggerExecuteParams,
7
+ TriggerExecuteRequest,
8
+ PlaceTriggerOrderParams,
9
+ CancelTriggerOrdersParams,
10
+ CancelProductTriggerOrdersParams,
11
+ to_trigger_execute_request,
12
+ )
13
+ from nado_protocol.engine_client.types.execute import ExecuteResponse
14
+ from nado_protocol.trigger_client.types import TriggerClientOpts
15
+ from nado_protocol.utils.exceptions import (
16
+ BadStatusCodeException,
17
+ ExecuteFailedException,
18
+ )
19
+ from nado_protocol.utils.execute import NadoBaseExecute
20
+ from nado_protocol.utils.model import NadoBaseModel, is_instance_of_union
21
+
22
+
23
+ class TriggerExecuteClient(NadoBaseExecute):
24
+ def __init__(self, opts: TriggerClientOpts):
25
+ super().__init__(opts)
26
+ self._opts: TriggerClientOpts = TriggerClientOpts.parse_obj(opts)
27
+ self.url: str = self._opts.url
28
+ self.session = requests.Session()
29
+
30
+ def tx_nonce(self, _: str) -> int:
31
+ raise NotImplementedError
32
+
33
+ @singledispatchmethod
34
+ def execute(
35
+ self, params: Union[TriggerExecuteParams, TriggerExecuteRequest]
36
+ ) -> ExecuteResponse:
37
+ """
38
+ Executes the operation defined by the provided parameters.
39
+
40
+ Args:
41
+ params (ExecuteParams): The parameters for the operation to execute. This can represent a variety of operations, such as placing orders, cancelling orders, and more.
42
+
43
+ Returns:
44
+ ExecuteResponse: The response from the executed operation.
45
+ """
46
+ req: TriggerExecuteRequest = (
47
+ params if is_instance_of_union(params, TriggerExecuteRequest) else to_trigger_execute_request(params) # type: ignore
48
+ )
49
+ return self._execute(req)
50
+
51
+ @execute.register
52
+ def _(self, req: dict) -> ExecuteResponse:
53
+ """
54
+ Overloaded method to execute the operation defined by the provided request.
55
+
56
+ Args:
57
+ req (dict): The request data for the operation to execute. Can be a dictionary or an instance of ExecuteRequest.
58
+
59
+ Returns:
60
+ ExecuteResponse: The response from the executed operation.
61
+ """
62
+ parsed_req: TriggerExecuteRequest = NadoBaseModel.parse_obj(req) # type: ignore
63
+ return self._execute(parsed_req)
64
+
65
+ def _execute(self, req: TriggerExecuteRequest) -> ExecuteResponse:
66
+ """
67
+ Internal method to execute the operation. Sends request to the server.
68
+
69
+ Args:
70
+ req (TriggerExecuteRequest): The request data for the operation to execute.
71
+
72
+ Returns:
73
+ ExecuteResponse: The response from the executed operation.
74
+
75
+ Raises:
76
+ BadStatusCodeException: If the server response status code is not 200.
77
+ ExecuteFailedException: If there's an error in the execution or the response status is not "success".
78
+ """
79
+ res = self.session.post(f"{self.url}/execute", json=req.dict())
80
+ if res.status_code != 200:
81
+ raise BadStatusCodeException(res.text)
82
+ try:
83
+ execute_res = ExecuteResponse(**res.json(), req=req.dict())
84
+ except Exception:
85
+ raise ExecuteFailedException(res.text)
86
+ if execute_res.status != "success":
87
+ raise ExecuteFailedException(res.text)
88
+ return execute_res
89
+
90
+ def place_trigger_order(self, params: PlaceTriggerOrderParams) -> ExecuteResponse:
91
+ params = PlaceTriggerOrderParams.parse_obj(params)
92
+ params.order = self.prepare_execute_params(params.order, True, True)
93
+ params.signature = params.signature or self._sign(
94
+ NadoExecuteType.PLACE_ORDER, params.order.dict(), params.product_id
95
+ )
96
+ return self.execute(params)
97
+
98
+ def cancel_trigger_orders(
99
+ self, params: CancelTriggerOrdersParams
100
+ ) -> ExecuteResponse:
101
+ params = self.prepare_execute_params(
102
+ CancelTriggerOrdersParams.parse_obj(params), True
103
+ )
104
+ params.signature = params.signature or self._sign(
105
+ NadoExecuteType.CANCEL_ORDERS, params.dict()
106
+ )
107
+ return self.execute(params)
108
+
109
+ def cancel_product_trigger_orders(
110
+ self, params: CancelProductTriggerOrdersParams
111
+ ) -> ExecuteResponse:
112
+ params = self.prepare_execute_params(
113
+ CancelProductTriggerOrdersParams.parse_obj(params), True
114
+ )
115
+ params.signature = params.signature or self._sign(
116
+ NadoExecuteType.CANCEL_PRODUCT_ORDERS, params.dict()
117
+ )
118
+ return self.execute(params)
@@ -0,0 +1,61 @@
1
+ import requests
2
+ from nado_protocol.contracts.types import NadoTxType
3
+ from nado_protocol.trigger_client.types import TriggerClientOpts
4
+ from nado_protocol.trigger_client.types.query import (
5
+ ListTriggerOrdersParams,
6
+ ListTriggerOrdersRequest,
7
+ TriggerQueryResponse,
8
+ )
9
+ from nado_protocol.utils.exceptions import (
10
+ BadStatusCodeException,
11
+ QueryFailedException,
12
+ )
13
+ from nado_protocol.utils.execute import NadoBaseExecute
14
+
15
+
16
+ class TriggerQueryClient(NadoBaseExecute):
17
+ """
18
+ Client class for querying the trigger service.
19
+ """
20
+
21
+ def __init__(self, opts: TriggerClientOpts):
22
+ self._opts: TriggerClientOpts = TriggerClientOpts.parse_obj(opts)
23
+ self.url: str = self._opts.url
24
+ self.session = requests.Session() # type: ignore
25
+
26
+ def tx_nonce(self, _: str) -> int:
27
+ raise NotImplementedError
28
+
29
+ def query(self, req: dict) -> TriggerQueryResponse:
30
+ """
31
+ Send a query to the trigger service.
32
+
33
+ Args:
34
+ req (QueryRequest): The query request parameters.
35
+
36
+ Returns:
37
+ QueryResponse: The response from the engine.
38
+
39
+ Raises:
40
+ BadStatusCodeException: If the response status code is not 200.
41
+ QueryFailedException: If the query status is not "success".
42
+ """
43
+ res = self.session.post(f"{self.url}/query", json=req)
44
+ if res.status_code != 200:
45
+ raise BadStatusCodeException(res.text)
46
+ try:
47
+ query_res = TriggerQueryResponse(**res.json())
48
+ except Exception:
49
+ raise QueryFailedException(res.text)
50
+ if query_res.status != "success":
51
+ raise QueryFailedException(res.text)
52
+ return query_res
53
+
54
+ def list_trigger_orders(
55
+ self, params: ListTriggerOrdersParams
56
+ ) -> TriggerQueryResponse:
57
+ params = ListTriggerOrdersParams.parse_obj(params)
58
+ params.signature = params.signature or self._sign(
59
+ NadoTxType.LIST_TRIGGER_ORDERS, params.tx.dict()
60
+ )
61
+ return self.query(ListTriggerOrdersRequest.parse_obj(params).dict())
@@ -0,0 +1,7 @@
1
+ from nado_protocol.utils.backend import NadoClientOpts
2
+
3
+
4
+ class TriggerClientOpts(NadoClientOpts):
5
+ """
6
+ Model defining the configuration options for the Trigger Client.
7
+ """
@@ -0,0 +1,89 @@
1
+ from typing import Union
2
+ from pydantic import validator
3
+ from nado_protocol.contracts.types import NadoExecuteType
4
+ from nado_protocol.utils.bytes32 import bytes32_to_hex
5
+ from nado_protocol.utils.model import NadoBaseModel
6
+ from nado_protocol.engine_client.types.execute import (
7
+ PlaceOrderParams,
8
+ CancelOrdersParams,
9
+ CancelProductOrdersParams,
10
+ CancelOrdersRequest,
11
+ CancelProductOrdersRequest,
12
+ )
13
+ from nado_protocol.trigger_client.types.models import TriggerCriteria
14
+
15
+
16
+ class PlaceTriggerOrderParams(PlaceOrderParams):
17
+ trigger: TriggerCriteria
18
+
19
+
20
+ CancelTriggerOrdersParams = CancelOrdersParams
21
+ CancelProductTriggerOrdersParams = CancelProductOrdersParams
22
+
23
+ TriggerExecuteParams = Union[
24
+ PlaceTriggerOrderParams, CancelTriggerOrdersParams, CancelProductTriggerOrdersParams
25
+ ]
26
+
27
+
28
+ class PlaceTriggerOrderRequest(NadoBaseModel):
29
+ """
30
+ Parameters for a request to place an order.
31
+
32
+ Attributes:
33
+ place_order (PlaceOrderParams): The parameters for the order to be placed.
34
+
35
+ Methods:
36
+ serialize: Validates and serializes the order parameters.
37
+ """
38
+
39
+ place_order: PlaceTriggerOrderParams
40
+
41
+ @validator("place_order")
42
+ def serialize(cls, v: PlaceTriggerOrderParams) -> PlaceTriggerOrderParams:
43
+ if v.order.nonce is None:
44
+ raise ValueError("Missing order `nonce`")
45
+ if v.signature is None:
46
+ raise ValueError("Missing `signature")
47
+ if isinstance(v.order.sender, bytes):
48
+ v.order.serialize_dict(["sender"], bytes32_to_hex)
49
+ v.order.serialize_dict(["nonce", "priceX18", "amount", "expiration"], str)
50
+ return v
51
+
52
+
53
+ CancelTriggerOrdersRequest = CancelOrdersRequest
54
+ CancelProductTriggerOrdersRequest = CancelProductOrdersRequest
55
+
56
+ TriggerExecuteRequest = Union[
57
+ PlaceTriggerOrderRequest,
58
+ CancelTriggerOrdersRequest,
59
+ CancelProductTriggerOrdersRequest,
60
+ ]
61
+
62
+
63
+ def to_trigger_execute_request(params: TriggerExecuteParams) -> TriggerExecuteRequest:
64
+ """
65
+ Maps `TriggerExecuteParams` to its corresponding `TriggerExecuteRequest` object based on the parameter type.
66
+
67
+ Args:
68
+ params (TriggerExecuteParams): The parameters to be executed.
69
+
70
+ Returns:
71
+ TriggerExecuteRequest: The corresponding `TriggerExecuteRequest` object.
72
+ """
73
+ execute_request_mapping = {
74
+ PlaceTriggerOrderParams: (
75
+ PlaceTriggerOrderRequest,
76
+ NadoExecuteType.PLACE_ORDER.value,
77
+ ),
78
+ CancelTriggerOrdersParams: (
79
+ CancelTriggerOrdersRequest,
80
+ NadoExecuteType.CANCEL_ORDERS.value,
81
+ ),
82
+ CancelProductTriggerOrdersParams: (
83
+ CancelProductTriggerOrdersRequest,
84
+ NadoExecuteType.CANCEL_PRODUCT_ORDERS.value,
85
+ ),
86
+ }
87
+
88
+ RequestClass, field_name = execute_request_mapping[type(params)]
89
+ return RequestClass(**{field_name: params}) # type: ignore
@@ -0,0 +1,44 @@
1
+ from typing import Optional, Union
2
+ from nado_protocol.utils.model import NadoBaseModel
3
+
4
+
5
+ class PriceAboveTrigger(NadoBaseModel):
6
+ price_above: str
7
+
8
+
9
+ class PriceBelowTrigger(NadoBaseModel):
10
+ price_below: str
11
+
12
+
13
+ class LastPriceAboveTrigger(NadoBaseModel):
14
+ last_price_above: str
15
+
16
+
17
+ class LastPriceBelowTrigger(NadoBaseModel):
18
+ last_price_below: str
19
+
20
+
21
+ TriggerCriteria = Union[
22
+ PriceAboveTrigger, PriceBelowTrigger, LastPriceAboveTrigger, LastPriceBelowTrigger
23
+ ]
24
+
25
+
26
+ class OrderData(NadoBaseModel):
27
+ sender: str
28
+ priceX18: str
29
+ amount: str
30
+ expiration: str
31
+ nonce: str
32
+
33
+
34
+ class TriggerOrderData(NadoBaseModel):
35
+ """
36
+ Data model for details of a trigger order.
37
+ """
38
+
39
+ product_id: int
40
+ order: OrderData
41
+ signature: str
42
+ spot_leverage: Optional[bool]
43
+ digest: Optional[str]
44
+ trigger: TriggerCriteria
@@ -0,0 +1,77 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import validator
4
+
5
+ from nado_protocol.engine_client.types.models import ResponseStatus
6
+ from nado_protocol.trigger_client.types.models import TriggerOrderData
7
+ from nado_protocol.utils.bytes32 import bytes32_to_hex
8
+ from nado_protocol.utils.execute import BaseParams, SignatureParams
9
+ from nado_protocol.utils.model import NadoBaseModel
10
+
11
+
12
+ class ListTriggerOrdersTx(BaseParams):
13
+ recvTime: int
14
+
15
+
16
+ class ListTriggerOrdersParams(NadoBaseModel):
17
+ """
18
+ Parameters for listing trigger orders
19
+ """
20
+
21
+ type = "list_trigger_orders"
22
+ tx: ListTriggerOrdersTx
23
+ product_id: Optional[int]
24
+ pending: bool
25
+ max_update_time: Optional[str]
26
+ max_digest: Optional[str]
27
+ digests: Optional[list[str]]
28
+ limit: Optional[int]
29
+ signature: Optional[str]
30
+
31
+
32
+ class TriggerOrder(NadoBaseModel):
33
+ order: TriggerOrderData
34
+ status: str
35
+ updated_at: int
36
+
37
+
38
+ class TriggerOrdersData(NadoBaseModel):
39
+ """
40
+ Data model for trigger orders
41
+ """
42
+
43
+ orders: list[TriggerOrder]
44
+
45
+
46
+ class ListTriggerOrdersRequest(ListTriggerOrdersParams):
47
+ tx: ListTriggerOrdersTx
48
+
49
+ @validator("tx")
50
+ def serialize(cls, v: ListTriggerOrdersTx) -> ListTriggerOrdersTx:
51
+ if isinstance(v.sender, bytes):
52
+ v.serialize_dict(["sender"], bytes32_to_hex)
53
+ v.serialize_dict(["recvTime"], str)
54
+ return v
55
+
56
+
57
+ class TriggerQueryResponse(NadoBaseModel):
58
+ """
59
+ Represents a response to a query request.
60
+
61
+ Attributes:
62
+ status (ResponseStatus): The status of the query response.
63
+
64
+ data (Optional[QueryResponseData]): The data returned from the query, or an error message if the query failed.
65
+
66
+ error (Optional[str]): The error message, if any error occurred during the query.
67
+
68
+ error_code (Optional[int]): The error code, if any error occurred during the query.
69
+
70
+ request_type (Optional[str]): Type of the request.
71
+ """
72
+
73
+ status: ResponseStatus
74
+ data: Optional[TriggerOrdersData]
75
+ error: Optional[str]
76
+ error_code: Optional[int]
77
+ request_type: Optional[str]
@@ -0,0 +1,37 @@
1
+ from nado_protocol.utils.backend import *
2
+ from nado_protocol.utils.bytes32 import *
3
+ from nado_protocol.utils.subaccount import *
4
+ from nado_protocol.utils.expiration import *
5
+ from nado_protocol.utils.math import *
6
+ from nado_protocol.utils.nonce import *
7
+ from nado_protocol.utils.exceptions import *
8
+
9
+ __all__ = [
10
+ "NadoBackendURL",
11
+ "NadoClientOpts",
12
+ "SubaccountParams",
13
+ "Subaccount",
14
+ "subaccount_to_bytes32",
15
+ "subaccount_to_hex",
16
+ "subaccount_name_to_bytes12",
17
+ "hex_to_bytes32",
18
+ "hex_to_bytes12",
19
+ "hex_to_bytes",
20
+ "str_to_hex",
21
+ "bytes32_to_hex",
22
+ "zero_subaccount",
23
+ "zero_address",
24
+ "OrderType",
25
+ "get_expiration_timestamp",
26
+ "gen_order_nonce",
27
+ "decode_expiration",
28
+ "to_pow_10",
29
+ "to_x18",
30
+ "from_pow_10",
31
+ "from_x18",
32
+ "ExecuteFailedException",
33
+ "QueryFailedException",
34
+ "BadStatusCodeException",
35
+ "MissingSignerException",
36
+ "InvalidProductId",
37
+ ]
@@ -0,0 +1,111 @@
1
+ from nado_protocol.utils.enum import StrEnum
2
+ from eth_account import Account
3
+ from eth_account.signers.local import LocalAccount
4
+ from typing import Optional, Union
5
+ from pydantic import BaseModel, AnyUrl, validator, root_validator
6
+
7
+
8
+ class NadoBackendURL(StrEnum):
9
+ """Enum representing different Nado backend URLs."""
10
+
11
+ # dev
12
+ DEVNET_GATEWAY = "http://localhost:80"
13
+ DEVNET_INDEXER = "http://localhost:8000"
14
+ DEVNET_TRIGGER = "http://localhost:8080"
15
+
16
+
17
+ PrivateKey = str
18
+ Signer = Union[LocalAccount, PrivateKey]
19
+
20
+
21
+ class NadoClientOpts(BaseModel):
22
+ """
23
+ Model defining the configuration options for execute Nado Clients (e.g: Engine, Trigger). It includes various parameters such as the URL,
24
+ the signer, the linked signer, the chain ID, and others.
25
+
26
+ Attributes:
27
+ url (AnyUrl): The URL of the server.
28
+ signer (Optional[Signer]): The signer for the client, if any. It can either be a `LocalAccount` or a private key.
29
+ linked_signer (Optional[Signer]): An optional signer linked the main subaccount to perform executes on it's behalf.
30
+ chain_id (Optional[int]): An optional network chain ID.
31
+ endpoint_addr (Optional[str]): Nado's endpoint address used for verifying executes.
32
+ book_addrs (Optional[list[str]]): Nado's book addresses used for verifying order placement.
33
+
34
+ Notes:
35
+ - The class also includes several methods for validating and sanitizing the input values.
36
+ - "linked_signer" cannot be set if "signer" is not set.
37
+ """
38
+
39
+ url: AnyUrl
40
+ signer: Optional[Union[LocalAccount, PrivateKey]] = None
41
+ linked_signer: Optional[Signer] = None
42
+ chain_id: Optional[int] = None
43
+ endpoint_addr: Optional[str] = None
44
+ book_addrs: Optional[list[str]] = None
45
+
46
+ class Config:
47
+ arbitrary_types_allowed = True
48
+
49
+ @root_validator
50
+ def check_linked_signer(cls, values: dict):
51
+ """
52
+ Validates that if a linked_signer is set, a signer must also be set.
53
+
54
+ Args:
55
+ values (dict): The input values to be validated.
56
+
57
+ Raises:
58
+ ValueError: If linked_signer is set but signer is not.
59
+
60
+ Returns:
61
+ dict: The validated values.
62
+ """
63
+ signer, linked_signer = values.get("signer"), values.get("linked_signer")
64
+ if linked_signer and not signer:
65
+ raise ValueError("linked_signer cannot be set if signer is not set")
66
+ return values
67
+
68
+ @validator("url")
69
+ def clean_url(cls, v: AnyUrl) -> str:
70
+ """
71
+ Cleans the URL input by removing trailing slashes.
72
+
73
+ Args:
74
+ v (AnyUrl): The input URL.
75
+
76
+ Returns:
77
+ str: The cleaned URL.
78
+ """
79
+ return v.rstrip("/")
80
+
81
+ @validator("signer")
82
+ def signer_to_local_account(cls, v: Optional[Signer]) -> Optional[LocalAccount]:
83
+ """
84
+ Validates and converts the signer to a LocalAccount instance.
85
+
86
+ Args:
87
+ v (Optional[Signer]): The signer instance or None.
88
+
89
+ Returns:
90
+ Optional[LocalAccount]: The LocalAccount instance or None.
91
+ """
92
+ if v is None or isinstance(v, LocalAccount):
93
+ return v
94
+ return Account.from_key(v)
95
+
96
+ @validator("linked_signer")
97
+ def linked_signer_to_local_account(
98
+ cls, v: Optional[Signer]
99
+ ) -> Optional[LocalAccount]:
100
+ """
101
+ Validates and converts the linked_signer to a LocalAccount instance.
102
+
103
+ Args:
104
+ v (Optional[Signer]): The linked_signer instance or None.
105
+
106
+ Returns:
107
+ Optional[LocalAccount]: The LocalAccount instance or None.
108
+ """
109
+ if v is None or isinstance(v, LocalAccount):
110
+ return v
111
+ return Account.from_key(v)