afp-sdk 0.4.0__py3-none-any.whl → 0.5.1__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.
- afp/__init__.py +12 -7
- afp/afp.py +210 -0
- afp/api/admin.py +32 -18
- afp/api/base.py +82 -29
- afp/api/{clearing.py → margin_account.py} +172 -104
- afp/api/{builder.py → product.py} +136 -42
- afp/api/trading.py +12 -29
- afp/auth.py +66 -0
- afp/bindings/facade.py +25 -13
- afp/config.py +22 -36
- afp/constants.py +52 -0
- afp/decorators.py +3 -1
- afp/enums.py +6 -0
- afp/exceptions.py +7 -3
- afp/exchange.py +26 -11
- afp/{signing.py → hashing.py} +4 -12
- afp/schemas.py +23 -3
- afp/validators.py +1 -0
- {afp_sdk-0.4.0.dist-info → afp_sdk-0.5.1.dist-info}/METADATA +54 -50
- afp_sdk-0.5.1.dist-info/RECORD +37 -0
- {afp_sdk-0.4.0.dist-info → afp_sdk-0.5.1.dist-info}/WHEEL +1 -1
- afp/.ruff_cache/.gitignore +0 -2
- afp/.ruff_cache/0.12.7/14089225400260942471 +0 -0
- afp/.ruff_cache/CACHEDIR.TAG +0 -1
- afp/api/liquidation.py +0 -167
- afp_sdk-0.4.0.dist-info/RECORD +0 -38
- {afp_sdk-0.4.0.dist-info → afp_sdk-0.5.1.dist-info}/licenses/LICENSE +0 -0
afp/__init__.py
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
"""Autonomous Futures Protocol Python SDK."""
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
from .
|
|
5
|
-
from .
|
|
6
|
-
from .
|
|
7
|
-
from .api.liquidation import Liquidation
|
|
8
|
-
from .api.trading import Trading
|
|
3
|
+
from . import bindings
|
|
4
|
+
from .afp import AFP
|
|
5
|
+
from .auth import Authenticator, KeyfileAuthenticator, PrivateKeyAuthenticator
|
|
6
|
+
from .exceptions import AFPException
|
|
9
7
|
|
|
10
|
-
__all__ = (
|
|
8
|
+
__all__ = (
|
|
9
|
+
"bindings",
|
|
10
|
+
"AFP",
|
|
11
|
+
"AFPException",
|
|
12
|
+
"Authenticator",
|
|
13
|
+
"KeyfileAuthenticator",
|
|
14
|
+
"PrivateKeyAuthenticator",
|
|
15
|
+
)
|
afp/afp.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
from .auth import Authenticator, KeyfileAuthenticator, PrivateKeyAuthenticator
|
|
2
|
+
from .config import Config
|
|
3
|
+
from .api.admin import Admin
|
|
4
|
+
from .api.margin_account import MarginAccount
|
|
5
|
+
from .api.product import Product
|
|
6
|
+
from .api.trading import Trading
|
|
7
|
+
from .constants import defaults
|
|
8
|
+
from .exceptions import ConfigurationError
|
|
9
|
+
from .validators import validate_address
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AFP:
|
|
13
|
+
"""Application object for interacting with the AFP Clearing System and the AutEx
|
|
14
|
+
exchange.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
authenticator : afp.Authenticator, optional
|
|
19
|
+
The default authenticator for signing transactions & messages. Can also be set
|
|
20
|
+
with environment variables; use `AFP_PRIVATE_KEY` for private key
|
|
21
|
+
authentication, `AFP_KEYFILE` and `AFP_KEYFILE_PASSWORD` for keyfile
|
|
22
|
+
authentication.
|
|
23
|
+
rpc_url : str, optional
|
|
24
|
+
The URL of an Autonity RPC provider. Can also be set with the `AFP_RPC_URL`
|
|
25
|
+
environment variable.
|
|
26
|
+
exchange_url : str, optional
|
|
27
|
+
The REST API base URL of the exchange. Defaults to the URL of the AutEx
|
|
28
|
+
exchange. Its default value can be overridden with the `AFP_EXCHANGE_URL`
|
|
29
|
+
environment variable.
|
|
30
|
+
chain_id : str, optional
|
|
31
|
+
The chain ID of the Autonity network. Defauls to the chain ID of Autonity
|
|
32
|
+
Mainnet. Its default value can be overridden with the `AFP_CHAIN_ID` environment
|
|
33
|
+
variable.
|
|
34
|
+
gas_limit : int, optional
|
|
35
|
+
The `gasLimit` parameter of blockchain transactions. Estimated with the
|
|
36
|
+
`eth_estimateGas` JSON-RPC call if not specified. Its default value can be
|
|
37
|
+
overridden with the `AFP_GAS_LIMIT` environment variable.
|
|
38
|
+
max_fee_per_gas : int, optional
|
|
39
|
+
The `maxFeePerGas` parameter of blockchain transactions in ton (wei) units.
|
|
40
|
+
Defaults to `baseFeePerGas` from the return value of the `eth_getBlock` JSON-RPC
|
|
41
|
+
call. Its default value can be overridden with the `AFP_MAX_FEE_PER_GAS`
|
|
42
|
+
environment variable.
|
|
43
|
+
max_priority_fee_per_gas : int, optional
|
|
44
|
+
The `maxPriorityFeePerGas` parameter of blockchain transactions in ton (wei)
|
|
45
|
+
units. Defaults to the return value of the `eth_maxPriorityFeePerGas` JSON-RPC
|
|
46
|
+
call. Its default value can be overridden with the
|
|
47
|
+
`AFP_MAX_PRIORITY_FEE_PER_GAS` environment variable.
|
|
48
|
+
timeout_seconds: int, optional
|
|
49
|
+
The number of seconds to wait for a blockchain transaction to be mined.
|
|
50
|
+
Defaults to 10 seconds. Its default value can be overridden with the
|
|
51
|
+
`AFP_TIMEOUT_SECONDS` environment variable.
|
|
52
|
+
clearing_diamond_address : str, optional
|
|
53
|
+
The address of the ClearingDiamond contract. Defaults to the Autonity Mainnet
|
|
54
|
+
deployment address. Its default value can be overridden with the
|
|
55
|
+
`AFP_CLEARING_DIAMOND_ADDRESS` environment variable.
|
|
56
|
+
margin_account_registry_address : str, optional
|
|
57
|
+
The address of the MarginAccountRegistry contract. Defaults to the Autonity
|
|
58
|
+
Mainnet deployment address. Its default value can be overridden with the
|
|
59
|
+
`AFP_MARGIN_ACCOUNT_REGISTRY_ADDRESS` environment variable.
|
|
60
|
+
oracle_provider_address : str, optional
|
|
61
|
+
The address of the OracleProvider contract. Defaults to the Autonity Mainnet
|
|
62
|
+
deployment address. Its default value can be overridden with the
|
|
63
|
+
`AFP_ORACLE_PROVIDER_ADDRESS` environment variable.
|
|
64
|
+
product_registry_address: str, optional
|
|
65
|
+
The address of the ProductRegistry contract. Defaults to the Autonity Mainnet
|
|
66
|
+
deployment address. Its default value can be overridden with the
|
|
67
|
+
`AFP_PRODUCT_REGISTRY_ADDRESS` environment variable.
|
|
68
|
+
system_viewer_address: str, optional
|
|
69
|
+
The address of the SystemViewer contract. Defaults to the Autonity Mainnet
|
|
70
|
+
deployment address. Its default value can be overridden with the
|
|
71
|
+
`AFP_SYSTEM_VIEWER_ADDRESS` environment variable.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
config: Config
|
|
75
|
+
|
|
76
|
+
def __init__(
|
|
77
|
+
self,
|
|
78
|
+
*,
|
|
79
|
+
authenticator: Authenticator | None = None,
|
|
80
|
+
rpc_url: str | None = defaults.RPC_URL,
|
|
81
|
+
exchange_url: str = defaults.EXCHANGE_URL,
|
|
82
|
+
chain_id: int = defaults.CHAIN_ID,
|
|
83
|
+
gas_limit: int | None = defaults.GAS_LIMIT,
|
|
84
|
+
max_fee_per_gas: int | None = defaults.MAX_FEE_PER_GAS,
|
|
85
|
+
max_priority_fee_per_gas: int | None = defaults.MAX_PRIORITY_FEE_PER_GAS,
|
|
86
|
+
timeout_seconds: int = defaults.TIMEOUT_SECONDS,
|
|
87
|
+
clearing_diamond_address: str = defaults.CLEARING_DIAMOND_ADDRESS,
|
|
88
|
+
margin_account_registry_address: str = defaults.MARGIN_ACCOUNT_REGISTRY_ADDRESS,
|
|
89
|
+
oracle_provider_address: str = defaults.ORACLE_PROVIDER_ADDRESS,
|
|
90
|
+
product_registry_address: str = defaults.PRODUCT_REGISTRY_ADDRESS,
|
|
91
|
+
system_viewer_address: str = defaults.SYSTEM_VIEWER_ADDRESS,
|
|
92
|
+
) -> None:
|
|
93
|
+
if authenticator is None:
|
|
94
|
+
authenticator = _default_authenticator()
|
|
95
|
+
|
|
96
|
+
self.config = Config(
|
|
97
|
+
authenticator=authenticator,
|
|
98
|
+
exchange_url=exchange_url,
|
|
99
|
+
rpc_url=rpc_url,
|
|
100
|
+
chain_id=chain_id,
|
|
101
|
+
gas_limit=gas_limit,
|
|
102
|
+
max_fee_per_gas=max_fee_per_gas,
|
|
103
|
+
max_priority_fee_per_gas=max_priority_fee_per_gas,
|
|
104
|
+
timeout_seconds=timeout_seconds,
|
|
105
|
+
clearing_diamond_address=validate_address(clearing_diamond_address),
|
|
106
|
+
margin_account_registry_address=validate_address(
|
|
107
|
+
margin_account_registry_address
|
|
108
|
+
),
|
|
109
|
+
oracle_provider_address=validate_address(oracle_provider_address),
|
|
110
|
+
product_registry_address=validate_address(product_registry_address),
|
|
111
|
+
system_viewer_address=validate_address(system_viewer_address),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def __repr__(self) -> str:
|
|
115
|
+
return f"AFP(config={repr(self.config)})"
|
|
116
|
+
|
|
117
|
+
# Clearing APIs
|
|
118
|
+
|
|
119
|
+
def MarginAccount(
|
|
120
|
+
self, authenticator: Authenticator | None = None
|
|
121
|
+
) -> MarginAccount:
|
|
122
|
+
"""API for managing margin accounts.
|
|
123
|
+
|
|
124
|
+
Parameters
|
|
125
|
+
----------
|
|
126
|
+
authenticator : afp.Authenticator, optional
|
|
127
|
+
Authenticator for signing transactions sent to the Clearing System.
|
|
128
|
+
Defaults to the authenticator specified in the `AFP` constructor.
|
|
129
|
+
"""
|
|
130
|
+
return MarginAccount(self.config, authenticator=authenticator)
|
|
131
|
+
|
|
132
|
+
def Product(self, authenticator: Authenticator | None = None) -> Product:
|
|
133
|
+
"""API for managing products.
|
|
134
|
+
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
authenticator : afp.Authenticator, optional
|
|
138
|
+
Authenticator for signing transactions sent to the Clearing System.
|
|
139
|
+
Defaults to the authenticator specified in the `AFP` constructor.
|
|
140
|
+
"""
|
|
141
|
+
return Product(self.config, authenticator=authenticator)
|
|
142
|
+
|
|
143
|
+
# Exchange APIs
|
|
144
|
+
|
|
145
|
+
def Admin(
|
|
146
|
+
self,
|
|
147
|
+
authenticator: Authenticator | None = None,
|
|
148
|
+
exchange_url: str | None = None,
|
|
149
|
+
) -> Admin:
|
|
150
|
+
"""API for AutEx administration, restricted to AutEx admins.
|
|
151
|
+
|
|
152
|
+
Authenticates with the exchange on creation.
|
|
153
|
+
|
|
154
|
+
Parameters
|
|
155
|
+
----------
|
|
156
|
+
authenticator : afp.Authenticator, optional
|
|
157
|
+
Authenticator for authenticating with the AutEx exchange. Defaults to the
|
|
158
|
+
authenticator specified in the `AFP` constructor.
|
|
159
|
+
exchange_url: str, optional
|
|
160
|
+
The REST API base URL of the exchange. Defaults to the value specified in
|
|
161
|
+
the `AFP` constructor.
|
|
162
|
+
|
|
163
|
+
Raises
|
|
164
|
+
------
|
|
165
|
+
afp.exceptions.AuthenticationError
|
|
166
|
+
If the exchange rejects the login attempt.
|
|
167
|
+
"""
|
|
168
|
+
return Admin(
|
|
169
|
+
self.config, authenticator=authenticator, exchange_url=exchange_url
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
def Trading(
|
|
173
|
+
self,
|
|
174
|
+
authenticator: Authenticator | None = None,
|
|
175
|
+
exchange_url: str | None = None,
|
|
176
|
+
) -> Trading:
|
|
177
|
+
"""API for trading in the AutEx exchange.
|
|
178
|
+
|
|
179
|
+
Authenticates with the exchange on creation.
|
|
180
|
+
|
|
181
|
+
Parameters
|
|
182
|
+
----------
|
|
183
|
+
authenticator : afp.Authenticator, optional
|
|
184
|
+
Authenticator for signing intents and authenticating with the AutEx
|
|
185
|
+
exchange. Defaults to the authenticator specified in the `AFP` constructor.
|
|
186
|
+
exchange_url: str, optional
|
|
187
|
+
The REST API base URL of the exchange. Defaults to the value specified in
|
|
188
|
+
the `AFP` constructor.
|
|
189
|
+
|
|
190
|
+
Raises
|
|
191
|
+
------
|
|
192
|
+
afp.exceptions.AuthenticationError
|
|
193
|
+
If the exchange rejects the login attempt.
|
|
194
|
+
"""
|
|
195
|
+
return Trading(
|
|
196
|
+
self.config, authenticator=authenticator, exchange_url=exchange_url
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _default_authenticator() -> Authenticator | None:
|
|
201
|
+
if defaults.PRIVATE_KEY is not None and defaults.KEYFILE is not None:
|
|
202
|
+
raise ConfigurationError(
|
|
203
|
+
"Only one of AFP_PRIVATE_KEY and AFP_KEYFILE environment "
|
|
204
|
+
"variables should be specified"
|
|
205
|
+
)
|
|
206
|
+
if defaults.PRIVATE_KEY is not None:
|
|
207
|
+
return PrivateKeyAuthenticator(defaults.PRIVATE_KEY)
|
|
208
|
+
if defaults.KEYFILE is not None:
|
|
209
|
+
return KeyfileAuthenticator(defaults.KEYFILE, defaults.KEYFILE_PASSWORD)
|
|
210
|
+
return None
|
afp/api/admin.py
CHANGED
|
@@ -1,28 +1,34 @@
|
|
|
1
1
|
from .. import validators
|
|
2
2
|
from ..decorators import refresh_token_on_expiry
|
|
3
|
-
from ..
|
|
3
|
+
from ..enums import ListingState
|
|
4
|
+
from ..schemas import ExchangeProductListingSubmission, ExchangeProductUpdateSubmission
|
|
4
5
|
from .base import ExchangeAPI
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class Admin(ExchangeAPI):
|
|
8
|
-
"""API for AutEx administration, restricted to AutEx admins.
|
|
9
|
+
"""API for AutEx administration, restricted to AutEx admins."""
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
@refresh_token_on_expiry
|
|
12
|
+
def list_product(self, product_id: str) -> None:
|
|
13
|
+
"""Lists a product on the exchange.
|
|
14
|
+
|
|
15
|
+
The product is in private state after listing until it is revealed to the public.
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
The private key of the exchange adminstrator account.
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
product_id : str
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
Raises
|
|
22
|
+
------
|
|
23
|
+
afp.exceptions.AuthorizationError
|
|
24
|
+
If the configured account is not an exchange administrator.
|
|
25
|
+
"""
|
|
26
|
+
product_listing = ExchangeProductListingSubmission(id=product_id)
|
|
27
|
+
self._exchange.list_product(product_listing)
|
|
22
28
|
|
|
23
29
|
@refresh_token_on_expiry
|
|
24
|
-
def
|
|
25
|
-
"""
|
|
30
|
+
def reveal_product(self, product_id: str) -> None:
|
|
31
|
+
"""Makes a product publicly available for trading on the exchange.
|
|
26
32
|
|
|
27
33
|
Parameters
|
|
28
34
|
----------
|
|
@@ -33,8 +39,12 @@ class Admin(ExchangeAPI):
|
|
|
33
39
|
afp.exceptions.AuthorizationError
|
|
34
40
|
If the configured account is not an exchange administrator.
|
|
35
41
|
"""
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
product_update = ExchangeProductUpdateSubmission(
|
|
43
|
+
listing_state=ListingState.PUBLIC
|
|
44
|
+
)
|
|
45
|
+
self._exchange.update_product_listing(
|
|
46
|
+
validators.validate_hexstr32(product_id), product_update
|
|
47
|
+
)
|
|
38
48
|
|
|
39
49
|
@refresh_token_on_expiry
|
|
40
50
|
def delist_product(self, product_id: str) -> None:
|
|
@@ -51,5 +61,9 @@ class Admin(ExchangeAPI):
|
|
|
51
61
|
afp.exceptions.AuthorizationError
|
|
52
62
|
If the configured account is not an exchange administrator.
|
|
53
63
|
"""
|
|
54
|
-
|
|
55
|
-
|
|
64
|
+
product_update = ExchangeProductUpdateSubmission(
|
|
65
|
+
listing_state=ListingState.DELISTED
|
|
66
|
+
)
|
|
67
|
+
self._exchange.update_product_listing(
|
|
68
|
+
validators.validate_hexstr32(product_id), product_update
|
|
69
|
+
)
|
afp/api/base.py
CHANGED
|
@@ -1,37 +1,78 @@
|
|
|
1
1
|
from abc import ABC
|
|
2
2
|
from datetime import datetime
|
|
3
3
|
from functools import cache
|
|
4
|
-
from typing import cast
|
|
5
4
|
from urllib.parse import urlparse
|
|
5
|
+
from typing import cast
|
|
6
6
|
|
|
7
|
-
from eth_account.account import Account
|
|
8
|
-
from eth_account.signers.local import LocalAccount
|
|
9
7
|
from eth_typing.evm import ChecksumAddress
|
|
10
8
|
from siwe import ISO8601Datetime, SiweMessage, siwe # type: ignore (untyped library)
|
|
11
9
|
from web3 import Web3, HTTPProvider
|
|
12
|
-
from web3.
|
|
10
|
+
from web3.contract.contract import ContractFunction
|
|
11
|
+
from web3.types import TxParams
|
|
13
12
|
|
|
14
|
-
from .. import
|
|
13
|
+
from ..auth import Authenticator
|
|
14
|
+
from ..config import Config
|
|
15
15
|
from ..bindings.erc20 import ERC20
|
|
16
|
+
from ..exceptions import ConfigurationError
|
|
16
17
|
from ..exchange import ExchangeClient
|
|
17
|
-
from ..schemas import LoginSubmission
|
|
18
|
+
from ..schemas import LoginSubmission, Transaction
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BaseAPI(ABC):
|
|
22
|
+
_authenticator: Authenticator
|
|
23
|
+
_config: Config
|
|
18
24
|
|
|
25
|
+
def __init__(self, config: Config, authenticator: Authenticator | None = None):
|
|
26
|
+
self._config = config
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
if authenticator is None:
|
|
29
|
+
if config.authenticator is None:
|
|
30
|
+
raise ConfigurationError("Authenticator not specified")
|
|
31
|
+
self._authenticator = config.authenticator
|
|
32
|
+
else:
|
|
33
|
+
self._authenticator = authenticator
|
|
21
34
|
|
|
35
|
+
def __repr__(self) -> str:
|
|
36
|
+
return f"{self.__class__.__name__}(authenticator={repr(self._authenticator)})"
|
|
22
37
|
|
|
23
|
-
|
|
24
|
-
|
|
38
|
+
|
|
39
|
+
class ClearingSystemAPI(BaseAPI, ABC):
|
|
40
|
+
_config: Config
|
|
25
41
|
_w3: Web3
|
|
26
42
|
|
|
27
|
-
def __init__(self,
|
|
28
|
-
|
|
29
|
-
|
|
43
|
+
def __init__(self, config: Config, authenticator: Authenticator | None = None):
|
|
44
|
+
super().__init__(config, authenticator)
|
|
45
|
+
|
|
46
|
+
if self._config.rpc_url is None:
|
|
47
|
+
raise ConfigurationError("RPC URL not specified")
|
|
48
|
+
|
|
49
|
+
self._w3 = Web3(HTTPProvider(self._config.rpc_url))
|
|
50
|
+
self._w3.eth.default_account = self._authenticator.address
|
|
30
51
|
|
|
31
|
-
|
|
32
|
-
self._w3.eth.
|
|
33
|
-
|
|
34
|
-
|
|
52
|
+
def _transact(self, func: ContractFunction) -> Transaction:
|
|
53
|
+
tx_count = self._w3.eth.get_transaction_count(self._authenticator.address)
|
|
54
|
+
tx_params = {
|
|
55
|
+
"from": self._authenticator.address,
|
|
56
|
+
"nonce": tx_count,
|
|
57
|
+
"gasLimit": self._config.gas_limit,
|
|
58
|
+
"maxFeePerGas": self._config.max_fee_per_gas,
|
|
59
|
+
"maxPriorityFeePerGas": self._config.max_priority_fee_per_gas,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
prepared_tx = func.build_transaction(
|
|
63
|
+
cast(TxParams, {k: v for k, v in tx_params.items() if v is not None})
|
|
64
|
+
)
|
|
65
|
+
signed_tx = self._authenticator.sign_transaction(prepared_tx)
|
|
66
|
+
tx_hash = self._w3.eth.send_raw_transaction(signed_tx.raw_transaction)
|
|
67
|
+
tx_receipt = self._w3.eth.wait_for_transaction_receipt(
|
|
68
|
+
tx_hash, timeout=self._config.timeout_seconds
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
return Transaction(
|
|
72
|
+
hash=tx_hash.to_0x_hex(),
|
|
73
|
+
data=dict(prepared_tx),
|
|
74
|
+
receipt=dict(tx_receipt),
|
|
75
|
+
)
|
|
35
76
|
|
|
36
77
|
@cache
|
|
37
78
|
def _decimals(self, collateral_asset: ChecksumAddress) -> int:
|
|
@@ -39,20 +80,33 @@ class ClearingSystemAPI(ABC):
|
|
|
39
80
|
return token_contract.decimals()
|
|
40
81
|
|
|
41
82
|
|
|
42
|
-
class ExchangeAPI(ABC):
|
|
43
|
-
_account: LocalAccount
|
|
83
|
+
class ExchangeAPI(BaseAPI, ABC):
|
|
44
84
|
_exchange: ExchangeClient
|
|
45
85
|
_trading_protocol_id: str
|
|
46
86
|
|
|
47
|
-
def __init__(
|
|
48
|
-
self
|
|
49
|
-
|
|
87
|
+
def __init__(
|
|
88
|
+
self,
|
|
89
|
+
config: Config,
|
|
90
|
+
authenticator: Authenticator | None = None,
|
|
91
|
+
exchange_url: str | None = None,
|
|
92
|
+
):
|
|
93
|
+
if exchange_url is None:
|
|
94
|
+
exchange_url = config.exchange_url
|
|
95
|
+
|
|
96
|
+
super().__init__(config, authenticator)
|
|
97
|
+
self._exchange = ExchangeClient(exchange_url)
|
|
50
98
|
self._login()
|
|
51
99
|
|
|
100
|
+
def __repr__(self) -> str:
|
|
101
|
+
return (
|
|
102
|
+
f"{self.__class__.__name__}(authenticator={repr(self._authenticator)}, "
|
|
103
|
+
f"exchange={repr(self._exchange)})"
|
|
104
|
+
)
|
|
105
|
+
|
|
52
106
|
def _login(self):
|
|
53
107
|
nonce = self._exchange.generate_login_nonce()
|
|
54
|
-
message = self._generate_eip4361_message(
|
|
55
|
-
signature =
|
|
108
|
+
message = self._generate_eip4361_message(nonce)
|
|
109
|
+
signature = self._authenticator.sign_message(message.encode("ascii"))
|
|
56
110
|
|
|
57
111
|
login_submission = LoginSubmission(
|
|
58
112
|
message=message, signature=Web3.to_hex(signature)
|
|
@@ -61,14 +115,13 @@ class ExchangeAPI(ABC):
|
|
|
61
115
|
|
|
62
116
|
self._trading_protocol_id = exchange_parameters.trading_protocol_id
|
|
63
117
|
|
|
64
|
-
|
|
65
|
-
def _generate_eip4361_message(account: LocalAccount, nonce: str) -> str:
|
|
118
|
+
def _generate_eip4361_message(self, nonce: str) -> str:
|
|
66
119
|
message = SiweMessage(
|
|
67
|
-
domain=
|
|
68
|
-
address=
|
|
69
|
-
uri=
|
|
120
|
+
domain=urlparse(self._config.exchange_url).netloc,
|
|
121
|
+
address=self._authenticator.address,
|
|
122
|
+
uri=self._config.exchange_url,
|
|
70
123
|
version=siwe.VersionEnum.one, # type: ignore
|
|
71
|
-
chain_id=
|
|
124
|
+
chain_id=self._config.chain_id,
|
|
72
125
|
issued_at=ISO8601Datetime.from_datetime(datetime.now()),
|
|
73
126
|
nonce=nonce,
|
|
74
127
|
statement=None,
|