poly-web3 0.0.4__py3-none-any.whl → 0.0.6__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.
- examples/example_redeem.py +67 -63
- poly_web3/__init__.py +33 -33
- poly_web3/const.py +171 -171
- poly_web3/log.py +39 -18
- poly_web3/schema.py +22 -22
- poly_web3/signature/__init__.py +6 -6
- poly_web3/signature/build.py +113 -113
- poly_web3/signature/hash_message.py +48 -48
- poly_web3/signature/secp256k1.py +57 -57
- poly_web3/web3_service/__init__.py +9 -9
- poly_web3/web3_service/base.py +332 -182
- poly_web3/web3_service/eoa_service.py +19 -17
- poly_web3/web3_service/proxy_service.py +125 -164
- poly_web3/web3_service/safe_service.py +24 -17
- {poly_web3-0.0.4.dist-info → poly_web3-0.0.6.dist-info}/METADATA +298 -302
- poly_web3-0.0.6.dist-info/RECORD +18 -0
- poly_web3-0.0.4.dist-info/RECORD +0 -18
- {poly_web3-0.0.4.dist-info → poly_web3-0.0.6.dist-info}/WHEEL +0 -0
- {poly_web3-0.0.4.dist-info → poly_web3-0.0.6.dist-info}/top_level.txt +0 -0
|
@@ -1,164 +1,125 @@
|
|
|
1
|
-
# -*- coding = utf-8 -*-
|
|
2
|
-
# @Time: 2025-12-27 16:00:42
|
|
3
|
-
# @Author: PinBar
|
|
4
|
-
# @Site:
|
|
5
|
-
# @File: proxy_service.py
|
|
6
|
-
# @Software: PyCharm
|
|
7
|
-
import requests
|
|
8
|
-
|
|
9
|
-
from web3 import Web3
|
|
10
|
-
from eth_utils import to_bytes
|
|
11
|
-
|
|
12
|
-
from poly_web3.const import (
|
|
13
|
-
proxy_wallet_factory_abi,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
from poly_web3.
|
|
24
|
-
from poly_web3.signature
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
args
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
#
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
)
|
|
127
|
-
response = requests.post(
|
|
128
|
-
RELAYER_URL + SUBMIT_TRANSACTION, json=req, headers=headers
|
|
129
|
-
).json()
|
|
130
|
-
redeem_res = self.relayer_client.poll_until_state(
|
|
131
|
-
transaction_id=response["transactionID"],
|
|
132
|
-
states=[STATE_MINED, STATE_CONFIRMED],
|
|
133
|
-
fail_state=STATE_FAILED,
|
|
134
|
-
max_polls=100,
|
|
135
|
-
)
|
|
136
|
-
return redeem_res
|
|
137
|
-
|
|
138
|
-
def redeem_all(self) -> list[dict]:
|
|
139
|
-
positions = self.fetch_positions(user_address=self._resolve_user_address())
|
|
140
|
-
if not positions:
|
|
141
|
-
return []
|
|
142
|
-
redeem_list = []
|
|
143
|
-
for pos in positions:
|
|
144
|
-
condition_id = pos.get("conditionId")
|
|
145
|
-
try:
|
|
146
|
-
can_redeem = self.get_redeemable_index_and_balance(condition_id)
|
|
147
|
-
if not can_redeem:
|
|
148
|
-
continue
|
|
149
|
-
if pos.get("negativeRisk"):
|
|
150
|
-
amounts = [0, 0]
|
|
151
|
-
amounts[pos.get("outcomeIndex")] = pos.get("size")
|
|
152
|
-
int_amounts = [int(amount * 1e6) for amount in amounts]
|
|
153
|
-
redeem_res = self.redeem(condition_id=condition_id, redeem_amounts=int_amounts, neg_risk=True)
|
|
154
|
-
else:
|
|
155
|
-
redeem_res = self.redeem(condition_id=condition_id)
|
|
156
|
-
except Exception as e:
|
|
157
|
-
logger.error(f"redeem error, {condition_id=}, error={e}")
|
|
158
|
-
else:
|
|
159
|
-
redeem_list.append(redeem_res)
|
|
160
|
-
buy_price = pos.get("avgPrice")
|
|
161
|
-
size = pos.get("size")
|
|
162
|
-
volume = 1 / buy_price * (buy_price * size)
|
|
163
|
-
logger.info(f"slug={pos.get('slug')} redeem success, volume={volume:.4f} usdc")
|
|
164
|
-
return redeem_list
|
|
1
|
+
# -*- coding = utf-8 -*-
|
|
2
|
+
# @Time: 2025-12-27 16:00:42
|
|
3
|
+
# @Author: PinBar
|
|
4
|
+
# @Site:
|
|
5
|
+
# @File: proxy_service.py
|
|
6
|
+
# @Software: PyCharm
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
from web3 import Web3
|
|
10
|
+
from eth_utils import to_bytes
|
|
11
|
+
|
|
12
|
+
from poly_web3.const import (
|
|
13
|
+
proxy_wallet_factory_abi,
|
|
14
|
+
RELAYER_URL,
|
|
15
|
+
PROXY_INIT_CODE_HASH,
|
|
16
|
+
SUBMIT_TRANSACTION,
|
|
17
|
+
STATE_MINED,
|
|
18
|
+
STATE_CONFIRMED,
|
|
19
|
+
STATE_FAILED,
|
|
20
|
+
)
|
|
21
|
+
from poly_web3.web3_service.base import BaseWeb3Service
|
|
22
|
+
from poly_web3.signature.build import derive_proxy_wallet, create_struct_hash
|
|
23
|
+
from poly_web3.signature.hash_message import hash_message
|
|
24
|
+
from poly_web3.signature import secp256k1
|
|
25
|
+
class ProxyWeb3Service(BaseWeb3Service):
|
|
26
|
+
def _build_redeem_tx(self, to: str, data: str) -> dict:
|
|
27
|
+
return {
|
|
28
|
+
"to": to,
|
|
29
|
+
"data": data,
|
|
30
|
+
"value": 0,
|
|
31
|
+
"typeCode": 1,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
def build_proxy_transaction_request(self, args: dict) -> dict:
|
|
35
|
+
proxy_contract_config = self.get_contract_config()["ProxyContracts"]
|
|
36
|
+
to = proxy_contract_config["ProxyFactory"]
|
|
37
|
+
proxy = derive_proxy_wallet(args["from"], to, PROXY_INIT_CODE_HASH)
|
|
38
|
+
relayer_fee = "0"
|
|
39
|
+
relay_hub = proxy_contract_config["RelayHub"]
|
|
40
|
+
gas_limit_str = self.estimate_gas(
|
|
41
|
+
tx={"from": args["from"], "to": to, "data": args["data"]}
|
|
42
|
+
)
|
|
43
|
+
sig_params = {
|
|
44
|
+
"gasPrice": args.get("gasPrice"),
|
|
45
|
+
"gasLimit": gas_limit_str,
|
|
46
|
+
"relayerFee": relayer_fee,
|
|
47
|
+
"relayHub": relay_hub,
|
|
48
|
+
"relay": args.get("relay"),
|
|
49
|
+
}
|
|
50
|
+
tx_hash = create_struct_hash(
|
|
51
|
+
args["from"],
|
|
52
|
+
to,
|
|
53
|
+
args["data"],
|
|
54
|
+
relayer_fee,
|
|
55
|
+
args.get("gasPrice"),
|
|
56
|
+
gas_limit_str,
|
|
57
|
+
args["nonce"],
|
|
58
|
+
relay_hub,
|
|
59
|
+
args.get("relay"),
|
|
60
|
+
)
|
|
61
|
+
message = {"raw": list(to_bytes(hexstr=tx_hash))}
|
|
62
|
+
|
|
63
|
+
r, s, recovery = secp256k1.sign(
|
|
64
|
+
hash_message(message)[2:], self.clob_client.signer.private_key
|
|
65
|
+
)
|
|
66
|
+
signature = {
|
|
67
|
+
"r": secp256k1.int_to_hex(r, 32),
|
|
68
|
+
"s": secp256k1.int_to_hex(s, 32),
|
|
69
|
+
"v": 28 if recovery else 27,
|
|
70
|
+
"yParity": recovery,
|
|
71
|
+
}
|
|
72
|
+
final_sig = secp256k1.serialize_signature(**signature, to="hex")
|
|
73
|
+
req = {
|
|
74
|
+
"from": args["from"],
|
|
75
|
+
"to": to,
|
|
76
|
+
"proxyWallet": proxy,
|
|
77
|
+
"data": args["data"],
|
|
78
|
+
"nonce": args["nonce"],
|
|
79
|
+
"signature": final_sig,
|
|
80
|
+
"signatureParams": sig_params,
|
|
81
|
+
"type": self.wallet_type.value,
|
|
82
|
+
"metadata": "redeem",
|
|
83
|
+
}
|
|
84
|
+
return req
|
|
85
|
+
|
|
86
|
+
def encode_proxy_transaction_data(self, txns):
|
|
87
|
+
# Prepare the arguments for the 'proxy' function
|
|
88
|
+
calls_data = [
|
|
89
|
+
(txn["typeCode"], txn["to"], txn["value"], txn["data"]) for txn in txns
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
# Create the contract object
|
|
93
|
+
contract = self.w3.eth.contract(abi=proxy_wallet_factory_abi)
|
|
94
|
+
|
|
95
|
+
# Encode function data
|
|
96
|
+
function_data = contract.encodeABI(fn_name="proxy", args=[calls_data])
|
|
97
|
+
|
|
98
|
+
return function_data
|
|
99
|
+
|
|
100
|
+
def _submit_redeem(self, txs: list[dict]) -> dict:
|
|
101
|
+
if self.clob_client is None:
|
|
102
|
+
raise Exception("signer not found")
|
|
103
|
+
_from = Web3.to_checksum_address(self.clob_client.get_address())
|
|
104
|
+
rp = self._get_relay_payload(_from, self.wallet_type)
|
|
105
|
+
args = {
|
|
106
|
+
"from": _from,
|
|
107
|
+
"gasPrice": "0",
|
|
108
|
+
"data": self.encode_proxy_transaction_data(txs),
|
|
109
|
+
"relay": rp["address"],
|
|
110
|
+
"nonce": rp["nonce"],
|
|
111
|
+
}
|
|
112
|
+
req = self.build_proxy_transaction_request(args)
|
|
113
|
+
headers = self.relayer_client._generate_builder_headers(
|
|
114
|
+
"POST", SUBMIT_TRANSACTION, req
|
|
115
|
+
)
|
|
116
|
+
response = requests.post(
|
|
117
|
+
RELAYER_URL + SUBMIT_TRANSACTION, json=req, headers=headers
|
|
118
|
+
).json()
|
|
119
|
+
redeem_res = self.relayer_client.poll_until_state(
|
|
120
|
+
transaction_id=response["transactionID"],
|
|
121
|
+
states=[STATE_MINED, STATE_CONFIRMED],
|
|
122
|
+
fail_state=STATE_FAILED,
|
|
123
|
+
max_polls=100,
|
|
124
|
+
)
|
|
125
|
+
return redeem_res
|
|
@@ -1,17 +1,24 @@
|
|
|
1
|
-
# -*- coding = utf-8 -*-
|
|
2
|
-
# @Time: 2025-12-27 16:01:00
|
|
3
|
-
# @Author: PinBar
|
|
4
|
-
# @Site:
|
|
5
|
-
# @File: safe_service.py
|
|
6
|
-
# @Software: PyCharm
|
|
7
|
-
from
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
# -*- coding = utf-8 -*-
|
|
2
|
+
# @Time: 2025-12-27 16:01:00
|
|
3
|
+
# @Author: PinBar
|
|
4
|
+
# @Site:
|
|
5
|
+
# @File: safe_service.py
|
|
6
|
+
# @Software: PyCharm
|
|
7
|
+
from py_builder_relayer_client.models import OperationType, SafeTransaction
|
|
8
|
+
from poly_web3.web3_service.base import BaseWeb3Service
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SafeWeb3Service(BaseWeb3Service):
|
|
12
|
+
def _build_redeem_tx(self, to: str, data: str) -> SafeTransaction:
|
|
13
|
+
return SafeTransaction(
|
|
14
|
+
to=to,
|
|
15
|
+
data=data,
|
|
16
|
+
value="0",
|
|
17
|
+
operation=OperationType.Call,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
def _submit_redeem(self, txs: list[SafeTransaction]) -> dict | None:
|
|
21
|
+
if self.relayer_client is None:
|
|
22
|
+
raise Exception("relayer_client not found")
|
|
23
|
+
resp = self.relayer_client.execute(txs, "redeem")
|
|
24
|
+
return resp.wait()
|