mm-eth 0.1.4__py3-none-any.whl → 0.2.0__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,252 +0,0 @@
1
- import time
2
- from pathlib import Path
3
- from typing import Self
4
-
5
- from loguru import logger
6
- from mm_std import Err, Ok, print_json, str_to_list, utc_now
7
- from pydantic import Field, StrictStr, field_validator, model_validator
8
-
9
- from mm_eth import rpc
10
- from mm_eth.account import create_private_keys_dict, private_to_address
11
- from mm_eth.cli import calcs, cli_helpers, cli_utils, validators
12
- from mm_eth.cli.cli_helpers import get_version
13
- from mm_eth.cli.cli_utils import BaseConfig
14
- from mm_eth.tx import sign_tx
15
- from mm_eth.utils import from_wei_str
16
-
17
-
18
- class Config(BaseConfig):
19
- class Tx(BaseConfig):
20
- from_address: str
21
- to_address: str
22
-
23
- nodes: list[StrictStr]
24
- chain_id: int
25
- private_keys: dict[str, str] = Field(default_factory=dict)
26
- private_keys_file: str | None = None
27
- max_fee_per_gas: str
28
- max_fee_per_gas_limit: str | None = None
29
- max_priority_fee_per_gas: str
30
- value: str
31
- value_min_limit: str | None = None
32
- gas: str
33
- addresses_map: str | None = None
34
- addresses_from_file: str | None = None
35
- addresses_to_file: str | None = None
36
- delay: str | None = None # in seconds
37
- round_ndigits: int = 5
38
- log_debug: str | None = None
39
- log_info: str | None = None
40
- txs: list[Tx] = Field(default_factory=list)
41
-
42
- @property
43
- def from_addresses(self) -> list[str]:
44
- return [tx.from_address for tx in self.txs]
45
-
46
- @field_validator("log_debug", "log_info", mode="before")
47
- def log_validator(cls, v: str | None) -> str | None:
48
- return validators.log_validator(v)
49
-
50
- @field_validator("nodes", mode="before")
51
- def nodes_validator(cls, v: str | list[str] | None) -> list[str]:
52
- return validators.nodes_validator(v)
53
-
54
- @field_validator("private_keys", mode="before")
55
- def private_keys_validator(cls, v: str | list[str] | None) -> dict[str, str]:
56
- if v is None:
57
- return {}
58
- if isinstance(v, str):
59
- return create_private_keys_dict(str_to_list(v, unique=True, remove_comments=True))
60
- return create_private_keys_dict(v)
61
-
62
- # noinspection DuplicatedCode
63
- @model_validator(mode="after")
64
- def final_validator(self) -> Self:
65
- # load private keys from file
66
- if self.private_keys_file:
67
- file = Path(self.private_keys_file).expanduser()
68
- if not file.is_file():
69
- raise ValueError("can't read private_keys_file")
70
- for line in file.read_text().strip().split("\n"):
71
- line = line.strip()
72
- address = private_to_address(line)
73
- if address is None:
74
- raise ValueError("there is not a private key in private_keys_file")
75
- self.private_keys[address.lower()] = line
76
-
77
- # max_fee_per_gas
78
- if not validators.is_valid_calc_var_wei_value(self.max_fee_per_gas, "base"):
79
- raise ValueError(f"wrong max_fee_per_gas: {self.max_fee_per_gas}")
80
-
81
- # max_fee_per_gas_limit
82
- if not validators.is_valid_calc_var_wei_value(self.max_fee_per_gas_limit, "base"):
83
- raise ValueError(f"wrong max_fee_per_gas_limit: {self.max_fee_per_gas_limit}")
84
-
85
- # max_priority_fee_per_gas
86
- if not validators.is_valid_calc_var_wei_value(self.max_priority_fee_per_gas):
87
- raise ValueError(f"wrong max_priority_fee_per_gas: {self.max_priority_fee_per_gas}")
88
-
89
- # value
90
- if not validators.is_valid_calc_var_wei_value(self.value, "balance"):
91
- raise ValueError(f"wrong value: {self.value}")
92
-
93
- # value_min_limit
94
- if not validators.is_valid_calc_var_wei_value(self.value_min_limit):
95
- raise ValueError(f"wrong value_min_limit: {self.value_min_limit}")
96
-
97
- # gas
98
- if not validators.is_valid_calc_var_wei_value(self.gas, "estimate"):
99
- raise ValueError(f"wrong gas: {self.gas}")
100
-
101
- # delay
102
- if not validators.is_valid_calc_decimal_value(self.delay):
103
- raise ValueError(f"wrong delay: {self.delay}")
104
-
105
- # txs
106
- if self.addresses_map:
107
- for tx in cli_utils.load_tx_addresses_from_str(self.addresses_map):
108
- self.txs.append(Config.Tx(from_address=tx[0], to_address=tx[1]))
109
- if self.addresses_from_file and self.addresses_to_file:
110
- self.txs.extend(
111
- Config.Tx(from_address=tx[0], to_address=tx[1])
112
- for tx in cli_utils.load_tx_addresses_from_files(self.addresses_from_file, self.addresses_to_file)
113
- )
114
- if not self.txs:
115
- raise ValueError("txs is empty")
116
-
117
- return self
118
-
119
-
120
- def run(
121
- config_path: str,
122
- *,
123
- print_balances: bool,
124
- print_config: bool,
125
- debug: bool,
126
- no_receipt: bool,
127
- emulate: bool,
128
- ) -> None:
129
- config = cli_utils.read_config(Config, config_path)
130
- if print_config:
131
- print_json(config.model_dump(exclude={"private_keys", "addresses_map"}))
132
- exit(0)
133
-
134
- cli_utils.init_logger(debug, config.log_debug, config.log_info)
135
- cli_utils.check_nodes_for_chain_id(config.nodes, config.chain_id)
136
-
137
- if print_balances:
138
- cli_helpers.print_balances(config.nodes, config.from_addresses, round_ndigits=config.round_ndigits)
139
- exit(0)
140
-
141
- return _run_transfers(config, no_receipt=no_receipt, emulate=emulate)
142
-
143
-
144
- # noinspection DuplicatedCode
145
- def _run_transfers(config: Config, *, no_receipt: bool, emulate: bool) -> None:
146
- logger.info(f"started at {utc_now()} UTC")
147
- logger.debug(f"config={config.model_dump(exclude={'private_keys', 'addresses_map'}) | {'version': get_version()} }")
148
- cli_utils.check_private_keys(config.from_addresses, config.private_keys)
149
- for i, tx in enumerate(config.txs):
150
- _transfer(from_address=tx.from_address, to_address=tx.to_address, config=config, no_receipt=no_receipt, emulate=emulate)
151
- if not emulate and config.delay is not None and i < len(config.txs) - 1:
152
- delay_value = calcs.calc_decimal_value(config.delay)
153
- logger.debug(f"delay {delay_value} seconds")
154
- time.sleep(float(delay_value))
155
- logger.info(f"finished at {utc_now()} UTC")
156
-
157
-
158
- # noinspection DuplicatedCode
159
- def _transfer(*, from_address: str, to_address: str, config: Config, no_receipt: bool, emulate: bool) -> None:
160
- log_prefix = f"{from_address}->{to_address}"
161
- # get nonce
162
- nonce = cli_helpers.get_nonce(config.nodes, from_address, log_prefix)
163
- if nonce is None:
164
- return
165
-
166
- # get max_fee_per_gas
167
- max_fee_per_gas = cli_helpers.calc_max_fee_per_gas(config.nodes, config.max_fee_per_gas, log_prefix)
168
- if max_fee_per_gas is None:
169
- return
170
-
171
- # check max_fee_per_gas_limit
172
- if cli_helpers.is_max_fee_per_gas_limit_exceeded(max_fee_per_gas, config.max_fee_per_gas_limit, log_prefix):
173
- return
174
-
175
- # get gas
176
- gas = cli_helpers.calc_gas(
177
- nodes=config.nodes,
178
- gas=config.gas,
179
- from_address=from_address,
180
- to_address=to_address,
181
- value=123,
182
- log_prefix=log_prefix,
183
- )
184
- if gas is None:
185
- return
186
-
187
- # get value
188
- value = cli_helpers.calc_eth_value(
189
- nodes=config.nodes,
190
- value_str=config.value,
191
- address=from_address,
192
- gas=gas,
193
- max_fee_per_gas=max_fee_per_gas,
194
- log_prefix=log_prefix,
195
- )
196
- if value is None:
197
- return
198
-
199
- # value_min_limit
200
- if cli_helpers.is_value_less_min_limit(config.value_min_limit, value, "eth", log_prefix=log_prefix):
201
- return
202
-
203
- max_priority_fee_per_gas = calcs.calc_var_wei_value(config.max_priority_fee_per_gas)
204
- tx_params = {
205
- "nonce": nonce,
206
- "max_fee_per_gas": max_fee_per_gas,
207
- "max_priority_fee_per_gas": max_priority_fee_per_gas,
208
- "gas": gas,
209
- "value": value,
210
- "to": to_address,
211
- "chain_id": config.chain_id,
212
- }
213
-
214
- # emulate?
215
- if emulate:
216
- msg = f"{log_prefix}: emulate, value={from_wei_str(value, 'eth', config.round_ndigits)},"
217
- msg += f" max_fee_per_gas={from_wei_str(max_fee_per_gas, 'gwei', config.round_ndigits)},"
218
- msg += f" max_priority_fee_per_gas={from_wei_str(max_priority_fee_per_gas, 'gwei', config.round_ndigits)},"
219
- msg += f" gas={gas}"
220
- logger.info(msg)
221
- return
222
-
223
- logger.debug(f"{log_prefix}: tx_params={tx_params}")
224
- signed_tx = sign_tx(
225
- nonce=nonce,
226
- max_fee_per_gas=max_fee_per_gas,
227
- max_priority_fee_per_gas=max_priority_fee_per_gas,
228
- gas=gas,
229
- private_key=config.private_keys[from_address],
230
- chain_id=config.chain_id,
231
- value=value,
232
- to=to_address,
233
- )
234
- res = rpc.eth_send_raw_transaction(config.nodes, signed_tx.raw_tx, attempts=5)
235
- if isinstance(res, Err):
236
- logger.info(f"{log_prefix}: send_error: {res.err}")
237
- return
238
- tx_hash = res.ok
239
-
240
- if no_receipt:
241
- msg = f"{log_prefix}: tx_hash={tx_hash}, value={from_wei_str(value, 'ether', round_ndigits=config.round_ndigits)}"
242
- logger.info(msg)
243
- else:
244
- logger.debug(f"{log_prefix}: tx_hash={tx_hash}, wait receipt")
245
- while True:
246
- receipt_res = rpc.get_tx_status(config.nodes, tx_hash)
247
- if isinstance(receipt_res, Ok):
248
- status = "OK" if receipt_res.ok == 1 else "FAIL"
249
- msg = f"{log_prefix}: tx_hash={tx_hash}, value={from_wei_str(value, 'ether', round_ndigits=config.round_ndigits)}, status={status}" # noqa
250
- logger.info(msg)
251
- break
252
- time.sleep(1)
@@ -1,16 +0,0 @@
1
- from mm_std import fatal, print_plain
2
-
3
- from mm_eth import vault
4
- from mm_eth.cli import cli_utils
5
-
6
-
7
- def run(keys_url: str, vault_token: str, keys_file: str) -> None:
8
- private_keys = cli_utils.load_private_keys_from_file(keys_file)
9
- if not private_keys:
10
- fatal("private keys not found")
11
-
12
- res = vault.set_keys_from_vault(keys_url, vault_token, private_keys)
13
- if res.is_ok() and res.ok is True:
14
- print_plain(f"saved {len(private_keys)} private keys to the vault")
15
- else:
16
- fatal(f"error: {res.err}")
@@ -1,15 +0,0 @@
1
- addresses: |
2
- 0x10fd602Bff689e64D4720D1DCCCD3494f1f16623
3
- 0x58487485c3858109f5A37e42546FE87473f79a4b
4
- 0x97C77B548aE0d4925F5C201220fC6d8996424309
5
-
6
- tokens: |
7
- 0x7EdF3b8579c21A8820b4C0B8352541c1CE04045f # USDT
8
- 0x6a55fe4884DE7E1d904BdC47A3BA092240ae9B39 # USDC
9
-
10
- nodes: |
11
- https://arb1.arbitrum.io/rpc
12
- https://rpc.arb1.arbitrum.gateway.fm
13
- https://arbitrum-one.publicnode.com
14
-
15
- round_ndigits: 3
@@ -1,5 +0,0 @@
1
- contract_address: "0xBa985cad26658EB00eA42aCc7516aed52e7a8AcC"
2
- function_signature: balanceOf(address)
3
- function_args: "['0x83aC43147BA5dAA5abc4ccEA84F2B8000bA82f26']"
4
- outputs_types: uint256 # optional
5
- node: https://rpc.eth.gateway.fm
@@ -1,26 +0,0 @@
1
- token: "0x60631C856303731BE4deb81C0303F80B652aA5b4" # USDC
2
- decimals: 6
3
- max_fee_per_gas: 1.2base + 1gwei + random(1,200) # supported var_name=base
4
- max_fee_per_gas_limit: 10.1gwei - random(1,10) # optional
5
- max_priority_fee_per_gas: 1gwei + random(1,12)
6
- gas: estimate + random(100,200) - 19 # supported var_name=estimate
7
- value: 0.5balance - random(1.5t,3t) + 11gwei # supported var_name=balance
8
- value_min_limit: 0.5t + random(1,2) - 7
9
- addresses_map: |
10
- 0x10fd602Bff689e64D4720D1DCCCD3494f1f16623 0x58487485c3858109f5A37e42546FE87473f79a4b
11
- 0x97C77B548aE0d4925F5C201220fC6d8996424309 0x7EdF3b8579c21A8820b4C0B8352541c1CE04045f # can comment here
12
- # and here
13
- addresses_from_file: ~/path/from.txt
14
- addresses_to_file: ~/path/to.txt
15
- delay: random(1.123,10) + 1 # secs, optional
16
- private_keys: | # optional, private_keys or private_keys_file must be used
17
- 0x7bb5b9c0ba991275f84b796b4d25fd3a8d7320911f50fade85410e7a2b000632
18
- 0xb7e0b671e176b04ceb0897a698d34771bfe9acf29273dc52a141be6e97145a00
19
- private_keys_file: ~/path/private_keys.txt # optional, private_keys or private_keys_file must be used
20
- log_debug: /path/to/file_debug.log # optional
21
- log_info: /path/to/file_info.log # optional
22
- round_ndigits: 6 # optional, default=5
23
- chain_id: 421613
24
- nodes: |
25
- https://arbitrum-goerli.publicnode.com
26
- https://rpc.goerli.arbitrum.gateway.fm
@@ -1,24 +0,0 @@
1
- max_fee_per_gas: 1.2base + 1gwei + random(1,200) # supported var_name=base
2
- max_fee_per_gas_limit: 10.1gwei - random(1,10) # optional
3
- max_priority_fee_per_gas: 1gwei + random(1,12)
4
- gas: estimate + random(100,200) - 19 # supported var_name=estimate
5
- value: balance - random(0.002eth,0.0025eth) + 11gwei # supported var_name=balance. If 'balance' is used, value=calc(value) - gas*max_fee_per_gas
6
- value_min_limit: 0.001eth + random(1,2) - 7
7
- addresses_map: |
8
- 0x10fd602Bff689e64D4720D1DCCCD3494f1f16623 0x58487485c3858109f5A37e42546FE87473f79a4b
9
- 0x97C77B548aE0d4925F5C201220fC6d8996424309 0x7EdF3b8579c21A8820b4C0B8352541c1CE04045f # can comment here
10
- # and here
11
- #addresses_from_file: ~/path/from.txt
12
- #addresses_to_file: ~/path/to.txt
13
- delay: random(1.123,10) + 1 # secs
14
- #private_keys: |
15
- # 0x7bb5b9c0ba991275f84b796b4d25fd3a8d7320911f50fade85410e7a2b000632
16
- # 0xb7e0b671e176b04ceb0897a698d34771bfe9acf29273dc52a141be6e97145a00
17
- private_keys_file: ~/path/private_keys.txt
18
- log_debug: /path/to/file_debug.log # optional
19
- log_info: /path/to/file_info.log # optional
20
- round_ndigits: 6
21
- chain_id: 421613
22
- nodes: |
23
- https://arbitrum-goerli.publicnode.com
24
- https://rpc.goerli.arbitrum.gateway.fm
mm_eth/cli/validators.py DELETED
@@ -1,84 +0,0 @@
1
- import os
2
- from decimal import Decimal, InvalidOperation
3
- from pathlib import Path
4
-
5
- from mm_std import str_to_list
6
-
7
- from mm_eth.cli import calcs
8
- from mm_eth.utils import to_wei
9
-
10
-
11
- def wei_validator(v: str | None) -> int | None:
12
- if v is None:
13
- return None
14
- return to_wei(v)
15
-
16
-
17
- def log_validator(v: str | None) -> str | None:
18
- if v is None:
19
- return None
20
- log_file = Path(v).expanduser()
21
- log_file.touch(exist_ok=True)
22
- if not (log_file.is_file() and os.access(log_file, os.W_OK)):
23
- raise ValueError(f"wrong log path: {v}")
24
- return v
25
-
26
-
27
- def nodes_validator(v: str | list[str] | None) -> list[str]:
28
- if v is None:
29
- return []
30
- if isinstance(v, str):
31
- return str_to_list(v, unique=True, remove_comments=True, split_line=True)
32
- return v
33
-
34
-
35
- def addresses_validator(v: str | list[str] | None) -> list[str]:
36
- if v is None:
37
- return []
38
- if isinstance(v, str):
39
- return str_to_list(v, unique=True, remove_comments=True, split_line=True, lower=True)
40
- return v
41
-
42
-
43
- def delay_validator(v: str | Decimal) -> Decimal | tuple[Decimal, Decimal]:
44
- if isinstance(v, int | float):
45
- return Decimal(str(v))
46
- elif isinstance(v, str):
47
- arr = [a.strip() for a in v.split("-")]
48
- if len(arr) != 2:
49
- raise ValueError("wrong delay value")
50
- try:
51
- return Decimal(arr[0]), Decimal(arr[1])
52
- except InvalidOperation:
53
- raise ValueError("wrong delay value") from None
54
- raise ValueError("wrong delay value")
55
-
56
-
57
- def is_valid_calc_var_wei_value(value: str | None, base_name: str = "var", decimals: int | None = None) -> bool:
58
- if value is None:
59
- return True # check for None on BaseModel.field type level
60
- try:
61
- calcs.calc_var_wei_value(value, var_value=123, var_name=base_name, decimals=decimals)
62
- return True
63
- except ValueError:
64
- return False
65
-
66
-
67
- def is_valid_calc_decimal_value(value: str | None) -> bool:
68
- if value is None:
69
- return True # check for None on BaseModel.field type level
70
- try:
71
- calcs.calc_decimal_value(value)
72
- return True
73
- except ValueError:
74
- return False
75
-
76
-
77
- def is_valid_calc_function_args(value: str | None) -> bool:
78
- if value is None:
79
- return True
80
- try:
81
- calcs.calc_function_args(value)
82
- return True
83
- except ValueError:
84
- return False
File without changes
mm_eth/zksync.py DELETED
@@ -1,203 +0,0 @@
1
- import json
2
- import pkgutil
3
- from typing import cast
4
-
5
- from eth_typing import ABI, ChecksumAddress, HexStr
6
- from hexbytes import HexBytes
7
- from mm_std import Err, Ok, Result
8
- from pydantic import BaseModel, Field
9
- from web3 import Web3
10
- from web3.types import Nonce, TxParams, Wei
11
-
12
- from mm_eth import abi, rpc
13
- from mm_eth.rpc import rpc_call
14
- from mm_eth.types import Nodes, Proxies
15
- from mm_eth.utils import hex_str_to_int
16
-
17
- L2_ETH_TOKEN_ADDRESS = Web3.to_checksum_address("0x000000000000000000000000000000000000800a")
18
-
19
-
20
- class BridgeContracts(BaseModel):
21
- l1_erc20_Default_bridge: str = Field(..., alias="l1Erc20DefaultBridge")
22
- l2_erc20_Default_bridge: str = Field(..., alias="l2Erc20DefaultBridge")
23
-
24
-
25
- def zks_get_bridge_contracts(
26
- rpc_urls: Nodes,
27
- timeout: int = 10,
28
- proxies: Proxies = None,
29
- attempts: int = 1,
30
- ) -> Result[BridgeContracts]:
31
- res = rpc_call(
32
- nodes=rpc_urls,
33
- method="zks_getBridgeContracts",
34
- params=[],
35
- timeout=timeout,
36
- proxies=proxies,
37
- attempts=attempts,
38
- )
39
- if isinstance(res, Err):
40
- return res
41
-
42
- try:
43
- return Ok(BridgeContracts(**res.ok), data=res.data)
44
- except Exception as e:
45
- return Err(f"exception: {e}", data=res.data)
46
-
47
-
48
- def zks_get_main_contract(
49
- rpc_urls: Nodes,
50
- timeout: int = 10,
51
- proxies: Proxies = None,
52
- attempts: int = 1,
53
- ) -> Result[str]:
54
- return rpc_call(
55
- nodes=rpc_urls,
56
- method="zks_getMainContract",
57
- params=[],
58
- timeout=timeout,
59
- proxies=proxies,
60
- attempts=attempts,
61
- )
62
-
63
-
64
- def zks_estimate_gas_l1_to_l2(
65
- rpc_urls: Nodes,
66
- timeout: int = 10,
67
- proxies: Proxies = None,
68
- attempts: int = 1,
69
- ) -> Result[int]:
70
- return rpc_call(
71
- nodes=rpc_urls,
72
- method="zks_estimateGasL1ToL2",
73
- params=[
74
- [
75
- {
76
- "from": "0x1111111111111111111111111111111111111111",
77
- "to": "0x2222222222222222222222222222222222222222",
78
- "data": "0xffffffff",
79
- },
80
- ],
81
- ],
82
- timeout=timeout,
83
- proxies=proxies,
84
- attempts=attempts,
85
- ).and_then(hex_str_to_int)
86
-
87
-
88
- def zksync_contract_abi() -> ABI:
89
- data = pkgutil.get_data(__name__, "abi/zksync.json")
90
- if data is None:
91
- raise RuntimeError("can't read abi/zksync.json")
92
- return cast(ABI, json.loads(data.decode()))
93
-
94
-
95
- def get_l2_transactio_base_cost(
96
- eth_rpc: str,
97
- contract_address: ChecksumAddress,
98
- gas_price: Wei,
99
- l2_gas_limit: int = 10000000,
100
- l2_gas_per_pubdata_byte_limit: int = 800,
101
- ) -> Result[Wei]:
102
- w3 = Web3(Web3.HTTPProvider(eth_rpc))
103
- try:
104
- contract = w3.eth.contract(address=contract_address, abi=zksync_contract_abi())
105
- res = contract.functions.l2TransactionBaseCost(gas_price, l2_gas_limit, l2_gas_per_pubdata_byte_limit).call()
106
- return Ok(Wei(res))
107
- except Exception as e:
108
- return Err(str(e))
109
-
110
-
111
- def deposit(
112
- *,
113
- eth_rpc: str,
114
- contract_address: ChecksumAddress,
115
- wallet_address: ChecksumAddress,
116
- private_key: str,
117
- gas_price: Wei,
118
- value: Wei,
119
- l2_gas_limit: int = 1_300_000,
120
- l2_gas_per_pubdata_byte_limit: int = 800,
121
- gas: int = 150096,
122
- nonce: Nonce | None = None,
123
- ) -> Result[str]:
124
- w3 = Web3(Web3.HTTPProvider(eth_rpc))
125
- try:
126
- # get nonce
127
- if nonce is None:
128
- res_nonce = rpc.eth_get_transaction_count(eth_rpc, wallet_address)
129
- if isinstance(res_nonce, Err):
130
- return res_nonce
131
-
132
- nonce = Nonce(res_nonce.ok)
133
-
134
- # get base_cost
135
- res = get_l2_transactio_base_cost(eth_rpc, contract_address, gas_price, l2_gas_limit, l2_gas_per_pubdata_byte_limit)
136
- if isinstance(res, Err):
137
- return res
138
- base_cost = Wei(res.ok)
139
-
140
- contract_address_l2 = wallet_address
141
- l2_value = value
142
- calldata = b""
143
- factory_deps = [] # type: ignore[var-annotated]
144
- refund_recipient = wallet_address
145
-
146
- contract = w3.eth.contract(address=contract_address, abi=zksync_contract_abi())
147
- function_call = contract.functions.requestL2Transaction(
148
- contract_address_l2,
149
- l2_value,
150
- calldata,
151
- l2_gas_limit,
152
- l2_gas_per_pubdata_byte_limit,
153
- factory_deps,
154
- refund_recipient,
155
- )
156
-
157
- transaction_data = function_call.build_transaction(
158
- {"from": wallet_address, "gas": gas, "gasPrice": gas_price, "nonce": nonce, "value": Wei(value + base_cost)},
159
- )
160
- signed_transaction = w3.eth.account.sign_transaction(transaction_data, private_key)
161
- txn = w3.eth.send_raw_transaction(signed_transaction.rawTransaction)
162
- return Ok(str(txn))
163
- except Exception as e:
164
- return Err(f"exception: {e}")
165
-
166
-
167
- def withdraw(
168
- *,
169
- zksync_rpc: str,
170
- wallet_address: ChecksumAddress,
171
- private_key: str,
172
- value: Wei,
173
- chain_id: int,
174
- ) -> Result[HexBytes]:
175
- try:
176
- w3 = Web3(Web3.HTTPProvider(zksync_rpc))
177
-
178
- # get nonce
179
- nonce = w3.eth.get_transaction_count(wallet_address)
180
-
181
- input_data = abi.encode_function_signature("withdraw(address)") + abi.encode_data(
182
- ["address"],
183
- [wallet_address],
184
- ).removeprefix("0x")
185
-
186
- transaction_data: TxParams = {
187
- "from": wallet_address,
188
- "to": L2_ETH_TOKEN_ADDRESS,
189
- "nonce": nonce,
190
- "data": HexStr(input_data),
191
- "value": value,
192
- "chainId": chain_id,
193
- }
194
- gas = w3.eth.estimate_gas(transaction_data)
195
- transaction_data["gas"] = gas
196
- gas_price = w3.eth.gas_price
197
- transaction_data["gasPrice"] = gas_price
198
- signed_transaction = w3.eth.account.sign_transaction(transaction_data, private_key)
199
- txn = w3.eth.send_raw_transaction(signed_transaction.rawTransaction)
200
- return Ok(txn)
201
-
202
- except Exception as e:
203
- return Err(f"exception: {e}")
@@ -1,9 +0,0 @@
1
- Metadata-Version: 2.3
2
- Name: mm-eth
3
- Version: 0.1.4
4
- Requires-Python: >=3.12
5
- Requires-Dist: loguru~=0.7.2
6
- Requires-Dist: mm-std~=0.1.6
7
- Requires-Dist: typer>=0.13.0
8
- Requires-Dist: web3~=7.5.0
9
- Requires-Dist: websocket-client~=1.8.0