polymarket-apis 0.3.0__py3-none-any.whl → 0.3.9__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.
Potentially problematic release.
This version of polymarket-apis might be problematic. Click here for more details.
- polymarket_apis/__init__.py +42 -0
- polymarket_apis/clients/__init__.py +23 -0
- polymarket_apis/clients/clob_client.py +224 -117
- polymarket_apis/clients/data_client.py +220 -67
- polymarket_apis/clients/gamma_client.py +589 -101
- polymarket_apis/clients/graphql_client.py +28 -11
- polymarket_apis/clients/web3_client.py +538 -131
- polymarket_apis/clients/websockets_client.py +24 -7
- polymarket_apis/types/__init__.py +167 -0
- polymarket_apis/types/clob_types.py +35 -14
- polymarket_apis/types/common.py +105 -35
- polymarket_apis/types/data_types.py +48 -3
- polymarket_apis/types/gamma_types.py +529 -257
- polymarket_apis/types/web3_types.py +45 -0
- polymarket_apis/types/websockets_types.py +92 -41
- polymarket_apis/utilities/config.py +1 -0
- polymarket_apis/utilities/constants.py +5 -4
- polymarket_apis/utilities/exceptions.py +9 -0
- polymarket_apis/utilities/order_builder/builder.py +38 -22
- polymarket_apis/utilities/order_builder/helpers.py +0 -1
- polymarket_apis/utilities/signing/hmac.py +5 -1
- polymarket_apis/utilities/signing/signer.py +2 -2
- polymarket_apis/utilities/web3/abis/Safe.json +1138 -0
- polymarket_apis/utilities/web3/abis/SafeProxyFactory.json +224 -0
- polymarket_apis/utilities/web3/abis/custom_contract_errors.py +1 -1
- polymarket_apis/utilities/web3/helpers.py +235 -0
- {polymarket_apis-0.3.0.dist-info → polymarket_apis-0.3.9.dist-info}/METADATA +48 -8
- polymarket_apis-0.3.9.dist-info/RECORD +44 -0
- polymarket_apis/utilities/schemas/activity-subgraph.graphql +0 -86
- polymarket_apis/utilities/schemas/open-interest.graphql +0 -30
- polymarket_apis-0.3.0.dist-info/RECORD +0 -43
- {polymarket_apis-0.3.0.dist-info → polymarket_apis-0.3.9.dist-info}/WHEEL +0 -0
|
@@ -1,58 +1,151 @@
|
|
|
1
1
|
from json import load
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from typing import Literal
|
|
3
|
+
from typing import Literal
|
|
4
4
|
|
|
5
5
|
from web3 import Web3
|
|
6
|
+
from web3.constants import MAX_INT
|
|
6
7
|
from web3.exceptions import ContractCustomError
|
|
7
|
-
from web3.middleware import
|
|
8
|
+
from web3.middleware import (
|
|
9
|
+
ExtraDataToPOAMiddleware,
|
|
10
|
+
SignAndSendRawMiddlewareBuilder,
|
|
11
|
+
)
|
|
12
|
+
from web3.types import ChecksumAddress, TxParams, Wei
|
|
8
13
|
|
|
9
14
|
from ..types.common import EthAddress, Keccak256
|
|
15
|
+
from ..types.web3_types import TransactionReceipt
|
|
10
16
|
from ..utilities.config import get_contract_config
|
|
11
|
-
from ..utilities.constants import HASH_ZERO, POLYGON
|
|
17
|
+
from ..utilities.constants import ADDRESS_ZERO, HASH_ZERO, POLYGON
|
|
18
|
+
from ..utilities.exceptions import SafeAlreadyDeployedError
|
|
12
19
|
from ..utilities.web3.abis.custom_contract_errors import CUSTOM_ERROR_DICT
|
|
13
|
-
from ..utilities.web3.helpers import
|
|
20
|
+
from ..utilities.web3.helpers import (
|
|
21
|
+
create_safe_create_signature,
|
|
22
|
+
get_index_set,
|
|
23
|
+
sign_safe_transaction,
|
|
24
|
+
split_signature,
|
|
25
|
+
)
|
|
14
26
|
|
|
15
27
|
|
|
16
28
|
def _load_abi(contract_name: str) -> list:
|
|
17
|
-
abi_path =
|
|
29
|
+
abi_path = (
|
|
30
|
+
Path(__file__).parent.parent
|
|
31
|
+
/ "utilities"
|
|
32
|
+
/ "web3"
|
|
33
|
+
/ "abis"
|
|
34
|
+
/ f"{contract_name}.json"
|
|
35
|
+
)
|
|
18
36
|
with Path.open(abi_path) as f:
|
|
19
37
|
return load(f)
|
|
20
38
|
|
|
21
|
-
class PolymarketWeb3Client:
|
|
22
|
-
def __init__(self, private_key: str , chain_id: Literal[137, 80002] = POLYGON):
|
|
23
39
|
|
|
40
|
+
class PolymarketWeb3Client:
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
private_key: str,
|
|
44
|
+
signature_type: Literal[0, 1, 2] = 1,
|
|
45
|
+
chain_id: Literal[137, 80002] = POLYGON,
|
|
46
|
+
):
|
|
24
47
|
self.w3 = Web3(Web3.HTTPProvider("https://polygon-rpc.com"))
|
|
25
|
-
self.w3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0)
|
|
26
|
-
self.w3.middleware_onion.inject(
|
|
48
|
+
self.w3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0) # type: ignore[arg-type]
|
|
49
|
+
self.w3.middleware_onion.inject(
|
|
50
|
+
SignAndSendRawMiddlewareBuilder.build(private_key), # type: ignore[arg-type]
|
|
51
|
+
layer=0,
|
|
52
|
+
)
|
|
27
53
|
|
|
28
54
|
self.account = self.w3.eth.account.from_key(private_key)
|
|
55
|
+
self.signature_type = signature_type
|
|
29
56
|
|
|
30
57
|
self.config = get_contract_config(chain_id, neg_risk=False)
|
|
31
58
|
self.neg_risk_config = get_contract_config(chain_id, neg_risk=True)
|
|
32
59
|
|
|
33
60
|
self.usdc_address = Web3.to_checksum_address(self.config.collateral)
|
|
34
61
|
self.usdc_abi = _load_abi("UChildERC20Proxy")
|
|
35
|
-
self.usdc = self.
|
|
62
|
+
self.usdc = self._contract(self.usdc_address, self.usdc_abi)
|
|
36
63
|
|
|
37
|
-
self.conditional_tokens_address = Web3.to_checksum_address(
|
|
64
|
+
self.conditional_tokens_address = Web3.to_checksum_address(
|
|
65
|
+
self.config.conditional_tokens
|
|
66
|
+
)
|
|
38
67
|
self.conditional_tokens_abi = _load_abi("ConditionalTokens")
|
|
39
|
-
self.conditional_tokens = self.
|
|
68
|
+
self.conditional_tokens = self._contract(
|
|
69
|
+
self.conditional_tokens_address, self.conditional_tokens_abi
|
|
70
|
+
)
|
|
40
71
|
|
|
41
72
|
self.exchange_address = Web3.to_checksum_address(self.config.exchange)
|
|
42
73
|
self.exchange_abi = _load_abi("CTFExchange")
|
|
43
|
-
self.exchange = self.
|
|
74
|
+
self.exchange = self._contract(self.exchange_address, self.exchange_abi)
|
|
44
75
|
|
|
45
|
-
self.neg_risk_exchange_address = Web3.to_checksum_address(
|
|
76
|
+
self.neg_risk_exchange_address = Web3.to_checksum_address(
|
|
77
|
+
self.neg_risk_config.exchange
|
|
78
|
+
)
|
|
46
79
|
self.neg_risk_exchange_abi = _load_abi("NegRiskCtfExchange")
|
|
47
|
-
self.neg_risk_exchange = self.
|
|
80
|
+
self.neg_risk_exchange = self._contract(
|
|
81
|
+
self.neg_risk_exchange_address, self.neg_risk_exchange_abi
|
|
82
|
+
)
|
|
48
83
|
|
|
49
|
-
self.neg_risk_adapter_address =
|
|
84
|
+
self.neg_risk_adapter_address = Web3.to_checksum_address(
|
|
85
|
+
"0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296"
|
|
86
|
+
)
|
|
50
87
|
self.neg_risk_adapter_abi = _load_abi("NegRiskAdapter")
|
|
51
|
-
self.neg_risk_adapter = self.
|
|
88
|
+
self.neg_risk_adapter = self._contract(
|
|
89
|
+
self.neg_risk_adapter_address, self.neg_risk_adapter_abi
|
|
90
|
+
)
|
|
52
91
|
|
|
53
|
-
self.proxy_factory_address =
|
|
92
|
+
self.proxy_factory_address = Web3.to_checksum_address(
|
|
93
|
+
"0xaB45c5A4B0c941a2F231C04C3f49182e1A254052"
|
|
94
|
+
)
|
|
54
95
|
self.proxy_factory_abi = _load_abi("ProxyWalletFactory")
|
|
55
|
-
self.proxy_factory = self.
|
|
96
|
+
self.proxy_factory = self._contract(
|
|
97
|
+
self.proxy_factory_address, self.proxy_factory_abi
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
self.safe_proxy_factory_address = Web3.to_checksum_address(
|
|
101
|
+
"0xaacFeEa03eb1561C4e67d661e40682Bd20E3541b"
|
|
102
|
+
)
|
|
103
|
+
self.safe_proxy_factory_abi = _load_abi("SafeProxyFactory")
|
|
104
|
+
self.safe_proxy_factory = self._contract(
|
|
105
|
+
self.safe_proxy_factory_address, self.safe_proxy_factory_abi
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
match self.signature_type:
|
|
109
|
+
case 0:
|
|
110
|
+
self.address = self.account.address
|
|
111
|
+
case 1:
|
|
112
|
+
self.address = self.get_poly_proxy_address()
|
|
113
|
+
case 2:
|
|
114
|
+
self.address = self.get_safe_proxy_address()
|
|
115
|
+
self.safe_abi = _load_abi("Safe")
|
|
116
|
+
self.safe = self._contract(self.address, self.safe_abi)
|
|
117
|
+
|
|
118
|
+
def _contract(self, address, abi):
|
|
119
|
+
return self.w3.eth.contract(
|
|
120
|
+
address=Web3.to_checksum_address(address),
|
|
121
|
+
abi=abi,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
def _encode_usdc_approve(self, address: ChecksumAddress) -> str:
|
|
125
|
+
return self.usdc.encode_abi(
|
|
126
|
+
abi_element_identifier="approve",
|
|
127
|
+
args=[address, int(MAX_INT, base=16)],
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def _encode_condition_tokens_approve(self, address: ChecksumAddress) -> str:
|
|
131
|
+
return self.conditional_tokens.encode_abi(
|
|
132
|
+
abi_element_identifier="setApprovalForAll",
|
|
133
|
+
args=[address, True],
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def _encode_transfer_usdc(self, address: ChecksumAddress, amount: int) -> str:
|
|
137
|
+
return self.usdc.encode_abi(
|
|
138
|
+
abi_element_identifier="transfer",
|
|
139
|
+
args=[address, amount],
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
def _encode_transfer_token(
|
|
143
|
+
self, token_id: str, address: ChecksumAddress, amount: int
|
|
144
|
+
) -> str:
|
|
145
|
+
return self.conditional_tokens.encode_abi(
|
|
146
|
+
abi_element_identifier="safeTransferFrom",
|
|
147
|
+
args=[self.address, address, int(token_id), amount, HASH_ZERO],
|
|
148
|
+
)
|
|
56
149
|
|
|
57
150
|
def _encode_split(self, condition_id: Keccak256, amount: int) -> str:
|
|
58
151
|
return self.conditional_tokens.encode_abi(
|
|
@@ -72,23 +165,117 @@ class PolymarketWeb3Client:
|
|
|
72
165
|
args=[self.usdc_address, HASH_ZERO, condition_id, [1, 2]],
|
|
73
166
|
)
|
|
74
167
|
|
|
75
|
-
def _encode_redeem_neg_risk(
|
|
168
|
+
def _encode_redeem_neg_risk(
|
|
169
|
+
self, condition_id: Keccak256, amounts: list[int]
|
|
170
|
+
) -> str:
|
|
76
171
|
return self.neg_risk_adapter.encode_abi(
|
|
77
172
|
abi_element_identifier="redeemPositions",
|
|
78
173
|
args=[condition_id, amounts],
|
|
79
174
|
)
|
|
80
|
-
|
|
175
|
+
|
|
176
|
+
def _encode_convert(
|
|
177
|
+
self, neg_risk_market_id: Keccak256, index_set: int, amount: int
|
|
178
|
+
) -> str:
|
|
81
179
|
return self.neg_risk_adapter.encode_abi(
|
|
82
180
|
abi_element_identifier="convertPositions",
|
|
83
181
|
args=[neg_risk_market_id, index_set, amount],
|
|
84
182
|
)
|
|
85
183
|
|
|
86
|
-
def
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
184
|
+
def _build_base_transaction(self) -> TxParams:
|
|
185
|
+
"""Build base transaction parameters."""
|
|
186
|
+
nonce = self.w3.eth.get_transaction_count(self.account.address)
|
|
187
|
+
|
|
188
|
+
current_gas_price: int = self.w3.eth.gas_price
|
|
189
|
+
adjusted_gas_price = Wei(int(current_gas_price * 1.05))
|
|
190
|
+
|
|
191
|
+
base_transaction: TxParams = {
|
|
192
|
+
"nonce": nonce,
|
|
193
|
+
"gasPrice": adjusted_gas_price,
|
|
194
|
+
"gas": 1000000,
|
|
195
|
+
"from": self.account.address,
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return base_transaction
|
|
199
|
+
|
|
200
|
+
def _build_proxy_transaction(self, to, data, base_transaction) -> TxParams:
|
|
201
|
+
proxy_txn = {
|
|
202
|
+
"typeCode": 1,
|
|
203
|
+
"to": to,
|
|
204
|
+
"value": 0,
|
|
205
|
+
"data": data,
|
|
206
|
+
}
|
|
207
|
+
txn_data = self.proxy_factory.functions.proxy([proxy_txn]).build_transaction(
|
|
208
|
+
transaction=base_transaction
|
|
90
209
|
)
|
|
91
210
|
|
|
211
|
+
return txn_data
|
|
212
|
+
|
|
213
|
+
def _build_safe_transaction(self, to, data, base_transaction) -> TxParams:
|
|
214
|
+
safe_nonce = self.safe.functions.nonce().call()
|
|
215
|
+
safe_txn = {
|
|
216
|
+
"to": to,
|
|
217
|
+
"data": data,
|
|
218
|
+
"operation": 0, # 1 for delegatecall, 0 for call
|
|
219
|
+
"value": 0,
|
|
220
|
+
}
|
|
221
|
+
packed_sig = sign_safe_transaction(
|
|
222
|
+
self.account,
|
|
223
|
+
self.safe,
|
|
224
|
+
safe_txn,
|
|
225
|
+
safe_nonce,
|
|
226
|
+
)
|
|
227
|
+
txn_data = self.safe.functions.execTransaction(
|
|
228
|
+
safe_txn["to"],
|
|
229
|
+
safe_txn["value"],
|
|
230
|
+
safe_txn["data"],
|
|
231
|
+
safe_txn.get("operation", 0),
|
|
232
|
+
0, # safeTxGas
|
|
233
|
+
0, # baseGas
|
|
234
|
+
0, # gasPrice
|
|
235
|
+
ADDRESS_ZERO, # gasToken
|
|
236
|
+
ADDRESS_ZERO, # refundReceiver
|
|
237
|
+
packed_sig,
|
|
238
|
+
).build_transaction(transaction=base_transaction)
|
|
239
|
+
|
|
240
|
+
return txn_data
|
|
241
|
+
|
|
242
|
+
def _execute_transaction(
|
|
243
|
+
self, txn_data: TxParams, operation_name: str = "Transaction"
|
|
244
|
+
) -> TransactionReceipt:
|
|
245
|
+
"""
|
|
246
|
+
Execute a transaction, wait for receipt, and print status.
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
txn_data: Built transaction data
|
|
250
|
+
operation_name: Name of operation for logging
|
|
251
|
+
|
|
252
|
+
"""
|
|
253
|
+
signed_txn = self.account.sign_transaction(txn_data)
|
|
254
|
+
tx_hash = self.w3.eth.send_raw_transaction(signed_txn.raw_transaction)
|
|
255
|
+
tx_hash_hex = tx_hash.hex()
|
|
256
|
+
|
|
257
|
+
print(f"Txn hash: 0x{tx_hash_hex}")
|
|
258
|
+
|
|
259
|
+
# Wait for transaction to be mined and get receipt
|
|
260
|
+
receipt_dict = self.w3.eth.wait_for_transaction_receipt(tx_hash)
|
|
261
|
+
receipt = TransactionReceipt.model_validate(receipt_dict)
|
|
262
|
+
|
|
263
|
+
print(f"{operation_name} succeeded") if receipt.status == 1 else print(
|
|
264
|
+
f"{operation_name} failed"
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
return receipt
|
|
268
|
+
|
|
269
|
+
def get_poly_proxy_address(self, address: EthAddress | None = None) -> EthAddress:
|
|
270
|
+
"""Get the polymarket proxy address for the current account."""
|
|
271
|
+
address = address if address else self.account.address
|
|
272
|
+
return self.exchange.functions.getPolyProxyWalletAddress(address).call()
|
|
273
|
+
|
|
274
|
+
def get_safe_proxy_address(self, address: EthAddress | None = None) -> EthAddress:
|
|
275
|
+
"""Get the safe proxy address for the current account."""
|
|
276
|
+
address = address if address else self.account.address
|
|
277
|
+
return self.safe_proxy_factory.functions.computeProxyAddress(address).call()
|
|
278
|
+
|
|
92
279
|
def get_usdc_balance(self, address: EthAddress | None = None) -> float:
|
|
93
280
|
"""
|
|
94
281
|
Get the usdc balance of the given address.
|
|
@@ -98,31 +285,41 @@ class PolymarketWeb3Client:
|
|
|
98
285
|
Explicitly passing the proxy address is faster due to only one contract function call.
|
|
99
286
|
"""
|
|
100
287
|
if address is None:
|
|
101
|
-
address = self.
|
|
288
|
+
address = self.address
|
|
102
289
|
balance_res = self.usdc.functions.balanceOf(address).call()
|
|
103
290
|
return float(balance_res / 1e6)
|
|
104
291
|
|
|
105
|
-
def get_token_balance(
|
|
292
|
+
def get_token_balance(
|
|
293
|
+
self, token_id: str, address: EthAddress | None = None
|
|
294
|
+
) -> float:
|
|
106
295
|
"""Get the token balance of the given address."""
|
|
107
|
-
if address
|
|
108
|
-
address = self.
|
|
109
|
-
balance_res = self.conditional_tokens.functions.balanceOf(
|
|
296
|
+
if not address:
|
|
297
|
+
address = self.address
|
|
298
|
+
balance_res = self.conditional_tokens.functions.balanceOf(
|
|
299
|
+
address, int(token_id)
|
|
300
|
+
).call()
|
|
110
301
|
return float(balance_res / 1e6)
|
|
111
302
|
|
|
112
|
-
def get_token_complement(self, token_id: str) ->
|
|
303
|
+
def get_token_complement(self, token_id: str) -> str | None:
|
|
113
304
|
"""Get the complement of the given token."""
|
|
114
305
|
try:
|
|
115
|
-
return str(
|
|
306
|
+
return str(
|
|
307
|
+
self.neg_risk_exchange.functions.getComplement(int(token_id)).call()
|
|
308
|
+
)
|
|
116
309
|
except ContractCustomError as e:
|
|
117
310
|
if e.args[0] in CUSTOM_ERROR_DICT:
|
|
118
311
|
try:
|
|
119
|
-
return str(
|
|
312
|
+
return str(
|
|
313
|
+
self.exchange.functions.getComplement(int(token_id)).call()
|
|
314
|
+
)
|
|
120
315
|
except ContractCustomError as e2:
|
|
121
316
|
if e2.args[0] in CUSTOM_ERROR_DICT:
|
|
122
317
|
msg = f"{CUSTOM_ERROR_DICT[e2.args[0]]}"
|
|
123
318
|
raise ContractCustomError(
|
|
124
319
|
msg,
|
|
125
320
|
) from e2
|
|
321
|
+
return None
|
|
322
|
+
return None
|
|
126
323
|
|
|
127
324
|
def get_condition_id_neg_risk(self, question_id: Keccak256) -> Keccak256:
|
|
128
325
|
"""
|
|
@@ -132,71 +329,269 @@ class PolymarketWeb3Client:
|
|
|
132
329
|
outcomeSlotCount is represented by the last two digits of question id). Returns a keccak256 hash of
|
|
133
330
|
the oracle and question id.
|
|
134
331
|
"""
|
|
135
|
-
return
|
|
332
|
+
return (
|
|
333
|
+
"0x"
|
|
334
|
+
+ self.neg_risk_adapter.functions.getConditionId(question_id).call().hex()
|
|
335
|
+
)
|
|
136
336
|
|
|
137
|
-
def
|
|
138
|
-
"""
|
|
139
|
-
|
|
140
|
-
|
|
337
|
+
def deploy_safe(self) -> TransactionReceipt:
|
|
338
|
+
"""Deploy a Safe wallet using the SafeProxyFactory contract."""
|
|
339
|
+
safe_address = self.get_safe_proxy_address()
|
|
340
|
+
if self.w3.eth.get_code(self.w3.to_checksum_address(safe_address)) != b"":
|
|
341
|
+
msg = f"Safe already deployed at {safe_address}"
|
|
342
|
+
raise SafeAlreadyDeployedError(msg)
|
|
343
|
+
|
|
344
|
+
# Create the EIP-712 signature for Safe creation
|
|
345
|
+
sig = create_safe_create_signature(account=self.account, chain_id=POLYGON)
|
|
346
|
+
|
|
347
|
+
# Split the signature into r, s, v components
|
|
348
|
+
split_sig = split_signature(sig)
|
|
349
|
+
|
|
350
|
+
# Build the transaction
|
|
351
|
+
base_transaction = self._build_base_transaction()
|
|
352
|
+
|
|
353
|
+
# Execute the createProxy function
|
|
354
|
+
txn_data = self.safe_proxy_factory.functions.createProxy(
|
|
355
|
+
ADDRESS_ZERO, # paymentToken
|
|
356
|
+
0, # payment
|
|
357
|
+
ADDRESS_ZERO, # paymentReceiver
|
|
358
|
+
(
|
|
359
|
+
split_sig["v"],
|
|
360
|
+
split_sig["r"],
|
|
361
|
+
split_sig["s"],
|
|
362
|
+
), # createSig tuple (uint8, bytes32, bytes32)
|
|
363
|
+
).build_transaction(transaction=base_transaction)
|
|
141
364
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
"
|
|
145
|
-
|
|
146
|
-
"data": self._encode_split(condition_id, amount),
|
|
147
|
-
}
|
|
365
|
+
# Sign and send transaction
|
|
366
|
+
return self._execute_transaction(
|
|
367
|
+
txn_data, operation_name="Gnosis Safe Deployment"
|
|
368
|
+
)
|
|
148
369
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
370
|
+
def set_collateral_approval(self, spender: ChecksumAddress) -> TransactionReceipt:
|
|
371
|
+
to = self.usdc_address
|
|
372
|
+
data = self._encode_usdc_approve(address=spender)
|
|
373
|
+
base_transaction = self._build_base_transaction()
|
|
374
|
+
txn_data: TxParams = {}
|
|
375
|
+
|
|
376
|
+
match self.signature_type:
|
|
377
|
+
case 0:
|
|
378
|
+
txn_data = self.usdc.functions.approve(
|
|
379
|
+
spender, int(MAX_INT, base=16)
|
|
380
|
+
).build_transaction(transaction=base_transaction)
|
|
381
|
+
case 1:
|
|
382
|
+
txn_data = self._build_proxy_transaction(to, data, base_transaction)
|
|
383
|
+
case 2:
|
|
384
|
+
txn_data = self._build_safe_transaction(to, data, base_transaction)
|
|
385
|
+
|
|
386
|
+
return self._execute_transaction(txn_data, operation_name="Collateral Approval")
|
|
387
|
+
|
|
388
|
+
def set_conditional_tokens_approval(
|
|
389
|
+
self, spender: ChecksumAddress
|
|
390
|
+
) -> TransactionReceipt:
|
|
391
|
+
to = self.conditional_tokens_address
|
|
392
|
+
data = self._encode_condition_tokens_approve(address=spender)
|
|
393
|
+
base_transaction = self._build_base_transaction()
|
|
394
|
+
txn_data: TxParams = {}
|
|
395
|
+
|
|
396
|
+
match self.signature_type:
|
|
397
|
+
case 0:
|
|
398
|
+
txn_data = self.conditional_tokens.functions.setApprovalForAll(
|
|
399
|
+
spender, True
|
|
400
|
+
).build_transaction(transaction=base_transaction)
|
|
401
|
+
case 1:
|
|
402
|
+
txn_data = self._build_proxy_transaction(to, data, base_transaction)
|
|
403
|
+
case 2:
|
|
404
|
+
txn_data = self._build_safe_transaction(to, data, base_transaction)
|
|
405
|
+
|
|
406
|
+
return self._execute_transaction(
|
|
407
|
+
txn_data, operation_name="Conditional Tokens Approval"
|
|
408
|
+
)
|
|
156
409
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
410
|
+
def set_all_approvals(self) -> list[TransactionReceipt]:
|
|
411
|
+
"""Sets both collateral and conditional tokens approvals."""
|
|
412
|
+
receipts = []
|
|
413
|
+
print("Approving ConditionalTokens as spender on USDC")
|
|
414
|
+
receipts.append(
|
|
415
|
+
self.set_collateral_approval(
|
|
416
|
+
spender=self.conditional_tokens_address,
|
|
417
|
+
)
|
|
418
|
+
)
|
|
419
|
+
print("Approving CTFExchange as spender on USDC")
|
|
420
|
+
receipts.append(
|
|
421
|
+
self.set_collateral_approval(
|
|
422
|
+
spender=self.exchange_address,
|
|
423
|
+
)
|
|
424
|
+
)
|
|
425
|
+
print("Approving NegRiskCtfExchange as spender on USDC")
|
|
426
|
+
receipts.append(
|
|
427
|
+
self.set_collateral_approval(
|
|
428
|
+
spender=self.neg_risk_exchange_address,
|
|
429
|
+
)
|
|
430
|
+
)
|
|
431
|
+
print("Approving NegRiskAdapter as spender on USDC")
|
|
432
|
+
receipts.append(
|
|
433
|
+
self.set_collateral_approval(
|
|
434
|
+
spender=self.neg_risk_adapter_address,
|
|
435
|
+
)
|
|
436
|
+
)
|
|
437
|
+
print("Approving CTFExchange as spender on ConditionalTokens")
|
|
438
|
+
receipts.append(
|
|
439
|
+
self.set_conditional_tokens_approval(
|
|
440
|
+
spender=self.exchange_address,
|
|
441
|
+
)
|
|
442
|
+
)
|
|
443
|
+
print("Approving NegRiskCtfExchange as spender on ConditionalTokens")
|
|
444
|
+
receipts.append(
|
|
445
|
+
self.set_conditional_tokens_approval(
|
|
446
|
+
spender=self.neg_risk_exchange_address,
|
|
447
|
+
)
|
|
448
|
+
)
|
|
449
|
+
print("Approving NegRiskAdapter as spender on ConditionalTokens")
|
|
450
|
+
receipts.append(
|
|
451
|
+
self.set_conditional_tokens_approval(
|
|
452
|
+
spender=self.neg_risk_adapter_address,
|
|
453
|
+
)
|
|
454
|
+
)
|
|
455
|
+
print("All approvals set!")
|
|
160
456
|
|
|
161
|
-
|
|
457
|
+
return receipts
|
|
162
458
|
|
|
163
|
-
|
|
164
|
-
|
|
459
|
+
def transfer_usdc(self, recipient: EthAddress, amount: float) -> TransactionReceipt:
|
|
460
|
+
"""Transfers usdc.e from the account to the proxy address."""
|
|
461
|
+
balance = self.get_usdc_balance(address=self.address)
|
|
462
|
+
if balance < amount:
|
|
463
|
+
msg = f"Insufficient USDC.e balance: {balance} < {amount}"
|
|
464
|
+
raise ValueError(msg)
|
|
465
|
+
amount = int(balance * 1e6)
|
|
165
466
|
|
|
166
|
-
|
|
467
|
+
to = self.usdc_address
|
|
468
|
+
data = self._encode_transfer_usdc(
|
|
469
|
+
self.w3.to_checksum_address(recipient), amount
|
|
470
|
+
)
|
|
471
|
+
base_transaction = self._build_base_transaction()
|
|
472
|
+
txn_data: TxParams = {}
|
|
473
|
+
|
|
474
|
+
match self.signature_type:
|
|
475
|
+
case 0:
|
|
476
|
+
txn_data = self.usdc.functions.transfer(
|
|
477
|
+
recipient,
|
|
478
|
+
amount,
|
|
479
|
+
).build_transaction(transaction=base_transaction)
|
|
480
|
+
case 1:
|
|
481
|
+
txn_data = self._build_proxy_transaction(to, data, base_transaction)
|
|
482
|
+
case 2:
|
|
483
|
+
txn_data = self._build_safe_transaction(to, data, base_transaction)
|
|
167
484
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
485
|
+
# Sign and send transaction
|
|
486
|
+
return self._execute_transaction(txn_data, operation_name="USDC Transfer")
|
|
487
|
+
|
|
488
|
+
def transfer_token(
|
|
489
|
+
self, token_id: str, recipient: EthAddress, amount: float
|
|
490
|
+
) -> TransactionReceipt:
|
|
491
|
+
"""Transfers conditional tokens from the account to the recipient address."""
|
|
492
|
+
balance = self.get_token_balance(token_id=token_id, address=self.address)
|
|
493
|
+
if balance < amount:
|
|
494
|
+
msg = f"Insufficient token balance: {balance} < {amount}"
|
|
495
|
+
raise ValueError(msg)
|
|
496
|
+
amount = int(balance * 1e6)
|
|
497
|
+
|
|
498
|
+
to = self.conditional_tokens_address
|
|
499
|
+
data = self._encode_transfer_token(
|
|
500
|
+
token_id, self.w3.to_checksum_address(recipient), amount
|
|
501
|
+
)
|
|
502
|
+
base_transaction = self._build_base_transaction()
|
|
503
|
+
txn_data: TxParams = {}
|
|
504
|
+
|
|
505
|
+
match self.signature_type:
|
|
506
|
+
case 0:
|
|
507
|
+
txn_data = self.conditional_tokens.functions.safeTransferFrom(
|
|
508
|
+
self.address,
|
|
509
|
+
recipient,
|
|
510
|
+
int(token_id),
|
|
511
|
+
amount,
|
|
512
|
+
b"",
|
|
513
|
+
).build_transaction(transaction=base_transaction)
|
|
514
|
+
case 1:
|
|
515
|
+
txn_data = self._build_proxy_transaction(to, data, base_transaction)
|
|
516
|
+
case 2:
|
|
517
|
+
txn_data = self._build_safe_transaction(to, data, base_transaction)
|
|
172
518
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
"to": self.neg_risk_adapter_address if neg_risk else self.conditional_tokens_address,
|
|
176
|
-
"value": 0,
|
|
177
|
-
"data": self._encode_merge(condition_id, amount),
|
|
178
|
-
}
|
|
519
|
+
# Sign and send transaction
|
|
520
|
+
return self._execute_transaction(txn_data, operation_name="Token Transfer")
|
|
179
521
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
522
|
+
def split_position(
|
|
523
|
+
self, condition_id: Keccak256, amount: float, neg_risk: bool = True
|
|
524
|
+
) -> TransactionReceipt:
|
|
525
|
+
"""Splits usdc into two complementary positions of equal size."""
|
|
526
|
+
amount = int(amount * 1e6)
|
|
527
|
+
|
|
528
|
+
to = (
|
|
529
|
+
self.neg_risk_adapter_address
|
|
530
|
+
if neg_risk
|
|
531
|
+
else self.conditional_tokens_address
|
|
532
|
+
)
|
|
533
|
+
data = self._encode_split(condition_id, amount)
|
|
534
|
+
base_transaction = self._build_base_transaction()
|
|
535
|
+
txn_data: TxParams = {}
|
|
536
|
+
|
|
537
|
+
match self.signature_type:
|
|
538
|
+
case 0:
|
|
539
|
+
_contract = (
|
|
540
|
+
self.neg_risk_adapter if neg_risk else self.conditional_tokens
|
|
541
|
+
)
|
|
542
|
+
txn_data = _contract.functions.splitPosition(
|
|
543
|
+
self.usdc_address,
|
|
544
|
+
HASH_ZERO,
|
|
545
|
+
condition_id,
|
|
546
|
+
[1, 2],
|
|
547
|
+
amount,
|
|
548
|
+
).build_transaction(transaction=base_transaction)
|
|
549
|
+
case 1:
|
|
550
|
+
txn_data = self._build_proxy_transaction(to, data, base_transaction)
|
|
551
|
+
case 2:
|
|
552
|
+
txn_data = self._build_safe_transaction(to, data, base_transaction)
|
|
187
553
|
|
|
188
554
|
# Sign and send transaction
|
|
189
|
-
|
|
190
|
-
tx_hash = self.w3.eth.send_raw_transaction(signed_txn.raw_transaction).hex()
|
|
555
|
+
return self._execute_transaction(txn_data, operation_name="Split Position")
|
|
191
556
|
|
|
192
|
-
|
|
557
|
+
def merge_position(
|
|
558
|
+
self, condition_id: Keccak256, amount: float, neg_risk: bool = True
|
|
559
|
+
) -> TransactionReceipt:
|
|
560
|
+
"""Merges two complementary positions into usdc."""
|
|
561
|
+
amount = int(amount * 1e6)
|
|
193
562
|
|
|
194
|
-
|
|
195
|
-
|
|
563
|
+
to = (
|
|
564
|
+
self.neg_risk_adapter_address
|
|
565
|
+
if neg_risk
|
|
566
|
+
else self.conditional_tokens_address
|
|
567
|
+
)
|
|
568
|
+
data = self._encode_merge(condition_id, amount)
|
|
569
|
+
base_transaction = self._build_base_transaction()
|
|
570
|
+
txn_data: TxParams = {}
|
|
571
|
+
|
|
572
|
+
match self.signature_type:
|
|
573
|
+
case 0:
|
|
574
|
+
_contract = (
|
|
575
|
+
self.neg_risk_adapter if neg_risk else self.conditional_tokens
|
|
576
|
+
)
|
|
577
|
+
txn_data = _contract.functions.mergePositions(
|
|
578
|
+
self.usdc_address,
|
|
579
|
+
HASH_ZERO,
|
|
580
|
+
condition_id,
|
|
581
|
+
[1, 2],
|
|
582
|
+
amount,
|
|
583
|
+
).build_transaction(transaction=base_transaction)
|
|
584
|
+
case 1:
|
|
585
|
+
txn_data = self._build_proxy_transaction(to, data, base_transaction)
|
|
586
|
+
case 2:
|
|
587
|
+
txn_data = self._build_safe_transaction(to, data, base_transaction)
|
|
196
588
|
|
|
197
|
-
|
|
589
|
+
# Sign and send transaction
|
|
590
|
+
return self._execute_transaction(txn_data, operation_name="Merge Position")
|
|
198
591
|
|
|
199
|
-
def redeem_position(
|
|
592
|
+
def redeem_position(
|
|
593
|
+
self, condition_id: Keccak256, amounts: list[float], neg_risk: bool = True
|
|
594
|
+
) -> TransactionReceipt:
|
|
200
595
|
"""
|
|
201
596
|
Redeem a position into usdc.
|
|
202
597
|
|
|
@@ -204,59 +599,71 @@ class PolymarketWeb3Client:
|
|
|
204
599
|
where x is the number of shares of the first outcome
|
|
205
600
|
y is the number of shares of the second outcome.
|
|
206
601
|
"""
|
|
207
|
-
|
|
208
|
-
amounts = [int(amount * 1e6) for amount in amounts]
|
|
209
|
-
|
|
210
|
-
proxy_txn = {
|
|
211
|
-
"typeCode": 1,
|
|
212
|
-
"to": self.neg_risk_adapter_address if neg_risk else self.conditional_tokens_address,
|
|
213
|
-
"value": 0,
|
|
214
|
-
"data": self._encode_redeem_neg_risk(condition_id, amounts) if neg_risk else self._encode_redeem(condition_id),
|
|
215
|
-
}
|
|
602
|
+
int_amounts = [int(amount * 1e6) for amount in amounts]
|
|
216
603
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
604
|
+
to = (
|
|
605
|
+
self.neg_risk_adapter_address
|
|
606
|
+
if neg_risk
|
|
607
|
+
else self.conditional_tokens_address
|
|
608
|
+
)
|
|
609
|
+
data = (
|
|
610
|
+
self._encode_redeem_neg_risk(condition_id, int_amounts)
|
|
611
|
+
if neg_risk
|
|
612
|
+
else self._encode_redeem(condition_id)
|
|
613
|
+
)
|
|
614
|
+
base_transaction = self._build_base_transaction()
|
|
615
|
+
txn_data: TxParams = {}
|
|
616
|
+
|
|
617
|
+
match self.signature_type:
|
|
618
|
+
case 0:
|
|
619
|
+
_contract = (
|
|
620
|
+
self.neg_risk_adapter if neg_risk else self.conditional_tokens
|
|
621
|
+
)
|
|
622
|
+
if neg_risk:
|
|
623
|
+
txn_data = _contract.functions.redeemPositions(
|
|
624
|
+
condition_id, int_amounts
|
|
625
|
+
).build_transaction(transaction=base_transaction)
|
|
626
|
+
else:
|
|
627
|
+
txn_data = _contract.functions.redeemPositions(
|
|
628
|
+
self.usdc_address,
|
|
629
|
+
HASH_ZERO,
|
|
630
|
+
condition_id,
|
|
631
|
+
[1, 2],
|
|
632
|
+
).build_transaction(transaction=base_transaction)
|
|
633
|
+
case 1:
|
|
634
|
+
txn_data = self._build_proxy_transaction(to, data, base_transaction)
|
|
635
|
+
case 2:
|
|
636
|
+
txn_data = self._build_safe_transaction(to, data, base_transaction)
|
|
224
637
|
|
|
225
638
|
# Sign and send transaction
|
|
226
|
-
|
|
227
|
-
tx_hash = self.w3.eth.send_raw_transaction(signed_txn.raw_transaction).hex()
|
|
228
|
-
|
|
229
|
-
print(f"Txn hash: {tx_hash}")
|
|
639
|
+
return self._execute_transaction(txn_data, operation_name="Redeem Position")
|
|
230
640
|
|
|
231
|
-
|
|
232
|
-
self
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
def convert_positions(self, question_ids: list[Keccak256], neg_risk_market_id: Keccak256, amount: int):
|
|
237
|
-
nonce = self.w3.eth.get_transaction_count(self.account.address)
|
|
641
|
+
def convert_positions(
|
|
642
|
+
self,
|
|
643
|
+
question_ids: list[Keccak256],
|
|
644
|
+
amount: float,
|
|
645
|
+
) -> TransactionReceipt:
|
|
238
646
|
amount = int(amount * 1e6)
|
|
647
|
+
neg_risk_market_id = question_ids[0][:-2] + "00"
|
|
239
648
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
# Wait for transaction to be mined
|
|
260
|
-
self.w3.eth.wait_for_transaction_receipt(tx_hash)
|
|
649
|
+
to = self.neg_risk_adapter_address
|
|
650
|
+
data = self._encode_convert(
|
|
651
|
+
neg_risk_market_id, get_index_set(question_ids), amount
|
|
652
|
+
)
|
|
653
|
+
base_transaction = self._build_base_transaction()
|
|
654
|
+
txn_data: TxParams = {}
|
|
655
|
+
|
|
656
|
+
match self.signature_type:
|
|
657
|
+
case 0:
|
|
658
|
+
txn_data = self.neg_risk_adapter.functions.convertPositions(
|
|
659
|
+
neg_risk_market_id,
|
|
660
|
+
get_index_set(question_ids),
|
|
661
|
+
amount,
|
|
662
|
+
).build_transaction(transaction=base_transaction)
|
|
663
|
+
case 1:
|
|
664
|
+
txn_data = self._build_proxy_transaction(to, data, base_transaction)
|
|
665
|
+
case 2:
|
|
666
|
+
txn_data = self._build_safe_transaction(to, data, base_transaction)
|
|
261
667
|
|
|
262
|
-
|
|
668
|
+
# Sign and send transaction
|
|
669
|
+
return self._execute_transaction(txn_data, operation_name="Convert Positions")
|