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.
- examples/example_redeem.py +63 -63
- poly_web3/__init__.py +33 -33
- poly_web3/const.py +171 -171
- poly_web3/log.py +18 -0
- 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 +182 -181
- poly_web3/web3_service/eoa_service.py +17 -17
- poly_web3/web3_service/proxy_service.py +166 -163
- poly_web3/web3_service/safe_service.py +17 -17
- {poly_web3-0.0.2.dist-info → poly_web3-0.0.3.dist-info}/METADATA +302 -302
- poly_web3-0.0.3.dist-info/RECORD +18 -0
- poly_web3-0.0.2.dist-info/RECORD +0 -17
- {poly_web3-0.0.2.dist-info → poly_web3-0.0.3.dist-info}/WHEEL +0 -0
- {poly_web3-0.0.2.dist-info → poly_web3-0.0.3.dist-info}/top_level.txt +0 -0
poly_web3/signature/build.py
CHANGED
|
@@ -1,113 +1,113 @@
|
|
|
1
|
-
# -*- coding = utf-8 -*-
|
|
2
|
-
# @Time: 2025/12/21 19:45
|
|
3
|
-
# @Author: PinBar
|
|
4
|
-
# @Site:
|
|
5
|
-
# @File: build.py
|
|
6
|
-
# @Software: PyCharm
|
|
7
|
-
from typing import Union, Optional
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def string_to_bytes(value: str, size: Optional[int] = None) -> bytes:
|
|
11
|
-
data = value.encode("utf-8")
|
|
12
|
-
if size is None:
|
|
13
|
-
return data
|
|
14
|
-
if len(data) > size:
|
|
15
|
-
raise ValueError(f"Size overflow: given {len(data)}, max {size}")
|
|
16
|
-
return data + b"\x00" * (size - len(data))
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def keccak256(data: bytes) -> bytes:
|
|
20
|
-
try:
|
|
21
|
-
from eth_hash.auto import keccak
|
|
22
|
-
|
|
23
|
-
return keccak(data)
|
|
24
|
-
except Exception:
|
|
25
|
-
try:
|
|
26
|
-
import sha3 # pysha3
|
|
27
|
-
|
|
28
|
-
k = sha3.keccak_256()
|
|
29
|
-
except Exception:
|
|
30
|
-
from Crypto.Hash import keccak # pycryptodome
|
|
31
|
-
|
|
32
|
-
k = keccak.new(digest_bits=256)
|
|
33
|
-
k.update(data)
|
|
34
|
-
return k.digest()
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def to_checksum_address(addr_bytes: bytes) -> str:
|
|
38
|
-
hex_addr = addr_bytes.hex()
|
|
39
|
-
hash_hex = keccak256(hex_addr.encode()).hex()
|
|
40
|
-
checksum = "".join(
|
|
41
|
-
ch.upper() if int(hash_hex[i], 16) >= 8 else ch for i, ch in enumerate(hex_addr)
|
|
42
|
-
)
|
|
43
|
-
return "0x" + checksum
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def derive_proxy_wallet(address: str, proxy_factory: str, bytecode_hash: str) -> str:
|
|
47
|
-
addr_bytes = bytes.fromhex(address[2:] if address.startswith("0x") else address)
|
|
48
|
-
factory_bytes = bytes.fromhex(
|
|
49
|
-
proxy_factory[2:] if proxy_factory.startswith("0x") else proxy_factory
|
|
50
|
-
)
|
|
51
|
-
bytecode_bytes = bytes.fromhex(
|
|
52
|
-
bytecode_hash[2:] if bytecode_hash.startswith("0x") else bytecode_hash
|
|
53
|
-
)
|
|
54
|
-
salt = keccak256(
|
|
55
|
-
addr_bytes
|
|
56
|
-
) # equivalent to keccak256(encodePacked(["address"], [address]))
|
|
57
|
-
data = b"\xff" + factory_bytes + salt + bytecode_bytes
|
|
58
|
-
create2_hash = keccak256(data)
|
|
59
|
-
return to_checksum_address(create2_hash[12:]) # last 20 bytes
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
HexLike = Union[str, bytes, int]
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def create_struct_hash(
|
|
66
|
-
from_addr: str,
|
|
67
|
-
to: str,
|
|
68
|
-
data: str,
|
|
69
|
-
tx_fee: HexLike,
|
|
70
|
-
gas_price: HexLike,
|
|
71
|
-
gas_limit: HexLike,
|
|
72
|
-
nonce: HexLike,
|
|
73
|
-
relay_hub_address: str,
|
|
74
|
-
relay_address: str,
|
|
75
|
-
) -> str:
|
|
76
|
-
def to_bytes(hex_like: HexLike, size: int | None = None) -> bytes:
|
|
77
|
-
if isinstance(hex_like, int):
|
|
78
|
-
length = (
|
|
79
|
-
size if size is not None else max(1, (hex_like.bit_length() + 7) // 8)
|
|
80
|
-
)
|
|
81
|
-
return hex_like.to_bytes(length, "big")
|
|
82
|
-
if isinstance(hex_like, bytes):
|
|
83
|
-
return hex_like.rjust(size, b"\x00") if size else hex_like
|
|
84
|
-
if isinstance(hex_like, str):
|
|
85
|
-
if hex_like.startswith("0x"):
|
|
86
|
-
raw = bytes.fromhex(hex_like[2:])
|
|
87
|
-
return raw.rjust(size, b"\x00") if size else raw
|
|
88
|
-
if hex_like.isdigit(): # numeric string -> int
|
|
89
|
-
num = int(hex_like)
|
|
90
|
-
length = (
|
|
91
|
-
size if size is not None else max(1, (num.bit_length() + 7) // 8)
|
|
92
|
-
)
|
|
93
|
-
return num.to_bytes(length, "big")
|
|
94
|
-
raw = hex_like.encode() # fallback ascii
|
|
95
|
-
return raw.rjust(size, b"\x00") if size else raw
|
|
96
|
-
raise TypeError("Unsupported type for to_bytes")
|
|
97
|
-
|
|
98
|
-
relay_hub_prefix = to_bytes("rlx:")
|
|
99
|
-
data_to_hash = b"".join(
|
|
100
|
-
[
|
|
101
|
-
relay_hub_prefix,
|
|
102
|
-
to_bytes(from_addr),
|
|
103
|
-
to_bytes(to),
|
|
104
|
-
to_bytes(data),
|
|
105
|
-
to_bytes(tx_fee, size=32),
|
|
106
|
-
to_bytes(gas_price, size=32),
|
|
107
|
-
to_bytes(gas_limit, size=32),
|
|
108
|
-
to_bytes(nonce, size=32),
|
|
109
|
-
to_bytes(relay_hub_address),
|
|
110
|
-
to_bytes(relay_address),
|
|
111
|
-
]
|
|
112
|
-
)
|
|
113
|
-
return "0x" + keccak256(data_to_hash).hex()
|
|
1
|
+
# -*- coding = utf-8 -*-
|
|
2
|
+
# @Time: 2025/12/21 19:45
|
|
3
|
+
# @Author: PinBar
|
|
4
|
+
# @Site:
|
|
5
|
+
# @File: build.py
|
|
6
|
+
# @Software: PyCharm
|
|
7
|
+
from typing import Union, Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def string_to_bytes(value: str, size: Optional[int] = None) -> bytes:
|
|
11
|
+
data = value.encode("utf-8")
|
|
12
|
+
if size is None:
|
|
13
|
+
return data
|
|
14
|
+
if len(data) > size:
|
|
15
|
+
raise ValueError(f"Size overflow: given {len(data)}, max {size}")
|
|
16
|
+
return data + b"\x00" * (size - len(data))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def keccak256(data: bytes) -> bytes:
|
|
20
|
+
try:
|
|
21
|
+
from eth_hash.auto import keccak
|
|
22
|
+
|
|
23
|
+
return keccak(data)
|
|
24
|
+
except Exception:
|
|
25
|
+
try:
|
|
26
|
+
import sha3 # pysha3
|
|
27
|
+
|
|
28
|
+
k = sha3.keccak_256()
|
|
29
|
+
except Exception:
|
|
30
|
+
from Crypto.Hash import keccak # pycryptodome
|
|
31
|
+
|
|
32
|
+
k = keccak.new(digest_bits=256)
|
|
33
|
+
k.update(data)
|
|
34
|
+
return k.digest()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def to_checksum_address(addr_bytes: bytes) -> str:
|
|
38
|
+
hex_addr = addr_bytes.hex()
|
|
39
|
+
hash_hex = keccak256(hex_addr.encode()).hex()
|
|
40
|
+
checksum = "".join(
|
|
41
|
+
ch.upper() if int(hash_hex[i], 16) >= 8 else ch for i, ch in enumerate(hex_addr)
|
|
42
|
+
)
|
|
43
|
+
return "0x" + checksum
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def derive_proxy_wallet(address: str, proxy_factory: str, bytecode_hash: str) -> str:
|
|
47
|
+
addr_bytes = bytes.fromhex(address[2:] if address.startswith("0x") else address)
|
|
48
|
+
factory_bytes = bytes.fromhex(
|
|
49
|
+
proxy_factory[2:] if proxy_factory.startswith("0x") else proxy_factory
|
|
50
|
+
)
|
|
51
|
+
bytecode_bytes = bytes.fromhex(
|
|
52
|
+
bytecode_hash[2:] if bytecode_hash.startswith("0x") else bytecode_hash
|
|
53
|
+
)
|
|
54
|
+
salt = keccak256(
|
|
55
|
+
addr_bytes
|
|
56
|
+
) # equivalent to keccak256(encodePacked(["address"], [address]))
|
|
57
|
+
data = b"\xff" + factory_bytes + salt + bytecode_bytes
|
|
58
|
+
create2_hash = keccak256(data)
|
|
59
|
+
return to_checksum_address(create2_hash[12:]) # last 20 bytes
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
HexLike = Union[str, bytes, int]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def create_struct_hash(
|
|
66
|
+
from_addr: str,
|
|
67
|
+
to: str,
|
|
68
|
+
data: str,
|
|
69
|
+
tx_fee: HexLike,
|
|
70
|
+
gas_price: HexLike,
|
|
71
|
+
gas_limit: HexLike,
|
|
72
|
+
nonce: HexLike,
|
|
73
|
+
relay_hub_address: str,
|
|
74
|
+
relay_address: str,
|
|
75
|
+
) -> str:
|
|
76
|
+
def to_bytes(hex_like: HexLike, size: int | None = None) -> bytes:
|
|
77
|
+
if isinstance(hex_like, int):
|
|
78
|
+
length = (
|
|
79
|
+
size if size is not None else max(1, (hex_like.bit_length() + 7) // 8)
|
|
80
|
+
)
|
|
81
|
+
return hex_like.to_bytes(length, "big")
|
|
82
|
+
if isinstance(hex_like, bytes):
|
|
83
|
+
return hex_like.rjust(size, b"\x00") if size else hex_like
|
|
84
|
+
if isinstance(hex_like, str):
|
|
85
|
+
if hex_like.startswith("0x"):
|
|
86
|
+
raw = bytes.fromhex(hex_like[2:])
|
|
87
|
+
return raw.rjust(size, b"\x00") if size else raw
|
|
88
|
+
if hex_like.isdigit(): # numeric string -> int
|
|
89
|
+
num = int(hex_like)
|
|
90
|
+
length = (
|
|
91
|
+
size if size is not None else max(1, (num.bit_length() + 7) // 8)
|
|
92
|
+
)
|
|
93
|
+
return num.to_bytes(length, "big")
|
|
94
|
+
raw = hex_like.encode() # fallback ascii
|
|
95
|
+
return raw.rjust(size, b"\x00") if size else raw
|
|
96
|
+
raise TypeError("Unsupported type for to_bytes")
|
|
97
|
+
|
|
98
|
+
relay_hub_prefix = to_bytes("rlx:")
|
|
99
|
+
data_to_hash = b"".join(
|
|
100
|
+
[
|
|
101
|
+
relay_hub_prefix,
|
|
102
|
+
to_bytes(from_addr),
|
|
103
|
+
to_bytes(to),
|
|
104
|
+
to_bytes(data),
|
|
105
|
+
to_bytes(tx_fee, size=32),
|
|
106
|
+
to_bytes(gas_price, size=32),
|
|
107
|
+
to_bytes(gas_limit, size=32),
|
|
108
|
+
to_bytes(nonce, size=32),
|
|
109
|
+
to_bytes(relay_hub_address),
|
|
110
|
+
to_bytes(relay_address),
|
|
111
|
+
]
|
|
112
|
+
)
|
|
113
|
+
return "0x" + keccak256(data_to_hash).hex()
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
# -*- coding = utf-8 -*-
|
|
2
|
-
# @Time: 2025/12/21 19:43
|
|
3
|
-
# @Author: PinBar
|
|
4
|
-
# @Site:
|
|
5
|
-
# @File: hash_message.py
|
|
6
|
-
# @Software: PyCharm
|
|
7
|
-
from eth_hash.auto import keccak
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def _is_hex(s: str) -> bool:
|
|
11
|
-
return isinstance(s, str) and s.startswith("0x")
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def _hex_to_bytes(h: str) -> bytes:
|
|
15
|
-
# 假设是 0x 前缀
|
|
16
|
-
return bytes.fromhex(h[2:])
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def _size_of_message(value) -> int:
|
|
20
|
-
# 对齐 viem: hex -> (len-2)/2 向上取整, bytes -> len
|
|
21
|
-
if _is_hex(value):
|
|
22
|
-
return (len(value) - 2 + 1) // 2
|
|
23
|
-
return len(value)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def _to_prefixed_message(message) -> bytes:
|
|
27
|
-
if isinstance(message, str):
|
|
28
|
-
msg_bytes = message.encode("utf-8")
|
|
29
|
-
elif isinstance(message, dict) and "raw" in message:
|
|
30
|
-
raw = message["raw"]
|
|
31
|
-
if isinstance(raw, str):
|
|
32
|
-
# viem 这里直接当作 hex 字符串使用
|
|
33
|
-
msg_bytes = _hex_to_bytes(raw)
|
|
34
|
-
else:
|
|
35
|
-
# raw 可以是 list[int], bytes, bytearray
|
|
36
|
-
msg_bytes = bytes(raw)
|
|
37
|
-
else:
|
|
38
|
-
raise TypeError("Unsupported SignableMessage")
|
|
39
|
-
|
|
40
|
-
prefix = f"\x19Ethereum Signed Message:\n{len(msg_bytes)}".encode("utf-8")
|
|
41
|
-
return prefix + msg_bytes
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def hash_message(message, to: str = "hex"):
|
|
45
|
-
digest = keccak(_to_prefixed_message(message))
|
|
46
|
-
if to == "bytes":
|
|
47
|
-
return digest
|
|
48
|
-
return "0x" + digest.hex()
|
|
1
|
+
# -*- coding = utf-8 -*-
|
|
2
|
+
# @Time: 2025/12/21 19:43
|
|
3
|
+
# @Author: PinBar
|
|
4
|
+
# @Site:
|
|
5
|
+
# @File: hash_message.py
|
|
6
|
+
# @Software: PyCharm
|
|
7
|
+
from eth_hash.auto import keccak
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _is_hex(s: str) -> bool:
|
|
11
|
+
return isinstance(s, str) and s.startswith("0x")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _hex_to_bytes(h: str) -> bytes:
|
|
15
|
+
# 假设是 0x 前缀
|
|
16
|
+
return bytes.fromhex(h[2:])
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _size_of_message(value) -> int:
|
|
20
|
+
# 对齐 viem: hex -> (len-2)/2 向上取整, bytes -> len
|
|
21
|
+
if _is_hex(value):
|
|
22
|
+
return (len(value) - 2 + 1) // 2
|
|
23
|
+
return len(value)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _to_prefixed_message(message) -> bytes:
|
|
27
|
+
if isinstance(message, str):
|
|
28
|
+
msg_bytes = message.encode("utf-8")
|
|
29
|
+
elif isinstance(message, dict) and "raw" in message:
|
|
30
|
+
raw = message["raw"]
|
|
31
|
+
if isinstance(raw, str):
|
|
32
|
+
# viem 这里直接当作 hex 字符串使用
|
|
33
|
+
msg_bytes = _hex_to_bytes(raw)
|
|
34
|
+
else:
|
|
35
|
+
# raw 可以是 list[int], bytes, bytearray
|
|
36
|
+
msg_bytes = bytes(raw)
|
|
37
|
+
else:
|
|
38
|
+
raise TypeError("Unsupported SignableMessage")
|
|
39
|
+
|
|
40
|
+
prefix = f"\x19Ethereum Signed Message:\n{len(msg_bytes)}".encode("utf-8")
|
|
41
|
+
return prefix + msg_bytes
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def hash_message(message, to: str = "hex"):
|
|
45
|
+
digest = keccak(_to_prefixed_message(message))
|
|
46
|
+
if to == "bytes":
|
|
47
|
+
return digest
|
|
48
|
+
return "0x" + digest.hex()
|
poly_web3/signature/secp256k1.py
CHANGED
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
# -*- coding = utf-8 -*-
|
|
2
|
-
# @Time: 2025/12/21 19:44
|
|
3
|
-
# @Author: PinBar
|
|
4
|
-
# @Site:
|
|
5
|
-
# @File: secp256k1.py
|
|
6
|
-
# @Software: PyCharm
|
|
7
|
-
from eth_keys import keys
|
|
8
|
-
from eth_utils import decode_hex
|
|
9
|
-
|
|
10
|
-
SECP256K1_N = int(
|
|
11
|
-
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16
|
|
12
|
-
)
|
|
13
|
-
HALF_N = SECP256K1_N // 2
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def sign(hash_hex: str, priv_hex: str):
|
|
17
|
-
msg = decode_hex(hash_hex)
|
|
18
|
-
priv = keys.PrivateKey(decode_hex(priv_hex))
|
|
19
|
-
sig = priv.sign_msg_hash(msg)
|
|
20
|
-
r, s, v = sig.r, sig.s, sig.v
|
|
21
|
-
|
|
22
|
-
if s > HALF_N:
|
|
23
|
-
s = SECP256K1_N - s
|
|
24
|
-
# Ethereum recovery id flips when s is negated
|
|
25
|
-
v ^= 1
|
|
26
|
-
|
|
27
|
-
return r, s, v
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def int_to_hex(n: int, size: int = 32) -> str:
|
|
31
|
-
# size 是字节数,32 字节 -> 64 个 hex 字符
|
|
32
|
-
return "0x" + n.to_bytes(size, "big").hex()
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def hex_to_int(h: str) -> int:
|
|
36
|
-
return int(h, 16)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def serialize_signature(r: str, s: str, v=None, yParity=None, to="hex"):
|
|
40
|
-
# 计算 yParity_
|
|
41
|
-
if yParity in (0, 1):
|
|
42
|
-
yParity_ = yParity
|
|
43
|
-
elif v is not None and (v in (27, 28) or v >= 35):
|
|
44
|
-
yParity_ = 1 if (v % 2 == 0) else 0
|
|
45
|
-
else:
|
|
46
|
-
raise ValueError("Invalid `v` or `yParity` value")
|
|
47
|
-
|
|
48
|
-
# toCompactHex: r||s (各 32 字节)
|
|
49
|
-
r_int = hex_to_int(r)
|
|
50
|
-
s_int = hex_to_int(s)
|
|
51
|
-
compact = r_int.to_bytes(32, "big").hex() + s_int.to_bytes(32, "big").hex()
|
|
52
|
-
|
|
53
|
-
signature_hex = "0x" + compact + ("1b" if yParity_ == 0 else "1c")
|
|
54
|
-
|
|
55
|
-
if to == "hex":
|
|
56
|
-
return signature_hex
|
|
57
|
-
return bytes.fromhex(signature_hex[2:])
|
|
1
|
+
# -*- coding = utf-8 -*-
|
|
2
|
+
# @Time: 2025/12/21 19:44
|
|
3
|
+
# @Author: PinBar
|
|
4
|
+
# @Site:
|
|
5
|
+
# @File: secp256k1.py
|
|
6
|
+
# @Software: PyCharm
|
|
7
|
+
from eth_keys import keys
|
|
8
|
+
from eth_utils import decode_hex
|
|
9
|
+
|
|
10
|
+
SECP256K1_N = int(
|
|
11
|
+
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16
|
|
12
|
+
)
|
|
13
|
+
HALF_N = SECP256K1_N // 2
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def sign(hash_hex: str, priv_hex: str):
|
|
17
|
+
msg = decode_hex(hash_hex)
|
|
18
|
+
priv = keys.PrivateKey(decode_hex(priv_hex))
|
|
19
|
+
sig = priv.sign_msg_hash(msg)
|
|
20
|
+
r, s, v = sig.r, sig.s, sig.v
|
|
21
|
+
|
|
22
|
+
if s > HALF_N:
|
|
23
|
+
s = SECP256K1_N - s
|
|
24
|
+
# Ethereum recovery id flips when s is negated
|
|
25
|
+
v ^= 1
|
|
26
|
+
|
|
27
|
+
return r, s, v
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def int_to_hex(n: int, size: int = 32) -> str:
|
|
31
|
+
# size 是字节数,32 字节 -> 64 个 hex 字符
|
|
32
|
+
return "0x" + n.to_bytes(size, "big").hex()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def hex_to_int(h: str) -> int:
|
|
36
|
+
return int(h, 16)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def serialize_signature(r: str, s: str, v=None, yParity=None, to="hex"):
|
|
40
|
+
# 计算 yParity_
|
|
41
|
+
if yParity in (0, 1):
|
|
42
|
+
yParity_ = yParity
|
|
43
|
+
elif v is not None and (v in (27, 28) or v >= 35):
|
|
44
|
+
yParity_ = 1 if (v % 2 == 0) else 0
|
|
45
|
+
else:
|
|
46
|
+
raise ValueError("Invalid `v` or `yParity` value")
|
|
47
|
+
|
|
48
|
+
# toCompactHex: r||s (各 32 字节)
|
|
49
|
+
r_int = hex_to_int(r)
|
|
50
|
+
s_int = hex_to_int(s)
|
|
51
|
+
compact = r_int.to_bytes(32, "big").hex() + s_int.to_bytes(32, "big").hex()
|
|
52
|
+
|
|
53
|
+
signature_hex = "0x" + compact + ("1b" if yParity_ == 0 else "1c")
|
|
54
|
+
|
|
55
|
+
if to == "hex":
|
|
56
|
+
return signature_hex
|
|
57
|
+
return bytes.fromhex(signature_hex[2:])
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
# -*- coding = utf-8 -*-
|
|
2
|
-
# @Time: 2025-12-27 16:07:53
|
|
3
|
-
# @Author: PinBar
|
|
4
|
-
# @Site:
|
|
5
|
-
# @File: __init__.py
|
|
6
|
-
# @Software: PyCharm
|
|
7
|
-
from poly_web3.web3_service.eoa_service import EOAWeb3Service
|
|
8
|
-
from poly_web3.web3_service.proxy_service import ProxyWeb3Service
|
|
9
|
-
from poly_web3.web3_service.safe_service import SafeWeb3Service
|
|
1
|
+
# -*- coding = utf-8 -*-
|
|
2
|
+
# @Time: 2025-12-27 16:07:53
|
|
3
|
+
# @Author: PinBar
|
|
4
|
+
# @Site:
|
|
5
|
+
# @File: __init__.py
|
|
6
|
+
# @Software: PyCharm
|
|
7
|
+
from poly_web3.web3_service.eoa_service import EOAWeb3Service
|
|
8
|
+
from poly_web3.web3_service.proxy_service import ProxyWeb3Service
|
|
9
|
+
from poly_web3.web3_service.safe_service import SafeWeb3Service
|