afp-sdk 0.5.4__py3-none-any.whl → 0.6.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 +4 -1
- afp/afp.py +11 -0
- afp/api/admin.py +1 -1
- afp/api/base.py +11 -2
- afp/api/margin_account.py +12 -148
- afp/api/product.py +302 -148
- afp/api/trading.py +10 -19
- afp/auth.py +14 -0
- afp/bindings/__init__.py +30 -16
- afp/bindings/admin_facet.py +890 -0
- afp/bindings/clearing_facet.py +356 -773
- afp/bindings/facade.py +9 -6
- afp/bindings/final_settlement_facet.py +258 -99
- afp/bindings/margin_account.py +524 -839
- afp/bindings/margin_account_facet.py +722 -0
- afp/bindings/margin_account_registry.py +184 -310
- afp/bindings/mark_price_tracker_facet.py +74 -16
- afp/bindings/product_registry.py +1577 -541
- afp/bindings/product_registry_facet.py +1467 -0
- afp/bindings/system_viewer.py +592 -369
- afp/bindings/types.py +223 -0
- afp/config.py +4 -0
- afp/constants.py +49 -6
- afp/decorators.py +25 -3
- afp/dtos.py +142 -0
- afp/exceptions.py +10 -0
- afp/exchange.py +10 -8
- afp/hashing.py +7 -5
- afp/ipfs.py +245 -0
- afp/json-schemas/bafyreiaw34o6l3rmatabzbds2i2myazdw2yolevcpsoyd2i2g3ms7wa2eq.json +1 -0
- afp/json-schemas/bafyreibnfg6nq74dvpkre5rakkccij7iadp5rxpim7omsatjnrpmj3y7v4.json +1 -0
- afp/json-schemas/bafyreicgr6dfo5yduixjkcifghiulskfegwojvuwodtouvivl362zndhxe.json +1 -0
- afp/json-schemas/bafyreicheoypx6synljushh7mq2572iyhlolf4nake2p5dwobgnj3r5eua.json +1 -0
- afp/json-schemas/bafyreid35a67db4sqh4fs6boddyt2xvscbqy6nqvsp5jjur56qhkw4ixre.json +1 -0
- afp/json-schemas/bafyreidzs7okcpqiss6ztftltyptqwnw5e5opsy5yntospekjha4kpykaa.json +1 -0
- afp/json-schemas/bafyreifcec2km7hxwq6oqzjlspni2mgipetjb7pqtaewh2efislzoctboi.json +1 -0
- afp/json-schemas/bafyreihn3oiaxffe4e2w7pwtreadpw3obfd7gqlogbcxm56jc2hzfvco74.json +1 -0
- afp/json-schemas/bafyreihur3dzwhja6uxsbcw6eeoj3xmmc4e3zkmyzpot5v5dleevxe5zam.json +1 -0
- afp/schemas.py +236 -177
- afp/types.py +169 -0
- afp/validators.py +218 -8
- {afp_sdk-0.5.4.dist-info → afp_sdk-0.6.1.dist-info}/METADATA +76 -11
- afp_sdk-0.6.1.dist-info/RECORD +50 -0
- afp/bindings/auctioneer_facet.py +0 -752
- afp/bindings/bankruptcy_facet.py +0 -391
- afp/bindings/trading_protocol.py +0 -1158
- afp_sdk-0.5.4.dist-info/RECORD +0 -37
- {afp_sdk-0.5.4.dist-info → afp_sdk-0.6.1.dist-info}/WHEEL +0 -0
- {afp_sdk-0.5.4.dist-info → afp_sdk-0.6.1.dist-info}/licenses/LICENSE +0 -0
afp/bindings/types.py
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
import typing
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
import eth_typing
|
|
6
|
+
import hexbytes
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Side(enum.IntEnum):
|
|
10
|
+
"""Port of `enum Side` on the ClearingFacet contract."""
|
|
11
|
+
|
|
12
|
+
BID = 0
|
|
13
|
+
ASK = 1
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ProductState(enum.IntEnum):
|
|
17
|
+
"""Port of `enum ProductState` on the ProductRegistry contract."""
|
|
18
|
+
|
|
19
|
+
NOT_EXIST = 0
|
|
20
|
+
PENDING = 1
|
|
21
|
+
LIVE = 2
|
|
22
|
+
TRADEOUT = 3
|
|
23
|
+
FINAL_SETTLEMENT = 4
|
|
24
|
+
EXPIRED = 5
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class ClearingConfig:
|
|
29
|
+
"""Port of `struct ClearingConfig` on the AdminFacet contract."""
|
|
30
|
+
|
|
31
|
+
clearing_fee_rate: int
|
|
32
|
+
max_trading_fee_rate: int
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class MarkPriceConfig:
|
|
37
|
+
"""Port of `struct MarkPriceConfig` on the AdminFacet contract."""
|
|
38
|
+
|
|
39
|
+
mark_price_interval: int
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class FinalSettlementConfig:
|
|
44
|
+
"""Port of `struct FinalSettlementConfig` on the AdminFacet contract."""
|
|
45
|
+
|
|
46
|
+
closeout_fee_rate: int
|
|
47
|
+
closeout_reward_rate: int
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class ProductConfig:
|
|
52
|
+
"""Port of `struct ProductConfig` on the AdminFacet contract."""
|
|
53
|
+
|
|
54
|
+
clearing_payout_ratio: int
|
|
55
|
+
maintenance_deposit_interval: int
|
|
56
|
+
min_product_maintenance_fee: int
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass
|
|
60
|
+
class Config:
|
|
61
|
+
"""Port of `struct Config` on the AdminFacet contract."""
|
|
62
|
+
|
|
63
|
+
clearing_config: ClearingConfig
|
|
64
|
+
mark_price_config: MarkPriceConfig
|
|
65
|
+
final_settlement_config: FinalSettlementConfig
|
|
66
|
+
product_config: ProductConfig
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass
|
|
70
|
+
class IntentData:
|
|
71
|
+
"""Port of `struct IntentData` on the ClearingFacet contract."""
|
|
72
|
+
|
|
73
|
+
nonce: int
|
|
74
|
+
trading_protocol_id: eth_typing.ChecksumAddress
|
|
75
|
+
product_id: hexbytes.HexBytes
|
|
76
|
+
limit_price: int
|
|
77
|
+
quantity: int
|
|
78
|
+
max_trading_fee_rate: int
|
|
79
|
+
good_until: int
|
|
80
|
+
side: Side
|
|
81
|
+
referral: eth_typing.ChecksumAddress
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass
|
|
85
|
+
class Intent:
|
|
86
|
+
"""Port of `struct Intent` on the ClearingFacet contract."""
|
|
87
|
+
|
|
88
|
+
margin_account_id: eth_typing.ChecksumAddress
|
|
89
|
+
intent_account_id: eth_typing.ChecksumAddress
|
|
90
|
+
hash: hexbytes.HexBytes
|
|
91
|
+
data: IntentData
|
|
92
|
+
signature: hexbytes.HexBytes
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@dataclass
|
|
96
|
+
class Trade:
|
|
97
|
+
"""Port of `struct Trade` on the ClearingFacet contract."""
|
|
98
|
+
|
|
99
|
+
product_id: hexbytes.HexBytes
|
|
100
|
+
protocol_id: eth_typing.ChecksumAddress
|
|
101
|
+
trade_id: int
|
|
102
|
+
price: int
|
|
103
|
+
timestamp: int
|
|
104
|
+
quantity: int
|
|
105
|
+
accounts: typing.List[eth_typing.ChecksumAddress]
|
|
106
|
+
quantities: typing.List[int]
|
|
107
|
+
fee_rates: typing.List[int]
|
|
108
|
+
intents: typing.List[Intent]
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@dataclass
|
|
112
|
+
class Settlement:
|
|
113
|
+
"""Port of `struct Settlement` on the MarginAccountFacet contract."""
|
|
114
|
+
|
|
115
|
+
product_id: hexbytes.HexBytes
|
|
116
|
+
quantity: int
|
|
117
|
+
price: int
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@dataclass
|
|
121
|
+
class MarginData:
|
|
122
|
+
"""Port of `struct MarginData` on the IMarginAccountFacet contract."""
|
|
123
|
+
|
|
124
|
+
capital: int
|
|
125
|
+
mae: int
|
|
126
|
+
mmu: int
|
|
127
|
+
pnl: int
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@dataclass
|
|
131
|
+
class PositionData:
|
|
132
|
+
"""Port of `struct PositionData` on the MarginAccountFacet contract."""
|
|
133
|
+
|
|
134
|
+
product_id: hexbytes.HexBytes
|
|
135
|
+
quantity: int
|
|
136
|
+
cost_basis: int
|
|
137
|
+
maintenance_margin: int
|
|
138
|
+
pnl: int
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass
|
|
142
|
+
class ExpirySpecification:
|
|
143
|
+
"""Port of `struct ExpirySpecification` on the ProductRegistryFacet contract."""
|
|
144
|
+
|
|
145
|
+
earliest_fsp_submission_time: int
|
|
146
|
+
tradeout_interval: int
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@dataclass
|
|
150
|
+
class PendingStakeData:
|
|
151
|
+
"""Port of `struct PendingStakeData` on the ProductRegistry contract."""
|
|
152
|
+
|
|
153
|
+
payout: int
|
|
154
|
+
active_at: int
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@dataclass
|
|
158
|
+
class BuilderStakeData:
|
|
159
|
+
"""Port of `struct BuilderStakeData` on the ProductRegistry contract."""
|
|
160
|
+
|
|
161
|
+
payout_remaining: int
|
|
162
|
+
claimable_rewards: int
|
|
163
|
+
pending_stake_data: PendingStakeData
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@dataclass
|
|
167
|
+
class ProductMetadata:
|
|
168
|
+
"""Port of `struct ProductMetadata` on the ProductRegistryFacet contract."""
|
|
169
|
+
|
|
170
|
+
builder: eth_typing.ChecksumAddress
|
|
171
|
+
symbol: str
|
|
172
|
+
description: str
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@dataclass
|
|
176
|
+
class OracleSpecification:
|
|
177
|
+
"""Port of `struct OracleSpecification` on the ProductRegistryFacet contract."""
|
|
178
|
+
|
|
179
|
+
oracle_address: eth_typing.ChecksumAddress
|
|
180
|
+
fsv_decimals: int
|
|
181
|
+
fsp_alpha: int
|
|
182
|
+
fsp_beta: int
|
|
183
|
+
fsv_calldata: hexbytes.HexBytes
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@dataclass
|
|
187
|
+
class BaseProduct:
|
|
188
|
+
"""Port of `struct BaseProduct` on the ProductRegistryFacet contract."""
|
|
189
|
+
|
|
190
|
+
metadata: ProductMetadata
|
|
191
|
+
oracle_spec: OracleSpecification
|
|
192
|
+
collateral_asset: eth_typing.ChecksumAddress
|
|
193
|
+
start_time: int
|
|
194
|
+
point_value: int
|
|
195
|
+
price_decimals: int
|
|
196
|
+
extended_metadata: str
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@dataclass
|
|
200
|
+
class MarginSpecification:
|
|
201
|
+
"""Port of `struct MarginSpecification` on the ProductRegistryFacet contract."""
|
|
202
|
+
|
|
203
|
+
imr: int
|
|
204
|
+
mmr: int
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@dataclass
|
|
208
|
+
class FuturesProductV1:
|
|
209
|
+
"""Port of `struct FuturesProductV1` on the ProductRegistryFacet contract."""
|
|
210
|
+
|
|
211
|
+
base: BaseProduct
|
|
212
|
+
expiry_spec: ExpirySpecification
|
|
213
|
+
margin_spec: MarginSpecification
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@dataclass
|
|
217
|
+
class PredictionProductV1:
|
|
218
|
+
"""Port of `struct PredictionProductV1` on the ProductRegistryFacet contract."""
|
|
219
|
+
|
|
220
|
+
base: BaseProduct
|
|
221
|
+
expiry_spec: ExpirySpecification
|
|
222
|
+
max_price: int
|
|
223
|
+
min_price: int
|
afp/config.py
CHANGED
afp/constants.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from datetime import timedelta
|
|
2
3
|
from importlib import metadata
|
|
3
4
|
from types import SimpleNamespace
|
|
4
5
|
|
|
@@ -17,6 +18,45 @@ RATE_MULTIPLIER = 10**4
|
|
|
17
18
|
FEE_RATE_MULTIPLIER = 10**6
|
|
18
19
|
FULL_PRECISION_MULTIPLIER = 10**18
|
|
19
20
|
|
|
21
|
+
# IPFS client constants
|
|
22
|
+
IPFS_CID_ENCODING = "base32"
|
|
23
|
+
IPFS_REQUEST_TIMEOUT = 30
|
|
24
|
+
JSON_SCHEMAS_DIRECTORY = os.path.join(os.path.dirname(__file__), "json-schemas")
|
|
25
|
+
|
|
26
|
+
# Product schema constants
|
|
27
|
+
MINIMUM_ORACLE_FALLBACK_INTERVAL = timedelta(days=7)
|
|
28
|
+
MONTH_CODES = {
|
|
29
|
+
"F": 1,
|
|
30
|
+
"G": 2,
|
|
31
|
+
"H": 3,
|
|
32
|
+
"J": 4,
|
|
33
|
+
"K": 5,
|
|
34
|
+
"M": 6,
|
|
35
|
+
"N": 7,
|
|
36
|
+
"Q": 8,
|
|
37
|
+
"U": 9,
|
|
38
|
+
"V": 10,
|
|
39
|
+
"X": 11,
|
|
40
|
+
"Z": 12,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
schema_cids = SimpleNamespace(
|
|
44
|
+
# afp-product-schemas v0.2.0
|
|
45
|
+
ORACLE_CONFIG_V020="bafyreifcec2km7hxwq6oqzjlspni2mgipetjb7pqtaewh2efislzoctboi",
|
|
46
|
+
ORACLE_CONFIG_PROTOTYPE1_V020="bafyreiaw34o6l3rmatabzbds2i2myazdw2yolevcpsoyd2i2g3ms7wa2eq",
|
|
47
|
+
ORACLE_FALLBACK_V020="bafyreicgr6dfo5yduixjkcifghiulskfegwojvuwodtouvivl362zndhxe",
|
|
48
|
+
OUTCOME_POINT_V020="bafyreibnfg6nq74dvpkre5rakkccij7iadp5rxpim7omsatjnrpmj3y7v4",
|
|
49
|
+
OUTCOME_POINT_EVENT_V020="bafyreihur3dzwhja6uxsbcw6eeoj3xmmc4e3zkmyzpot5v5dleevxe5zam",
|
|
50
|
+
OUTCOME_POINT_TIME_SERIES_V020=(
|
|
51
|
+
"bafyreidzs7okcpqiss6ztftltyptqwnw5e5opsy5yntospekjha4kpykaa"
|
|
52
|
+
),
|
|
53
|
+
OUTCOME_SPACE_V020="bafyreicheoypx6synljushh7mq2572iyhlolf4nake2p5dwobgnj3r5eua",
|
|
54
|
+
OUTCOME_SPACE_SCALAR_V020="bafyreihn3oiaxffe4e2w7pwtreadpw3obfd7gqlogbcxm56jc2hzfvco74",
|
|
55
|
+
OUTCOME_SPACE_TIME_SERIES_V020=(
|
|
56
|
+
"bafyreid35a67db4sqh4fs6boddyt2xvscbqy6nqvsp5jjur56qhkw4ixre"
|
|
57
|
+
),
|
|
58
|
+
)
|
|
59
|
+
|
|
20
60
|
defaults = SimpleNamespace(
|
|
21
61
|
# Authentication parameters
|
|
22
62
|
KEYFILE=os.getenv("AFP_KEYFILE", None),
|
|
@@ -24,8 +64,11 @@ defaults = SimpleNamespace(
|
|
|
24
64
|
PRIVATE_KEY=os.getenv("AFP_PRIVATE_KEY", None),
|
|
25
65
|
# Venue parameters
|
|
26
66
|
EXCHANGE_URL=os.getenv(
|
|
27
|
-
"AFP_EXCHANGE_URL", "https://afp-exchange-stable.up.railway.app"
|
|
67
|
+
"AFP_EXCHANGE_URL", "https://afp-exchange-stable.up.railway.app/"
|
|
28
68
|
),
|
|
69
|
+
# IPFS client parameters
|
|
70
|
+
IPFS_API_URL=os.getenv("AFP_IPFS_API_URL", "http://localhost:5001"),
|
|
71
|
+
IPFS_API_KEY=os.getenv("AFP_IPFS_API_KEY", None),
|
|
29
72
|
# Blockchain parameters
|
|
30
73
|
RPC_URL=os.getenv("AFP_RPC_URL", None),
|
|
31
74
|
CHAIN_ID=int(os.getenv("AFP_CHAIN_ID", 65000000)),
|
|
@@ -37,19 +80,19 @@ defaults = SimpleNamespace(
|
|
|
37
80
|
TIMEOUT_SECONDS=int(os.getenv("AFP_TIMEOUT_SECONDS", 10)),
|
|
38
81
|
# Clearing System parameters
|
|
39
82
|
CLEARING_DIAMOND_ADDRESS=os.getenv(
|
|
40
|
-
"AFP_CLEARING_DIAMOND_ADDRESS", "
|
|
83
|
+
"AFP_CLEARING_DIAMOND_ADDRESS", "0x08e725d38BCABCf62806E84bE044dF67E8b476Ae"
|
|
41
84
|
),
|
|
42
85
|
MARGIN_ACCOUNT_REGISTRY_ADDRESS=os.getenv(
|
|
43
86
|
"AFP_MARGIN_ACCOUNT_REGISTRY_ADDRESS",
|
|
44
|
-
"
|
|
87
|
+
"0x1FAa342Ea34332ee1186FE48f03894F70F7fFc4f",
|
|
45
88
|
),
|
|
46
89
|
ORACLE_PROVIDER_ADDRESS=os.getenv(
|
|
47
|
-
"AFP_ORACLE_PROVIDER_ADDRESS", "
|
|
90
|
+
"AFP_ORACLE_PROVIDER_ADDRESS", "0x06CaDDDf6CC08048596aE051c8ce644725219C73"
|
|
48
91
|
),
|
|
49
92
|
PRODUCT_REGISTRY_ADDRESS=os.getenv(
|
|
50
|
-
"AFP_PRODUCT_REGISTRY_ADDRESS", "
|
|
93
|
+
"AFP_PRODUCT_REGISTRY_ADDRESS", "0xB605fc6B76eA4592CaA3AEd26EFd3edbAc5A3A24"
|
|
51
94
|
),
|
|
52
95
|
SYSTEM_VIEWER_ADDRESS=os.getenv(
|
|
53
|
-
"AFP_SYSTEM_VIEWER_ADDRESS", "
|
|
96
|
+
"AFP_SYSTEM_VIEWER_ADDRESS", "0xBeC22149650CcaC2a1233c7b489aFF3ea24df36d"
|
|
54
97
|
),
|
|
55
98
|
)
|
afp/decorators.py
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
from collections.abc import Callable
|
|
2
2
|
from itertools import chain
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any, cast
|
|
4
4
|
|
|
5
5
|
import inflection
|
|
6
6
|
from decorator import decorator
|
|
7
|
-
from
|
|
7
|
+
from eth_abi.exceptions import InsufficientDataBytes
|
|
8
|
+
from eth_typing.abi import ABI, ABIError, ABIFunction
|
|
9
|
+
from eth_typing.encoding import HexStr
|
|
8
10
|
from eth_utils import abi
|
|
9
11
|
from hexbytes import HexBytes
|
|
10
12
|
from web3.exceptions import (
|
|
@@ -13,6 +15,7 @@ from web3.exceptions import (
|
|
|
13
15
|
Web3Exception,
|
|
14
16
|
Web3RPCError,
|
|
15
17
|
)
|
|
18
|
+
from web3._utils import contracts, normalizers
|
|
16
19
|
|
|
17
20
|
from .api.base import ExchangeAPI
|
|
18
21
|
from .exceptions import ClearingSystemError, AuthenticationError
|
|
@@ -71,6 +74,25 @@ def _decode_custom_error(data: str, *contract_abis: ABI) -> str | None:
|
|
|
71
74
|
selector_candidate = abi.function_signature_to_4byte_selector(signature)
|
|
72
75
|
if selector == selector_candidate:
|
|
73
76
|
error = error_candidate["name"]
|
|
77
|
+
args = _decode_transaction_args(error_candidate, data)
|
|
74
78
|
# Convert 'ErrorType' to 'Error type'
|
|
75
|
-
|
|
79
|
+
error = inflection.humanize(inflection.underscore(error))
|
|
80
|
+
if args:
|
|
81
|
+
error += ": " + ", ".join(f"{key}='{val}'" for key, val in args.items())
|
|
82
|
+
return error
|
|
76
83
|
return None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _decode_transaction_args(error_abi: ABIError, data: str) -> dict[str, str]:
|
|
87
|
+
try:
|
|
88
|
+
args = contracts.decode_transaction_data( # type: ignore
|
|
89
|
+
cast(ABIFunction, error_abi),
|
|
90
|
+
cast(HexStr, data),
|
|
91
|
+
normalizers=normalizers.BASE_RETURN_NORMALIZERS, # type: ignore
|
|
92
|
+
)
|
|
93
|
+
except InsufficientDataBytes:
|
|
94
|
+
return {}
|
|
95
|
+
return {
|
|
96
|
+
key: HexBytes(val).to_0x_hex() if isinstance(val, bytes) else str(val)
|
|
97
|
+
for key, val in args.items()
|
|
98
|
+
}
|
afp/dtos.py
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Internal schemas."""
|
|
2
|
+
|
|
3
|
+
from decimal import Decimal
|
|
4
|
+
from typing import Annotated, Literal
|
|
5
|
+
|
|
6
|
+
from pydantic import AfterValidator, ConfigDict, Field, computed_field
|
|
7
|
+
|
|
8
|
+
from . import validators
|
|
9
|
+
from .enums import ListingState, OrderSide, OrderState, OrderType, TradeState
|
|
10
|
+
from .schemas import (
|
|
11
|
+
Intent,
|
|
12
|
+
OracleConfig,
|
|
13
|
+
OracleConfigPrototype1,
|
|
14
|
+
OracleFallback,
|
|
15
|
+
OrderCancellationData,
|
|
16
|
+
OutcomePoint,
|
|
17
|
+
OutcomePointEvent,
|
|
18
|
+
OutcomePointTimeSeries,
|
|
19
|
+
OutcomeSpace,
|
|
20
|
+
OutcomeSpaceScalar,
|
|
21
|
+
OutcomeSpaceTimeSeries,
|
|
22
|
+
Timestamp,
|
|
23
|
+
)
|
|
24
|
+
from .types import IPLD_LINK, AliasedModel, Model
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class PaginationFilter(Model):
|
|
28
|
+
batch: Annotated[None | int, Field(gt=0, exclude=True)]
|
|
29
|
+
batch_size: Annotated[None | int, Field(gt=0, exclude=True)]
|
|
30
|
+
newest_first: Annotated[None | bool, Field(exclude=True)]
|
|
31
|
+
|
|
32
|
+
@computed_field
|
|
33
|
+
@property
|
|
34
|
+
def page(self) -> None | int:
|
|
35
|
+
return self.batch
|
|
36
|
+
|
|
37
|
+
@computed_field
|
|
38
|
+
@property
|
|
39
|
+
def page_size(self) -> None | int:
|
|
40
|
+
return self.batch_size
|
|
41
|
+
|
|
42
|
+
@computed_field
|
|
43
|
+
@property
|
|
44
|
+
def sort(self) -> None | Literal["ASC", "DESC"]:
|
|
45
|
+
match self.newest_first:
|
|
46
|
+
case None:
|
|
47
|
+
return None
|
|
48
|
+
case True:
|
|
49
|
+
return "DESC"
|
|
50
|
+
case False:
|
|
51
|
+
return "ASC"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# Authentication
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class LoginSubmission(AliasedModel):
|
|
58
|
+
message: str
|
|
59
|
+
signature: str
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ExchangeParameters(AliasedModel):
|
|
63
|
+
trading_protocol_id: str
|
|
64
|
+
maker_trading_fee_rate: Decimal
|
|
65
|
+
taker_trading_fee_rate: Decimal
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# Admin API
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ExchangeProductListingSubmission(AliasedModel):
|
|
72
|
+
id: Annotated[str, AfterValidator(validators.validate_hexstr32)]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class ExchangeProductUpdateSubmission(AliasedModel):
|
|
76
|
+
listing_state: ListingState
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# Trading API
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class ExchangeProductFilter(PaginationFilter):
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class OrderFilter(PaginationFilter):
|
|
87
|
+
intent_account_id: str
|
|
88
|
+
product_id: None | Annotated[str, AfterValidator(validators.validate_hexstr32)]
|
|
89
|
+
type: None | OrderType
|
|
90
|
+
states: Annotated[list[OrderState], Field(exclude=True)]
|
|
91
|
+
side: None | OrderSide
|
|
92
|
+
start: None | Timestamp
|
|
93
|
+
end: None | Timestamp
|
|
94
|
+
|
|
95
|
+
@computed_field
|
|
96
|
+
@property
|
|
97
|
+
def state(self) -> str | None:
|
|
98
|
+
return ",".join(self.states) if self.states else None
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class OrderSubmission(AliasedModel):
|
|
102
|
+
type: OrderType
|
|
103
|
+
intent: Intent | None = None
|
|
104
|
+
cancellation_data: OrderCancellationData | None = None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class OrderFillFilter(PaginationFilter):
|
|
108
|
+
intent_account_id: str
|
|
109
|
+
product_id: None | Annotated[str, AfterValidator(validators.validate_hexstr32)]
|
|
110
|
+
intent_hash: None | Annotated[str, AfterValidator(validators.validate_hexstr32)]
|
|
111
|
+
start: None | Timestamp
|
|
112
|
+
end: None | Timestamp
|
|
113
|
+
trade_states: Annotated[list[TradeState], Field(exclude=True)]
|
|
114
|
+
|
|
115
|
+
@computed_field
|
|
116
|
+
@property
|
|
117
|
+
def trade_state(self) -> str | None:
|
|
118
|
+
return ",".join(self.trade_states) if self.trade_states else None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# IPFS Client
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class ExtendedMetadata(Model):
|
|
125
|
+
outcome_space: OutcomeSpaceTimeSeries | OutcomeSpaceScalar | OutcomeSpace
|
|
126
|
+
outcome_point: OutcomePointEvent | OutcomePointTimeSeries | OutcomePoint
|
|
127
|
+
oracle_config: OracleConfigPrototype1 | OracleConfig
|
|
128
|
+
oracle_fallback: OracleFallback
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class ComponentLink(AliasedModel):
|
|
132
|
+
model_config = AliasedModel.model_config | ConfigDict(arbitrary_types_allowed=True)
|
|
133
|
+
|
|
134
|
+
data: IPLD_LINK
|
|
135
|
+
schema_: Annotated[IPLD_LINK, Field(alias="schema")]
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class ExtendedMetadataDAG(Model):
|
|
139
|
+
outcome_space: ComponentLink
|
|
140
|
+
outcome_point: ComponentLink
|
|
141
|
+
oracle_config: ComponentLink
|
|
142
|
+
oracle_fallback: ComponentLink
|
afp/exceptions.py
CHANGED
|
@@ -2,6 +2,9 @@ class AFPException(Exception):
|
|
|
2
2
|
pass
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
# API-specific exceptions
|
|
6
|
+
|
|
7
|
+
|
|
5
8
|
class ConfigurationError(AFPException):
|
|
6
9
|
pass
|
|
7
10
|
|
|
@@ -10,10 +13,17 @@ class ClearingSystemError(AFPException):
|
|
|
10
13
|
pass
|
|
11
14
|
|
|
12
15
|
|
|
16
|
+
class IPFSError(AFPException):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
13
20
|
class ExchangeError(AFPException):
|
|
14
21
|
pass
|
|
15
22
|
|
|
16
23
|
|
|
24
|
+
# Exchange error sub-types
|
|
25
|
+
|
|
26
|
+
|
|
17
27
|
class AuthenticationError(ExchangeError):
|
|
18
28
|
pass
|
|
19
29
|
|
afp/exchange.py
CHANGED
|
@@ -6,6 +6,16 @@ import requests
|
|
|
6
6
|
from requests import Response, Session
|
|
7
7
|
|
|
8
8
|
from . import constants
|
|
9
|
+
from .dtos import (
|
|
10
|
+
ExchangeParameters,
|
|
11
|
+
ExchangeProductFilter,
|
|
12
|
+
ExchangeProductListingSubmission,
|
|
13
|
+
ExchangeProductUpdateSubmission,
|
|
14
|
+
LoginSubmission,
|
|
15
|
+
OrderFilter,
|
|
16
|
+
OrderFillFilter,
|
|
17
|
+
OrderSubmission,
|
|
18
|
+
)
|
|
9
19
|
from .exceptions import (
|
|
10
20
|
AuthenticationError,
|
|
11
21
|
AuthorizationError,
|
|
@@ -15,19 +25,11 @@ from .exceptions import (
|
|
|
15
25
|
ValidationError,
|
|
16
26
|
)
|
|
17
27
|
from .schemas import (
|
|
18
|
-
ExchangeParameters,
|
|
19
28
|
ExchangeProduct,
|
|
20
|
-
ExchangeProductFilter,
|
|
21
|
-
ExchangeProductListingSubmission,
|
|
22
|
-
ExchangeProductUpdateSubmission,
|
|
23
|
-
LoginSubmission,
|
|
24
29
|
MarketDepthData,
|
|
25
30
|
OHLCVItem,
|
|
26
31
|
Order,
|
|
27
|
-
OrderFilter,
|
|
28
32
|
OrderFill,
|
|
29
|
-
OrderFillFilter,
|
|
30
|
-
OrderSubmission,
|
|
31
33
|
)
|
|
32
34
|
|
|
33
35
|
|
afp/hashing.py
CHANGED
|
@@ -6,13 +6,13 @@ from hexbytes import HexBytes
|
|
|
6
6
|
from web3 import Web3
|
|
7
7
|
|
|
8
8
|
from . import constants
|
|
9
|
-
from .bindings import
|
|
9
|
+
from .bindings import Side as OnChainSide
|
|
10
10
|
from .schemas import IntentData, OrderSide
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
ORDER_SIDE_MAPPING: dict[OrderSide, int] = {
|
|
14
|
-
OrderSide.BID:
|
|
15
|
-
OrderSide.ASK:
|
|
14
|
+
OrderSide.BID: OnChainSide.BID.value,
|
|
15
|
+
OrderSide.ASK: OnChainSide.ASK.value,
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
|
|
@@ -28,11 +28,12 @@ def generate_intent_hash(
|
|
|
28
28
|
"uint256", # nonce
|
|
29
29
|
"address", # trading_protocol_id
|
|
30
30
|
"bytes32", # product_id
|
|
31
|
-
"
|
|
31
|
+
"int256", # limit_price
|
|
32
32
|
"uint256", # quantity
|
|
33
|
-
"
|
|
33
|
+
"uint32", # max_trading_fee_rate
|
|
34
34
|
"uint256", # good_until_time
|
|
35
35
|
"uint8", # side
|
|
36
|
+
"address", # referral
|
|
36
37
|
]
|
|
37
38
|
values: list[Any] = [
|
|
38
39
|
margin_account_id,
|
|
@@ -45,6 +46,7 @@ def generate_intent_hash(
|
|
|
45
46
|
int(intent_data.max_trading_fee_rate * constants.FEE_RATE_MULTIPLIER),
|
|
46
47
|
int(intent_data.good_until_time.timestamp()),
|
|
47
48
|
ORDER_SIDE_MAPPING[intent_data.side],
|
|
49
|
+
cast(ChecksumAddress, intent_data.referral),
|
|
48
50
|
]
|
|
49
51
|
return Web3.keccak(encode_packed(types, values))
|
|
50
52
|
|