afp-sdk 0.3.0__py3-none-any.whl → 0.5.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.
- afp/__init__.py +12 -7
- afp/afp.py +210 -0
- afp/api/admin.py +1 -14
- afp/api/base.py +82 -29
- afp/api/{clearing.py → margin_account.py} +172 -104
- afp/api/{builder.py → product.py} +103 -41
- afp/api/trading.py +18 -31
- afp/auth.py +66 -0
- afp/bindings/bankruptcy_facet.py +36 -0
- afp/bindings/clearing_facet.py +28 -0
- afp/bindings/facade.py +25 -13
- afp/bindings/margin_account.py +108 -0
- afp/bindings/product_registry.py +1 -48
- afp/bindings/system_viewer.py +1 -7
- afp/config.py +22 -36
- afp/constants.py +52 -0
- afp/decorators.py +3 -1
- afp/exceptions.py +7 -3
- afp/exchange.py +15 -7
- afp/{signing.py → hashing.py} +4 -12
- afp/schemas.py +9 -1
- afp/validators.py +1 -0
- {afp_sdk-0.3.0.dist-info → afp_sdk-0.5.0.dist-info}/METADATA +54 -50
- afp_sdk-0.5.0.dist-info/RECORD +37 -0
- {afp_sdk-0.3.0.dist-info → afp_sdk-0.5.0.dist-info}/WHEEL +1 -1
- afp/api/liquidation.py +0 -167
- afp_sdk-0.3.0.dist-info/RECORD +0 -35
- {afp_sdk-0.3.0.dist-info → afp_sdk-0.5.0.dist-info}/licenses/LICENSE +0 -0
afp/bindings/margin_account.py
CHANGED
|
@@ -53,6 +53,11 @@ class MarginAccount:
|
|
|
53
53
|
abi=ABI,
|
|
54
54
|
)
|
|
55
55
|
|
|
56
|
+
@property
|
|
57
|
+
def Deposit(self) -> contract.ContractEvent:
|
|
58
|
+
"""Binding for `event Deposit` on the MarginAccount contract."""
|
|
59
|
+
return self._contract.events.Deposit
|
|
60
|
+
|
|
56
61
|
@property
|
|
57
62
|
def FeeCollected(self) -> contract.ContractEvent:
|
|
58
63
|
"""Binding for `event FeeCollected` on the MarginAccount contract."""
|
|
@@ -68,11 +73,26 @@ class MarginAccount:
|
|
|
68
73
|
"""Binding for `event Initialized` on the MarginAccount contract."""
|
|
69
74
|
return self._contract.events.Initialized
|
|
70
75
|
|
|
76
|
+
@property
|
|
77
|
+
def IntentAuthorized(self) -> contract.ContractEvent:
|
|
78
|
+
"""Binding for `event IntentAuthorized` on the MarginAccount contract."""
|
|
79
|
+
return self._contract.events.IntentAuthorized
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def IntentRevoked(self) -> contract.ContractEvent:
|
|
83
|
+
"""Binding for `event IntentRevoked` on the MarginAccount contract."""
|
|
84
|
+
return self._contract.events.IntentRevoked
|
|
85
|
+
|
|
71
86
|
@property
|
|
72
87
|
def PositionUpdated(self) -> contract.ContractEvent:
|
|
73
88
|
"""Binding for `event PositionUpdated` on the MarginAccount contract."""
|
|
74
89
|
return self._contract.events.PositionUpdated
|
|
75
90
|
|
|
91
|
+
@property
|
|
92
|
+
def Withdraw(self) -> contract.ContractEvent:
|
|
93
|
+
"""Binding for `event Withdraw` on the MarginAccount contract."""
|
|
94
|
+
return self._contract.events.Withdraw
|
|
95
|
+
|
|
76
96
|
def authorize(
|
|
77
97
|
self,
|
|
78
98
|
intent_account: eth_typing.ChecksumAddress,
|
|
@@ -789,6 +809,25 @@ ABI = typing.cast(
|
|
|
789
809
|
"name": "Unauthorized",
|
|
790
810
|
"type": "error",
|
|
791
811
|
},
|
|
812
|
+
{
|
|
813
|
+
"anonymous": False,
|
|
814
|
+
"inputs": [
|
|
815
|
+
{
|
|
816
|
+
"indexed": True,
|
|
817
|
+
"internalType": "address",
|
|
818
|
+
"name": "user",
|
|
819
|
+
"type": "address",
|
|
820
|
+
},
|
|
821
|
+
{
|
|
822
|
+
"indexed": False,
|
|
823
|
+
"internalType": "uint256",
|
|
824
|
+
"name": "amount",
|
|
825
|
+
"type": "uint256",
|
|
826
|
+
},
|
|
827
|
+
],
|
|
828
|
+
"name": "Deposit",
|
|
829
|
+
"type": "event",
|
|
830
|
+
},
|
|
792
831
|
{
|
|
793
832
|
"anonymous": False,
|
|
794
833
|
"inputs": [
|
|
@@ -840,6 +879,44 @@ ABI = typing.cast(
|
|
|
840
879
|
"name": "Initialized",
|
|
841
880
|
"type": "event",
|
|
842
881
|
},
|
|
882
|
+
{
|
|
883
|
+
"anonymous": False,
|
|
884
|
+
"inputs": [
|
|
885
|
+
{
|
|
886
|
+
"indexed": True,
|
|
887
|
+
"internalType": "address",
|
|
888
|
+
"name": "marginAccountID",
|
|
889
|
+
"type": "address",
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
"indexed": True,
|
|
893
|
+
"internalType": "address",
|
|
894
|
+
"name": "intentAccount",
|
|
895
|
+
"type": "address",
|
|
896
|
+
},
|
|
897
|
+
],
|
|
898
|
+
"name": "IntentAuthorized",
|
|
899
|
+
"type": "event",
|
|
900
|
+
},
|
|
901
|
+
{
|
|
902
|
+
"anonymous": False,
|
|
903
|
+
"inputs": [
|
|
904
|
+
{
|
|
905
|
+
"indexed": True,
|
|
906
|
+
"internalType": "address",
|
|
907
|
+
"name": "marginAccountID",
|
|
908
|
+
"type": "address",
|
|
909
|
+
},
|
|
910
|
+
{
|
|
911
|
+
"indexed": True,
|
|
912
|
+
"internalType": "address",
|
|
913
|
+
"name": "intentAccount",
|
|
914
|
+
"type": "address",
|
|
915
|
+
},
|
|
916
|
+
],
|
|
917
|
+
"name": "IntentRevoked",
|
|
918
|
+
"type": "event",
|
|
919
|
+
},
|
|
843
920
|
{
|
|
844
921
|
"anonymous": False,
|
|
845
922
|
"inputs": [
|
|
@@ -867,10 +944,41 @@ ABI = typing.cast(
|
|
|
867
944
|
"name": "costBasis",
|
|
868
945
|
"type": "int256",
|
|
869
946
|
},
|
|
947
|
+
{
|
|
948
|
+
"indexed": False,
|
|
949
|
+
"internalType": "uint256",
|
|
950
|
+
"name": "price",
|
|
951
|
+
"type": "uint256",
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
"indexed": False,
|
|
955
|
+
"internalType": "int256",
|
|
956
|
+
"name": "quantity",
|
|
957
|
+
"type": "int256",
|
|
958
|
+
},
|
|
870
959
|
],
|
|
871
960
|
"name": "PositionUpdated",
|
|
872
961
|
"type": "event",
|
|
873
962
|
},
|
|
963
|
+
{
|
|
964
|
+
"anonymous": False,
|
|
965
|
+
"inputs": [
|
|
966
|
+
{
|
|
967
|
+
"indexed": True,
|
|
968
|
+
"internalType": "address",
|
|
969
|
+
"name": "user",
|
|
970
|
+
"type": "address",
|
|
971
|
+
},
|
|
972
|
+
{
|
|
973
|
+
"indexed": False,
|
|
974
|
+
"internalType": "uint256",
|
|
975
|
+
"name": "amount",
|
|
976
|
+
"type": "uint256",
|
|
977
|
+
},
|
|
978
|
+
],
|
|
979
|
+
"name": "Withdraw",
|
|
980
|
+
"type": "event",
|
|
981
|
+
},
|
|
874
982
|
{
|
|
875
983
|
"inputs": [
|
|
876
984
|
{"internalType": "address", "name": "intentAccount", "type": "address"}
|
afp/bindings/product_registry.py
CHANGED
|
@@ -56,7 +56,6 @@ class Product:
|
|
|
56
56
|
unit_value: int
|
|
57
57
|
initial_margin_requirement: int
|
|
58
58
|
maintenance_margin_requirement: int
|
|
59
|
-
offer_price_buffer: int
|
|
60
59
|
auction_bounty: int
|
|
61
60
|
tradeout_interval: int
|
|
62
61
|
tick_size: int
|
|
@@ -221,7 +220,6 @@ class ProductRegistry:
|
|
|
221
220
|
product.unit_value,
|
|
222
221
|
product.initial_margin_requirement,
|
|
223
222
|
product.maintenance_margin_requirement,
|
|
224
|
-
product.offer_price_buffer,
|
|
225
223
|
product.auction_bounty,
|
|
226
224
|
product.tradeout_interval,
|
|
227
225
|
product.tick_size,
|
|
@@ -280,25 +278,6 @@ class ProductRegistry:
|
|
|
280
278
|
).call()
|
|
281
279
|
return int(return_value)
|
|
282
280
|
|
|
283
|
-
def offer_price_buffer(
|
|
284
|
-
self,
|
|
285
|
-
product_id: hexbytes.HexBytes,
|
|
286
|
-
) -> int:
|
|
287
|
-
"""Binding for `offerPriceBuffer` on the ProductRegistry contract.
|
|
288
|
-
|
|
289
|
-
Parameters
|
|
290
|
-
----------
|
|
291
|
-
product_id : hexbytes.HexBytes
|
|
292
|
-
|
|
293
|
-
Returns
|
|
294
|
-
-------
|
|
295
|
-
int
|
|
296
|
-
"""
|
|
297
|
-
return_value = self._contract.functions.offerPriceBuffer(
|
|
298
|
-
product_id,
|
|
299
|
-
).call()
|
|
300
|
-
return int(return_value)
|
|
301
|
-
|
|
302
281
|
def oracle_specification(
|
|
303
282
|
self,
|
|
304
283
|
product_id: hexbytes.HexBytes,
|
|
@@ -395,8 +374,7 @@ class ProductRegistry:
|
|
|
395
374
|
int(return_value[9]),
|
|
396
375
|
int(return_value[10]),
|
|
397
376
|
int(return_value[11]),
|
|
398
|
-
|
|
399
|
-
str(return_value[13]),
|
|
377
|
+
str(return_value[12]),
|
|
400
378
|
)
|
|
401
379
|
|
|
402
380
|
def proxiable_uuid(
|
|
@@ -447,7 +425,6 @@ class ProductRegistry:
|
|
|
447
425
|
product.unit_value,
|
|
448
426
|
product.initial_margin_requirement,
|
|
449
427
|
product.maintenance_margin_requirement,
|
|
450
|
-
product.offer_price_buffer,
|
|
451
428
|
product.auction_bounty,
|
|
452
429
|
product.tradeout_interval,
|
|
453
430
|
product.tick_size,
|
|
@@ -856,11 +833,6 @@ ABI = typing.cast(
|
|
|
856
833
|
"name": "maintenanceMarginRequirement",
|
|
857
834
|
"type": "uint16",
|
|
858
835
|
},
|
|
859
|
-
{
|
|
860
|
-
"internalType": "uint64",
|
|
861
|
-
"name": "offerPriceBuffer",
|
|
862
|
-
"type": "uint64",
|
|
863
|
-
},
|
|
864
836
|
{
|
|
865
837
|
"internalType": "uint64",
|
|
866
838
|
"name": "auctionBounty",
|
|
@@ -913,15 +885,6 @@ ABI = typing.cast(
|
|
|
913
885
|
"stateMutability": "view",
|
|
914
886
|
"type": "function",
|
|
915
887
|
},
|
|
916
|
-
{
|
|
917
|
-
"inputs": [
|
|
918
|
-
{"internalType": "bytes32", "name": "productId", "type": "bytes32"}
|
|
919
|
-
],
|
|
920
|
-
"name": "offerPriceBuffer",
|
|
921
|
-
"outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}],
|
|
922
|
-
"stateMutability": "view",
|
|
923
|
-
"type": "function",
|
|
924
|
-
},
|
|
925
888
|
{
|
|
926
889
|
"inputs": [
|
|
927
890
|
{"internalType": "bytes32", "name": "productId", "type": "bytes32"}
|
|
@@ -1073,11 +1036,6 @@ ABI = typing.cast(
|
|
|
1073
1036
|
"name": "maintenanceMarginRequirement",
|
|
1074
1037
|
"type": "uint16",
|
|
1075
1038
|
},
|
|
1076
|
-
{
|
|
1077
|
-
"internalType": "uint64",
|
|
1078
|
-
"name": "offerPriceBuffer",
|
|
1079
|
-
"type": "uint64",
|
|
1080
|
-
},
|
|
1081
1039
|
{
|
|
1082
1040
|
"internalType": "uint64",
|
|
1083
1041
|
"name": "auctionBounty",
|
|
@@ -1203,11 +1161,6 @@ ABI = typing.cast(
|
|
|
1203
1161
|
"name": "maintenanceMarginRequirement",
|
|
1204
1162
|
"type": "uint16",
|
|
1205
1163
|
},
|
|
1206
|
-
{
|
|
1207
|
-
"internalType": "uint64",
|
|
1208
|
-
"name": "offerPriceBuffer",
|
|
1209
|
-
"type": "uint64",
|
|
1210
|
-
},
|
|
1211
1164
|
{
|
|
1212
1165
|
"internalType": "uint64",
|
|
1213
1166
|
"name": "auctionBounty",
|
afp/bindings/system_viewer.py
CHANGED
|
@@ -324,8 +324,7 @@ class SystemViewer:
|
|
|
324
324
|
int(return_value_elem[9]),
|
|
325
325
|
int(return_value_elem[10]),
|
|
326
326
|
int(return_value_elem[11]),
|
|
327
|
-
|
|
328
|
-
str(return_value_elem[13]),
|
|
327
|
+
str(return_value_elem[12]),
|
|
329
328
|
)
|
|
330
329
|
for return_value_elem in return_value
|
|
331
330
|
]
|
|
@@ -929,11 +928,6 @@ ABI = typing.cast(
|
|
|
929
928
|
"name": "maintenanceMarginRequirement",
|
|
930
929
|
"type": "uint16",
|
|
931
930
|
},
|
|
932
|
-
{
|
|
933
|
-
"internalType": "uint64",
|
|
934
|
-
"name": "offerPriceBuffer",
|
|
935
|
-
"type": "uint64",
|
|
936
|
-
},
|
|
937
931
|
{
|
|
938
932
|
"internalType": "uint64",
|
|
939
933
|
"name": "auctionBounty",
|
afp/config.py
CHANGED
|
@@ -1,42 +1,28 @@
|
|
|
1
|
-
import
|
|
1
|
+
from dataclasses import dataclass
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from eth_typing.evm import ChecksumAddress
|
|
4
4
|
|
|
5
|
+
from .auth import Authenticator
|
|
5
6
|
|
|
6
|
-
# Constants from clearing/contracts/lib/constants.sol
|
|
7
|
-
RATE_MULTIPLIER = 10**4
|
|
8
|
-
FEE_RATE_MULTIPLIER = 10**6
|
|
9
|
-
FULL_PRECISION_MULTIPLIER = 10**18
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"AFP_EXCHANGE_URL", "https://afp-exchange-stable.up.railway.app"
|
|
15
|
-
)
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class Config:
|
|
10
|
+
authenticator: Authenticator | None
|
|
16
11
|
|
|
17
|
-
|
|
12
|
+
# Venue parameters
|
|
13
|
+
exchange_url: str
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
)
|
|
34
|
-
)
|
|
35
|
-
PRODUCT_REGISTRY_ADDRESS = Web3.to_checksum_address(
|
|
36
|
-
os.getenv(
|
|
37
|
-
"AFP_PRODUCT_REGISTRY_ADDRESS", "0x86B3829471929B115367DA0958f56A6AB844b08e"
|
|
38
|
-
)
|
|
39
|
-
)
|
|
40
|
-
SYSTEM_VIEWER_ADDRESS = Web3.to_checksum_address(
|
|
41
|
-
os.getenv("AFP_SYSTEM_VIEWER_ADDRESS", "0xfF2DFcC44a95cce96E03EfC33C65c8Be671Bae5B")
|
|
42
|
-
)
|
|
15
|
+
# Blockchain parameters
|
|
16
|
+
rpc_url: str | None
|
|
17
|
+
chain_id: int
|
|
18
|
+
gas_limit: int | None
|
|
19
|
+
max_fee_per_gas: int | None
|
|
20
|
+
max_priority_fee_per_gas: int | None
|
|
21
|
+
timeout_seconds: int
|
|
22
|
+
|
|
23
|
+
# Clearing System parameters
|
|
24
|
+
clearing_diamond_address: ChecksumAddress
|
|
25
|
+
margin_account_registry_address: ChecksumAddress
|
|
26
|
+
oracle_provider_address: ChecksumAddress
|
|
27
|
+
product_registry_address: ChecksumAddress
|
|
28
|
+
system_viewer_address: ChecksumAddress
|
afp/constants.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from types import SimpleNamespace
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def _int_or_none(value: str | None) -> int | None:
|
|
6
|
+
return int(value) if value is not None else None
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
USER_AGENT = "afp-sdk"
|
|
10
|
+
DEFAULT_EXCHANGE_API_VERSION = 1
|
|
11
|
+
|
|
12
|
+
# Constants from clearing/contracts/lib/constants.sol
|
|
13
|
+
RATE_MULTIPLIER = 10**4
|
|
14
|
+
FEE_RATE_MULTIPLIER = 10**6
|
|
15
|
+
FULL_PRECISION_MULTIPLIER = 10**18
|
|
16
|
+
|
|
17
|
+
defaults = SimpleNamespace(
|
|
18
|
+
# Authentication parameters
|
|
19
|
+
KEYFILE=os.getenv("AFP_KEYFILE", None),
|
|
20
|
+
KEYFILE_PASSWORD=os.getenv("AFP_KEYFILE_PASSWORD", ""),
|
|
21
|
+
PRIVATE_KEY=os.getenv("AFP_PRIVATE_KEY", None),
|
|
22
|
+
# Venue parameters
|
|
23
|
+
EXCHANGE_URL=os.getenv(
|
|
24
|
+
"AFP_EXCHANGE_URL", "https://afp-exchange-stable.up.railway.app"
|
|
25
|
+
),
|
|
26
|
+
# Blockchain parameters
|
|
27
|
+
RPC_URL=os.getenv("AFP_RPC_URL", None),
|
|
28
|
+
CHAIN_ID=int(os.getenv("AFP_CHAIN_ID", 65000000)),
|
|
29
|
+
GAS_LIMIT=_int_or_none(os.getenv("AFP_GAS_LIMIT", None)),
|
|
30
|
+
MAX_FEE_PER_GAS=_int_or_none(os.getenv("AFP_MAX_FEE_PER_GAS", None)),
|
|
31
|
+
MAX_PRIORITY_FEE_PER_GAS=_int_or_none(
|
|
32
|
+
os.getenv("AFP_MAX_PRIORITY_FEE_PER_GAS", None)
|
|
33
|
+
),
|
|
34
|
+
TIMEOUT_SECONDS=int(os.getenv("AFP_TIMEOUT_SECONDS", 10)),
|
|
35
|
+
# Clearing System parameters
|
|
36
|
+
CLEARING_DIAMOND_ADDRESS=os.getenv(
|
|
37
|
+
"AFP_CLEARING_DIAMOND_ADDRESS", "0x5B5411F1548254d25360d71FE40cFc1cC983B2A2"
|
|
38
|
+
),
|
|
39
|
+
MARGIN_ACCOUNT_REGISTRY_ADDRESS=os.getenv(
|
|
40
|
+
"AFP_MARGIN_ACCOUNT_REGISTRY_ADDRESS",
|
|
41
|
+
"0x99f4FA9Cdce7AD227eB84907936a8FeF2095D846",
|
|
42
|
+
),
|
|
43
|
+
ORACLE_PROVIDER_ADDRESS=os.getenv(
|
|
44
|
+
"AFP_ORACLE_PROVIDER_ADDRESS", "0xF2A2A27da33D30B4BF38D7e186E7B0b1e964e55c"
|
|
45
|
+
),
|
|
46
|
+
PRODUCT_REGISTRY_ADDRESS=os.getenv(
|
|
47
|
+
"AFP_PRODUCT_REGISTRY_ADDRESS", "0x86B3829471929B115367DA0958f56A6AB844b08e"
|
|
48
|
+
),
|
|
49
|
+
SYSTEM_VIEWER_ADDRESS=os.getenv(
|
|
50
|
+
"AFP_SYSTEM_VIEWER_ADDRESS", "0xfF2DFcC44a95cce96E03EfC33C65c8Be671Bae5B"
|
|
51
|
+
),
|
|
52
|
+
)
|
afp/decorators.py
CHANGED
|
@@ -45,8 +45,10 @@ def convert_web3_error(*contract_abis: ABI) -> Callable[..., Any]:
|
|
|
45
45
|
)
|
|
46
46
|
if reason == "no data":
|
|
47
47
|
reason = "Unspecified reason"
|
|
48
|
+
if reason is None:
|
|
49
|
+
reason = "Unknown error"
|
|
48
50
|
raise ClearingSystemError(
|
|
49
|
-
"Contract call reverted" + f": {reason}" if reason else ""
|
|
51
|
+
"Contract call reverted" + (f": {reason}" if reason else "")
|
|
50
52
|
) from contract_error
|
|
51
53
|
except Web3RPCError as rpc_error:
|
|
52
54
|
reason = None
|
afp/exceptions.py
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
class
|
|
1
|
+
class AFPException(Exception):
|
|
2
2
|
pass
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
class
|
|
5
|
+
class ConfigurationError(AFPException):
|
|
6
6
|
pass
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class
|
|
9
|
+
class ClearingSystemError(AFPException):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ExchangeError(AFPException):
|
|
10
14
|
pass
|
|
11
15
|
|
|
12
16
|
|
afp/exchange.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import re
|
|
2
3
|
from typing import Any, Generator
|
|
3
4
|
|
|
4
5
|
import requests
|
|
5
6
|
from requests import Response, Session
|
|
6
7
|
|
|
7
|
-
from . import
|
|
8
|
+
from . import constants
|
|
8
9
|
from .exceptions import (
|
|
9
10
|
AuthenticationError,
|
|
10
11
|
AuthorizationError,
|
|
@@ -26,11 +27,16 @@ from .schemas import (
|
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
class ExchangeClient:
|
|
30
|
+
_base_url: str
|
|
29
31
|
_session: Session
|
|
30
32
|
|
|
31
|
-
def __init__(self):
|
|
33
|
+
def __init__(self, base_url: str):
|
|
34
|
+
self._base_url = re.sub(r"/$", "", base_url)
|
|
32
35
|
self._session = Session()
|
|
33
36
|
|
|
37
|
+
def __repr__(self) -> str:
|
|
38
|
+
return f"{self.__class__.__name__}(base_url={self._base_url})"
|
|
39
|
+
|
|
34
40
|
# POST /nonce
|
|
35
41
|
def generate_login_nonce(self) -> str:
|
|
36
42
|
response = self._send_request("GET", "/nonce")
|
|
@@ -71,8 +77,10 @@ class ExchangeClient:
|
|
|
71
77
|
return Order(**response.json())
|
|
72
78
|
|
|
73
79
|
# GET /orders
|
|
74
|
-
def get_open_orders(self) -> list[Order]:
|
|
75
|
-
response = self._send_request(
|
|
80
|
+
def get_open_orders(self, product_id: str | None = None) -> list[Order]:
|
|
81
|
+
response = self._send_request(
|
|
82
|
+
"GET", "/orders", params=({"product_id": product_id} if product_id else {})
|
|
83
|
+
)
|
|
76
84
|
return [Order(**item) for item in response.json()["orders"]]
|
|
77
85
|
|
|
78
86
|
# GET /orders/{order_id}
|
|
@@ -121,19 +129,19 @@ class ExchangeClient:
|
|
|
121
129
|
endpoint: str,
|
|
122
130
|
*,
|
|
123
131
|
stream: bool = False,
|
|
124
|
-
api_version: int =
|
|
132
|
+
api_version: int = constants.DEFAULT_EXCHANGE_API_VERSION,
|
|
125
133
|
**kwargs: Any,
|
|
126
134
|
) -> Response:
|
|
127
135
|
kwargs["headers"] = {
|
|
128
136
|
"Content-Type": "application/json",
|
|
129
137
|
"Accept": "application/x-ndjson" if stream else "application/json",
|
|
130
|
-
"User-Agent":
|
|
138
|
+
"User-Agent": constants.USER_AGENT,
|
|
131
139
|
}
|
|
132
140
|
|
|
133
141
|
try:
|
|
134
142
|
response = self._session.request(
|
|
135
143
|
method,
|
|
136
|
-
f"{
|
|
144
|
+
f"{self._base_url}/v{api_version}{endpoint}",
|
|
137
145
|
stream=stream,
|
|
138
146
|
**kwargs,
|
|
139
147
|
)
|
afp/{signing.py → hashing.py}
RENAMED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
from typing import Any, cast
|
|
2
2
|
|
|
3
3
|
from eth_abi.packed import encode_packed
|
|
4
|
-
from eth_account import messages
|
|
5
4
|
from eth_typing.evm import ChecksumAddress
|
|
6
|
-
from eth_account.signers.local import LocalAccount
|
|
7
5
|
from hexbytes import HexBytes
|
|
8
6
|
from web3 import Web3
|
|
9
7
|
|
|
10
|
-
from . import
|
|
8
|
+
from . import constants
|
|
11
9
|
from .bindings import clearing_facet
|
|
12
10
|
from .schemas import IntentData, OrderSide
|
|
13
11
|
|
|
@@ -44,7 +42,7 @@ def generate_intent_hash(
|
|
|
44
42
|
HexBytes(intent_data.product_id),
|
|
45
43
|
int(intent_data.limit_price * 10**tick_size),
|
|
46
44
|
intent_data.quantity,
|
|
47
|
-
int(intent_data.max_trading_fee_rate *
|
|
45
|
+
int(intent_data.max_trading_fee_rate * constants.FEE_RATE_MULTIPLIER),
|
|
48
46
|
int(intent_data.good_until_time.timestamp()),
|
|
49
47
|
ORDER_SIDE_MAPPING[intent_data.side],
|
|
50
48
|
]
|
|
@@ -57,13 +55,7 @@ def generate_order_cancellation_hash(nonce: int, intent_hash: str) -> HexBytes:
|
|
|
57
55
|
return Web3.keccak(encode_packed(types, values))
|
|
58
56
|
|
|
59
57
|
|
|
60
|
-
def generate_product_id(
|
|
58
|
+
def generate_product_id(builder_address: ChecksumAddress, symbol: str) -> HexBytes:
|
|
61
59
|
types = ["address", "string"]
|
|
62
|
-
values: list[Any] = [
|
|
60
|
+
values: list[Any] = [builder_address, symbol]
|
|
63
61
|
return Web3.keccak(encode_packed(types, values))
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def sign_message(account: LocalAccount, message: bytes) -> HexBytes:
|
|
67
|
-
eip191_message = messages.encode_defunct(message)
|
|
68
|
-
signed_message = account.sign_message(eip191_message)
|
|
69
|
-
return signed_message.signature
|
afp/schemas.py
CHANGED
|
@@ -66,6 +66,9 @@ class ExchangeProduct(Model):
|
|
|
66
66
|
tick_size: int
|
|
67
67
|
collateral_asset: str
|
|
68
68
|
|
|
69
|
+
def __str__(self) -> str:
|
|
70
|
+
return self.id
|
|
71
|
+
|
|
69
72
|
|
|
70
73
|
class IntentData(Model):
|
|
71
74
|
trading_protocol_id: str
|
|
@@ -152,6 +155,12 @@ class MarketDepthData(Model):
|
|
|
152
155
|
# Clearing API
|
|
153
156
|
|
|
154
157
|
|
|
158
|
+
class Transaction(Model):
|
|
159
|
+
hash: str
|
|
160
|
+
data: dict[str, Any]
|
|
161
|
+
receipt: dict[str, Any]
|
|
162
|
+
|
|
163
|
+
|
|
155
164
|
class Position(Model):
|
|
156
165
|
id: str
|
|
157
166
|
quantity: int
|
|
@@ -184,7 +193,6 @@ class ProductSpecification(Model):
|
|
|
184
193
|
unit_value: Annotated[Decimal, Field(gt=0)]
|
|
185
194
|
initial_margin_requirement: Annotated[Decimal, Field(gt=0)]
|
|
186
195
|
maintenance_margin_requirement: Annotated[Decimal, Field(gt=0)]
|
|
187
|
-
offer_price_buffer: Annotated[Decimal, Field(ge=0, lt=1)]
|
|
188
196
|
auction_bounty: Annotated[Decimal, Field(ge=0, le=1)]
|
|
189
197
|
tradeout_interval: Annotated[int, Field(ge=0)]
|
|
190
198
|
extended_metadata: str
|
afp/validators.py
CHANGED