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