meshtensor-cli 9.18.1__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.
- meshtensor_cli/__init__.py +22 -0
- meshtensor_cli/cli.py +10742 -0
- meshtensor_cli/doc_generation_helper.py +4 -0
- meshtensor_cli/src/__init__.py +1085 -0
- meshtensor_cli/src/commands/__init__.py +0 -0
- meshtensor_cli/src/commands/axon/__init__.py +0 -0
- meshtensor_cli/src/commands/axon/axon.py +132 -0
- meshtensor_cli/src/commands/crowd/__init__.py +0 -0
- meshtensor_cli/src/commands/crowd/contribute.py +621 -0
- meshtensor_cli/src/commands/crowd/contributors.py +200 -0
- meshtensor_cli/src/commands/crowd/create.py +783 -0
- meshtensor_cli/src/commands/crowd/dissolve.py +219 -0
- meshtensor_cli/src/commands/crowd/refund.py +233 -0
- meshtensor_cli/src/commands/crowd/update.py +418 -0
- meshtensor_cli/src/commands/crowd/utils.py +124 -0
- meshtensor_cli/src/commands/crowd/view.py +991 -0
- meshtensor_cli/src/commands/governance/__init__.py +0 -0
- meshtensor_cli/src/commands/governance/governance.py +794 -0
- meshtensor_cli/src/commands/liquidity/__init__.py +0 -0
- meshtensor_cli/src/commands/liquidity/liquidity.py +699 -0
- meshtensor_cli/src/commands/liquidity/utils.py +202 -0
- meshtensor_cli/src/commands/proxy.py +700 -0
- meshtensor_cli/src/commands/stake/__init__.py +0 -0
- meshtensor_cli/src/commands/stake/add.py +799 -0
- meshtensor_cli/src/commands/stake/auto_staking.py +306 -0
- meshtensor_cli/src/commands/stake/children_hotkeys.py +865 -0
- meshtensor_cli/src/commands/stake/claim.py +770 -0
- meshtensor_cli/src/commands/stake/list.py +738 -0
- meshtensor_cli/src/commands/stake/move.py +1211 -0
- meshtensor_cli/src/commands/stake/remove.py +1466 -0
- meshtensor_cli/src/commands/stake/wizard.py +323 -0
- meshtensor_cli/src/commands/subnets/__init__.py +0 -0
- meshtensor_cli/src/commands/subnets/mechanisms.py +515 -0
- meshtensor_cli/src/commands/subnets/price.py +733 -0
- meshtensor_cli/src/commands/subnets/subnets.py +2908 -0
- meshtensor_cli/src/commands/sudo.py +1294 -0
- meshtensor_cli/src/commands/tc/__init__.py +0 -0
- meshtensor_cli/src/commands/tc/tc.py +190 -0
- meshtensor_cli/src/commands/treasury/__init__.py +0 -0
- meshtensor_cli/src/commands/treasury/treasury.py +194 -0
- meshtensor_cli/src/commands/view.py +354 -0
- meshtensor_cli/src/commands/wallets.py +2311 -0
- meshtensor_cli/src/commands/weights.py +467 -0
- meshtensor_cli/src/meshtensor/__init__.py +0 -0
- meshtensor_cli/src/meshtensor/balances.py +313 -0
- meshtensor_cli/src/meshtensor/chain_data.py +1263 -0
- meshtensor_cli/src/meshtensor/extrinsics/__init__.py +0 -0
- meshtensor_cli/src/meshtensor/extrinsics/mev_shield.py +174 -0
- meshtensor_cli/src/meshtensor/extrinsics/registration.py +1861 -0
- meshtensor_cli/src/meshtensor/extrinsics/root.py +550 -0
- meshtensor_cli/src/meshtensor/extrinsics/serving.py +255 -0
- meshtensor_cli/src/meshtensor/extrinsics/transfer.py +239 -0
- meshtensor_cli/src/meshtensor/meshtensor_interface.py +2598 -0
- meshtensor_cli/src/meshtensor/minigraph.py +254 -0
- meshtensor_cli/src/meshtensor/networking.py +12 -0
- meshtensor_cli/src/meshtensor/templates/main-filters.j2 +24 -0
- meshtensor_cli/src/meshtensor/templates/main-header.j2 +36 -0
- meshtensor_cli/src/meshtensor/templates/neuron-details.j2 +111 -0
- meshtensor_cli/src/meshtensor/templates/price-multi.j2 +113 -0
- meshtensor_cli/src/meshtensor/templates/price-single.j2 +99 -0
- meshtensor_cli/src/meshtensor/templates/subnet-details-header.j2 +49 -0
- meshtensor_cli/src/meshtensor/templates/subnet-details.j2 +32 -0
- meshtensor_cli/src/meshtensor/templates/subnet-metrics.j2 +57 -0
- meshtensor_cli/src/meshtensor/templates/subnets-table.j2 +28 -0
- meshtensor_cli/src/meshtensor/templates/table.j2 +267 -0
- meshtensor_cli/src/meshtensor/templates/view.css +1058 -0
- meshtensor_cli/src/meshtensor/templates/view.j2 +43 -0
- meshtensor_cli/src/meshtensor/templates/view.js +1053 -0
- meshtensor_cli/src/meshtensor/utils.py +2007 -0
- meshtensor_cli/version.py +23 -0
- meshtensor_cli-9.18.1.dist-info/METADATA +261 -0
- meshtensor_cli-9.18.1.dist-info/RECORD +74 -0
- meshtensor_cli-9.18.1.dist-info/WHEEL +4 -0
- meshtensor_cli-9.18.1.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
from typing import Optional, Union
|
|
4
|
+
|
|
5
|
+
from meshtensor_wallet import Wallet
|
|
6
|
+
from rich.prompt import IntPrompt, FloatPrompt
|
|
7
|
+
from rich.table import Table, Column, box
|
|
8
|
+
|
|
9
|
+
from meshtensor_cli.src import COLORS
|
|
10
|
+
from meshtensor_cli.src.meshtensor.balances import Balance
|
|
11
|
+
from meshtensor_cli.src.meshtensor.meshtensor_interface import MeshtensorInterface
|
|
12
|
+
from meshtensor_cli.src.meshtensor.utils import (
|
|
13
|
+
blocks_to_duration,
|
|
14
|
+
confirm_action,
|
|
15
|
+
console,
|
|
16
|
+
json_console,
|
|
17
|
+
print_error,
|
|
18
|
+
unlock_key,
|
|
19
|
+
print_extrinsic_id,
|
|
20
|
+
)
|
|
21
|
+
from meshtensor_cli.src.commands.crowd.view import show_crowdloan_details
|
|
22
|
+
from meshtensor_cli.src.commands.crowd.utils import get_constant
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async def update_crowdloan(
|
|
26
|
+
meshtensor: MeshtensorInterface,
|
|
27
|
+
wallet: Wallet,
|
|
28
|
+
proxy: Optional[str],
|
|
29
|
+
crowdloan_id: int,
|
|
30
|
+
min_contribution: Optional[Balance] = None,
|
|
31
|
+
end: Optional[int] = None,
|
|
32
|
+
cap: Optional[Balance] = None,
|
|
33
|
+
wait_for_inclusion: bool = True,
|
|
34
|
+
wait_for_finalization: bool = False,
|
|
35
|
+
prompt: bool = True,
|
|
36
|
+
decline: bool = False,
|
|
37
|
+
quiet: bool = False,
|
|
38
|
+
json_output: bool = False,
|
|
39
|
+
) -> tuple[bool, str]:
|
|
40
|
+
"""Update parameters of a non-finalized crowdloan.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
meshtensor: MeshtensorInterface object for chain interaction
|
|
44
|
+
wallet: Wallet object containing coldkey (must be creator)
|
|
45
|
+
proxy: Optional proxy to use for this extrinsic submissions
|
|
46
|
+
crowdloan_id: ID of the crowdloan to update
|
|
47
|
+
min_contribution: New minimum contribution in MESH (None to prompt)
|
|
48
|
+
end: New end block (None to prompt)
|
|
49
|
+
cap: New cap in MESH (None to prompt)
|
|
50
|
+
wait_for_inclusion: Wait for transaction inclusion
|
|
51
|
+
wait_for_finalization: Wait for transaction finalization
|
|
52
|
+
prompt: Whether to prompt for values
|
|
53
|
+
json_output: Whether to output JSON or human-readable
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
tuple[bool, str]: Success status and message
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
block_hash = await meshtensor.substrate.get_chain_head()
|
|
60
|
+
crowdloan, current_block = await asyncio.gather(
|
|
61
|
+
meshtensor.get_single_crowdloan(crowdloan_id, block_hash=block_hash),
|
|
62
|
+
meshtensor.substrate.get_block_number(block_hash=block_hash),
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
runtime = await meshtensor.substrate.init_runtime(block_hash=block_hash)
|
|
66
|
+
absolute_min_meshlet, min_duration, max_duration = await asyncio.gather(
|
|
67
|
+
get_constant(meshtensor, "AbsoluteMinimumContribution", runtime=runtime),
|
|
68
|
+
get_constant(meshtensor, "MinimumBlockDuration", runtime=runtime),
|
|
69
|
+
get_constant(meshtensor, "MaximumBlockDuration", runtime=runtime),
|
|
70
|
+
)
|
|
71
|
+
absolute_min = Balance.from_meshlet(absolute_min_meshlet)
|
|
72
|
+
|
|
73
|
+
if not crowdloan:
|
|
74
|
+
error_msg = f"Crowdloan #{crowdloan_id} not found."
|
|
75
|
+
if json_output:
|
|
76
|
+
json_console.print(json.dumps({"success": False, "error": error_msg}))
|
|
77
|
+
else:
|
|
78
|
+
print_error(f"[red]{error_msg}[/red]")
|
|
79
|
+
return False, error_msg
|
|
80
|
+
|
|
81
|
+
if crowdloan.finalized:
|
|
82
|
+
error_msg = (
|
|
83
|
+
f"Crowdloan #{crowdloan_id} is already finalized and cannot be updated."
|
|
84
|
+
)
|
|
85
|
+
if json_output:
|
|
86
|
+
json_console.print(json.dumps({"success": False, "error": error_msg}))
|
|
87
|
+
else:
|
|
88
|
+
print_error(f"[red]{error_msg}[/red]")
|
|
89
|
+
return False, f"Crowdloan #{crowdloan_id} is already finalized."
|
|
90
|
+
|
|
91
|
+
creator_address = wallet.coldkeypub.ss58_address
|
|
92
|
+
if creator_address != crowdloan.creator:
|
|
93
|
+
error_msg = "Only the creator can update this crowdloan."
|
|
94
|
+
if json_output:
|
|
95
|
+
json_console.print(json.dumps({"success": False, "error": error_msg}))
|
|
96
|
+
else:
|
|
97
|
+
print_error(
|
|
98
|
+
f"[red]Only the creator can update this crowdloan.[/red]\n"
|
|
99
|
+
f"Creator: [blue]{crowdloan.creator}[/blue]\n"
|
|
100
|
+
f"Your address: [blue]{creator_address}[/blue]"
|
|
101
|
+
)
|
|
102
|
+
return False, error_msg
|
|
103
|
+
|
|
104
|
+
await show_crowdloan_details(
|
|
105
|
+
meshtensor=meshtensor,
|
|
106
|
+
crowdloan_id=crowdloan_id,
|
|
107
|
+
wallet=wallet,
|
|
108
|
+
verbose=False,
|
|
109
|
+
crowdloan=crowdloan,
|
|
110
|
+
current_block=current_block,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if all(x is None for x in [min_contribution, end, cap]) and prompt:
|
|
114
|
+
console.print(
|
|
115
|
+
f"\n[bold cyan]What would you like to update for Crowdloan #{crowdloan_id}?[/bold cyan]\n"
|
|
116
|
+
)
|
|
117
|
+
time_left = blocks_to_duration(crowdloan.end - current_block)
|
|
118
|
+
choice = IntPrompt.ask(
|
|
119
|
+
f"[cyan][1][/cyan] Minimum Contribution (current: [yellow]{crowdloan.min_contribution}[/yellow])\n"
|
|
120
|
+
f"[cyan][2][/cyan] End Block (current: [yellow]block {crowdloan.end:,}[/yellow], {time_left} remaining)\n"
|
|
121
|
+
f"[cyan][3][/cyan] Cap (current: [yellow]{crowdloan.cap}[/yellow])\n"
|
|
122
|
+
f"[cyan][4][/cyan] Cancel\n\n"
|
|
123
|
+
f"Enter your choice",
|
|
124
|
+
choices=["1", "2", "3", "4"],
|
|
125
|
+
default=4,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
if choice == 4:
|
|
129
|
+
if json_output:
|
|
130
|
+
json_console.print(
|
|
131
|
+
json.dumps({"success": False, "error": "Update cancelled by user."})
|
|
132
|
+
)
|
|
133
|
+
else:
|
|
134
|
+
console.print("[yellow]Update cancelled.[/yellow]")
|
|
135
|
+
return False, "Update cancelled by user."
|
|
136
|
+
|
|
137
|
+
if choice == 1:
|
|
138
|
+
console.print(
|
|
139
|
+
f"\n[cyan]Update Minimum Contribution[/cyan]"
|
|
140
|
+
f"\n • Current: [yellow]{crowdloan.min_contribution}[/yellow]"
|
|
141
|
+
f"\n • Absolute minimum: [dim]{absolute_min}[/dim]\n"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
while True:
|
|
145
|
+
new_value = FloatPrompt.ask(
|
|
146
|
+
"Enter new minimum contribution (MESH)",
|
|
147
|
+
default=float(crowdloan.min_contribution.tao),
|
|
148
|
+
)
|
|
149
|
+
candidate = Balance.from_tao(new_value)
|
|
150
|
+
if candidate.meshlet < absolute_min.meshlet:
|
|
151
|
+
print_error(
|
|
152
|
+
f"[red]Minimum contribution must be at least {absolute_min}. Try again.[/red]"
|
|
153
|
+
)
|
|
154
|
+
continue
|
|
155
|
+
min_contribution = candidate
|
|
156
|
+
break
|
|
157
|
+
|
|
158
|
+
elif choice == 2:
|
|
159
|
+
min_end_block = current_block + min_duration
|
|
160
|
+
max_end_block = current_block + max_duration
|
|
161
|
+
duration_remaining = blocks_to_duration(crowdloan.end - current_block)
|
|
162
|
+
console.print(
|
|
163
|
+
f"\n[cyan]Update End Block[/cyan]"
|
|
164
|
+
f"\n • Current: [yellow]block {crowdloan.end:,}[/yellow] ({duration_remaining} remaining)"
|
|
165
|
+
f"\n • Current block: [dim]{current_block:,}[/dim]"
|
|
166
|
+
f"\n • Valid range: [dim]{min_end_block:,} - {max_end_block:,}[/dim]"
|
|
167
|
+
f"\n • Duration range: [dim]{blocks_to_duration(min_duration)} - {blocks_to_duration(max_duration)}[/dim]\n"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
while True:
|
|
171
|
+
candidate_end = IntPrompt.ask(
|
|
172
|
+
"Enter new end block",
|
|
173
|
+
default=crowdloan.end,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
if candidate_end <= current_block:
|
|
177
|
+
print_error(
|
|
178
|
+
f"[red]End block must be after current block ({current_block:,}). Try again.[/red]"
|
|
179
|
+
)
|
|
180
|
+
continue
|
|
181
|
+
|
|
182
|
+
duration = candidate_end - current_block
|
|
183
|
+
if duration < min_duration:
|
|
184
|
+
duration_range = f"[dim]{min_end_block} - {blocks_to_duration(min_duration)}[/dim]"
|
|
185
|
+
print_error(
|
|
186
|
+
f"[red]Duration is too short. Minimum: {duration_range}. Try again.[/red]"
|
|
187
|
+
)
|
|
188
|
+
continue
|
|
189
|
+
if duration > max_duration:
|
|
190
|
+
duration_range = f"[dim]{max_end_block} - {blocks_to_duration(max_duration)}[/dim]"
|
|
191
|
+
print_error(
|
|
192
|
+
f"[red]Duration is too long. Maximum: {duration_range}. Try again.[/red]"
|
|
193
|
+
)
|
|
194
|
+
continue
|
|
195
|
+
|
|
196
|
+
end = candidate_end
|
|
197
|
+
break
|
|
198
|
+
|
|
199
|
+
elif choice == 3:
|
|
200
|
+
console.print(
|
|
201
|
+
f"\n[cyan]Update Cap[/cyan]"
|
|
202
|
+
f"\n • Current cap: [yellow]{crowdloan.cap}[/yellow]"
|
|
203
|
+
f"\n • Already raised: [green]{crowdloan.raised}[/green]"
|
|
204
|
+
f"\n • Remaining to raise: [dim]{(crowdloan.cap.meshlet - crowdloan.raised.meshlet) / 1e9:.9f} MESH[/dim]"
|
|
205
|
+
f"\n • New cap must be >= raised amount\n"
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
while True:
|
|
209
|
+
new_value = FloatPrompt.ask(
|
|
210
|
+
"Enter new cap (MESH)",
|
|
211
|
+
default=float(crowdloan.cap.tao),
|
|
212
|
+
)
|
|
213
|
+
candidate_cap = Balance.from_tao(new_value)
|
|
214
|
+
if candidate_cap.meshlet < crowdloan.raised.meshlet:
|
|
215
|
+
print_error(
|
|
216
|
+
f"[red]Cap must be >= amount already raised ({crowdloan.raised}). Try again.[/red]"
|
|
217
|
+
)
|
|
218
|
+
continue
|
|
219
|
+
cap = candidate_cap
|
|
220
|
+
break
|
|
221
|
+
|
|
222
|
+
value: Optional[Union[Balance, int]] = None
|
|
223
|
+
call_function: Optional[str] = None
|
|
224
|
+
param_name: Optional[str] = None
|
|
225
|
+
update_type: Optional[str] = None
|
|
226
|
+
|
|
227
|
+
if min_contribution is not None:
|
|
228
|
+
value = min_contribution
|
|
229
|
+
call_function = "update_min_contribution"
|
|
230
|
+
param_name = "new_min_contribution"
|
|
231
|
+
update_type = "Minimum Contribution"
|
|
232
|
+
elif cap is not None:
|
|
233
|
+
value = cap
|
|
234
|
+
call_function = "update_cap"
|
|
235
|
+
param_name = "new_cap"
|
|
236
|
+
update_type = "Cap"
|
|
237
|
+
elif end is not None:
|
|
238
|
+
value = end
|
|
239
|
+
call_function = "update_end"
|
|
240
|
+
param_name = "new_end"
|
|
241
|
+
update_type = "End Block"
|
|
242
|
+
|
|
243
|
+
if call_function is None or value is None or param_name is None:
|
|
244
|
+
error_msg = "No update parameter specified."
|
|
245
|
+
if json_output:
|
|
246
|
+
json_console.print(json.dumps({"success": False, "error": error_msg}))
|
|
247
|
+
else:
|
|
248
|
+
print_error(f"[red]{error_msg}[/red]")
|
|
249
|
+
return False, error_msg
|
|
250
|
+
|
|
251
|
+
# Validation
|
|
252
|
+
if call_function == "update_min_contribution":
|
|
253
|
+
if value.meshlet < absolute_min.meshlet:
|
|
254
|
+
error_msg = f"Minimum contribution must be at least {absolute_min}."
|
|
255
|
+
if json_output:
|
|
256
|
+
json_console.print(json.dumps({"success": False, "error": error_msg}))
|
|
257
|
+
else:
|
|
258
|
+
print_error(
|
|
259
|
+
f"[red]Minimum contribution ({value}) must be at least {absolute_min}.[/red]"
|
|
260
|
+
)
|
|
261
|
+
return False, error_msg
|
|
262
|
+
|
|
263
|
+
elif call_function == "update_end":
|
|
264
|
+
if value <= current_block:
|
|
265
|
+
error_msg = "End block must be in the future."
|
|
266
|
+
if json_output:
|
|
267
|
+
json_console.print(json.dumps({"success": False, "error": error_msg}))
|
|
268
|
+
else:
|
|
269
|
+
print_error(
|
|
270
|
+
f"[red]End block ({value:,}) must be after current block ({current_block:,}).[/red]"
|
|
271
|
+
)
|
|
272
|
+
return False, error_msg
|
|
273
|
+
|
|
274
|
+
block_duration = value - current_block
|
|
275
|
+
if block_duration < min_duration:
|
|
276
|
+
error_msg = "Block duration too short."
|
|
277
|
+
if json_output:
|
|
278
|
+
json_console.print(json.dumps({"success": False, "error": error_msg}))
|
|
279
|
+
else:
|
|
280
|
+
print_error(
|
|
281
|
+
f"[red]Duration ({blocks_to_duration(block_duration)}) is too short. "
|
|
282
|
+
f"Minimum: [dim]{min_end_block} - {blocks_to_duration(min_duration)}[/dim][/red]"
|
|
283
|
+
)
|
|
284
|
+
return False, error_msg
|
|
285
|
+
|
|
286
|
+
if block_duration > max_duration:
|
|
287
|
+
error_msg = "Block duration too long."
|
|
288
|
+
if json_output:
|
|
289
|
+
json_console.print(json.dumps({"success": False, "error": error_msg}))
|
|
290
|
+
else:
|
|
291
|
+
print_error(
|
|
292
|
+
f"[red]Duration ({blocks_to_duration(block_duration)}) is too long. "
|
|
293
|
+
f"Maximum: [dim]{max_end_block} - {blocks_to_duration(max_duration)}[/dim][/red]"
|
|
294
|
+
)
|
|
295
|
+
return False, error_msg
|
|
296
|
+
|
|
297
|
+
elif call_function == "update_cap":
|
|
298
|
+
if value < crowdloan.raised:
|
|
299
|
+
error_msg = "Cap must be >= raised amount."
|
|
300
|
+
if json_output:
|
|
301
|
+
json_console.print(json.dumps({"success": False, "error": error_msg}))
|
|
302
|
+
else:
|
|
303
|
+
print_error(
|
|
304
|
+
f"[red]New cap ({value}) must be at least the amount already raised ({crowdloan.raised}).[/red]"
|
|
305
|
+
)
|
|
306
|
+
return False, error_msg
|
|
307
|
+
|
|
308
|
+
# Update summary
|
|
309
|
+
table = Table(
|
|
310
|
+
Column("[bold white]Parameter", style=COLORS.G.SUBHEAD),
|
|
311
|
+
Column("[bold white]Current Value", style=COLORS.G.TEMPO),
|
|
312
|
+
Column("[bold white]New Value", style=COLORS.G.TEMPO),
|
|
313
|
+
title="\n[bold cyan]Update Summary[/bold cyan]",
|
|
314
|
+
show_footer=False,
|
|
315
|
+
width=None,
|
|
316
|
+
pad_edge=False,
|
|
317
|
+
box=box.SIMPLE,
|
|
318
|
+
show_edge=True,
|
|
319
|
+
border_style="bright_black",
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
if call_function == "update_min_contribution":
|
|
323
|
+
table.add_row(
|
|
324
|
+
"Minimum Contribution", str(crowdloan.min_contribution), str(value)
|
|
325
|
+
)
|
|
326
|
+
elif call_function == "update_end":
|
|
327
|
+
table.add_row(
|
|
328
|
+
"End Block",
|
|
329
|
+
f"{crowdloan.end:,} ({blocks_to_duration(crowdloan.end - current_block)} remaining)",
|
|
330
|
+
f"{value:,} ({blocks_to_duration(value - current_block)} remaining)",
|
|
331
|
+
)
|
|
332
|
+
elif call_function == "update_cap":
|
|
333
|
+
table.add_row("Cap", str(crowdloan.cap), str(value))
|
|
334
|
+
|
|
335
|
+
console.print(table)
|
|
336
|
+
|
|
337
|
+
if prompt and not confirm_action(
|
|
338
|
+
f"\n[bold]Proceed with updating {update_type}?[/bold]",
|
|
339
|
+
default=False,
|
|
340
|
+
decline=decline,
|
|
341
|
+
quiet=quiet,
|
|
342
|
+
):
|
|
343
|
+
if json_output:
|
|
344
|
+
json_console.print(
|
|
345
|
+
json.dumps({"success": False, "error": "Update cancelled by user."})
|
|
346
|
+
)
|
|
347
|
+
else:
|
|
348
|
+
console.print("[yellow]Update cancelled.[/yellow]")
|
|
349
|
+
return False, "Update cancelled by user."
|
|
350
|
+
|
|
351
|
+
unlock_status = unlock_key(wallet)
|
|
352
|
+
if not unlock_status.success:
|
|
353
|
+
if json_output:
|
|
354
|
+
json_console.print(
|
|
355
|
+
json.dumps({"success": False, "error": unlock_status.message})
|
|
356
|
+
)
|
|
357
|
+
else:
|
|
358
|
+
print_error(f"[red]{unlock_status.message}[/red]")
|
|
359
|
+
return False, unlock_status.message
|
|
360
|
+
|
|
361
|
+
if call_function != "update_end":
|
|
362
|
+
value = value.meshlet
|
|
363
|
+
|
|
364
|
+
with console.status(
|
|
365
|
+
":satellite: Submitting update transaction...", spinner="aesthetic"
|
|
366
|
+
):
|
|
367
|
+
call = await meshtensor.substrate.compose_call(
|
|
368
|
+
call_module="Crowdloan",
|
|
369
|
+
call_function=call_function,
|
|
370
|
+
call_params={"crowdloan_id": crowdloan_id, param_name: value},
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
(
|
|
374
|
+
success,
|
|
375
|
+
error_message,
|
|
376
|
+
extrinsic_receipt,
|
|
377
|
+
) = await meshtensor.sign_and_send_extrinsic(
|
|
378
|
+
call=call,
|
|
379
|
+
wallet=wallet,
|
|
380
|
+
proxy=proxy,
|
|
381
|
+
wait_for_inclusion=wait_for_inclusion,
|
|
382
|
+
wait_for_finalization=wait_for_finalization,
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
if not success:
|
|
386
|
+
if json_output:
|
|
387
|
+
json_console.print(
|
|
388
|
+
json.dumps(
|
|
389
|
+
{
|
|
390
|
+
"success": False,
|
|
391
|
+
"error": error_message or f"Failed to update {update_type}.",
|
|
392
|
+
}
|
|
393
|
+
)
|
|
394
|
+
)
|
|
395
|
+
else:
|
|
396
|
+
print_error(f"[red]Failed to update {update_type}.[/red]\n{error_message}")
|
|
397
|
+
return False, error_message
|
|
398
|
+
|
|
399
|
+
if json_output:
|
|
400
|
+
extrinsic_id = await extrinsic_receipt.get_extrinsic_identifier()
|
|
401
|
+
output_dict = {
|
|
402
|
+
"success": True,
|
|
403
|
+
"error": None,
|
|
404
|
+
"extrinsic_identifier": extrinsic_id,
|
|
405
|
+
"data": {
|
|
406
|
+
"crowdloan_id": crowdloan_id,
|
|
407
|
+
"update_type": update_type,
|
|
408
|
+
},
|
|
409
|
+
}
|
|
410
|
+
json_console.print(json.dumps(output_dict))
|
|
411
|
+
else:
|
|
412
|
+
console.print(
|
|
413
|
+
f"[green]{update_type} updated successfully![/green]\n"
|
|
414
|
+
f"Crowdloan #{crowdloan_id} has been updated."
|
|
415
|
+
)
|
|
416
|
+
await print_extrinsic_id(extrinsic_receipt)
|
|
417
|
+
|
|
418
|
+
return True, f"{update_type} updated successfully."
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from async_substrate_interface.types import Runtime
|
|
5
|
+
from rich.prompt import Prompt
|
|
6
|
+
|
|
7
|
+
from meshtensor_cli.src.meshtensor.meshtensor_interface import MeshtensorInterface
|
|
8
|
+
from meshtensor_cli.src.meshtensor.utils import console, json_console, print_error
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async def prompt_custom_call_params(
|
|
12
|
+
meshtensor: MeshtensorInterface,
|
|
13
|
+
json_output: bool = False,
|
|
14
|
+
) -> tuple[bool, Optional[str], Optional[str], Optional[str], Optional[str]]:
|
|
15
|
+
"""
|
|
16
|
+
Prompt user for custom call parameters (pallet, method, and JSON args)
|
|
17
|
+
and validate that the call can be composed.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
meshtensor: MeshtensorInterface instance for call validation
|
|
21
|
+
json_output: Whether to output errors as JSON
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Tuple of (success, pallet_name, method_name, args_json, error_msg)
|
|
25
|
+
On success: (True, pallet, method, args, None)
|
|
26
|
+
On failure: (False, None, None, None, error_msg)
|
|
27
|
+
"""
|
|
28
|
+
if not json_output:
|
|
29
|
+
console.print(
|
|
30
|
+
"\n[bold cyan]Custom Call Parameters[/bold cyan]\n"
|
|
31
|
+
"[dim]You'll need to provide a pallet (module) name, method name, and optional JSON arguments.\n\n"
|
|
32
|
+
"[yellow]Examples:[/yellow]\n"
|
|
33
|
+
" • Pallet: [cyan]MeshtensorModule[/cyan], [cyan]Balances[/cyan], [cyan]System[/cyan]\n"
|
|
34
|
+
" • Method: [cyan]transfer_allow_death[/cyan], [cyan]transfer_keep_alive[/cyan], [cyan]transfer_all[/cyan]\n"
|
|
35
|
+
' • Args: [cyan]{"dest": "5D...", "value": 1000000000}[/cyan] or [cyan]{}[/cyan] for empty\n'
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
pallet = Prompt.ask("Enter pallet name")
|
|
39
|
+
if not pallet.strip():
|
|
40
|
+
if json_output:
|
|
41
|
+
json_console.print(
|
|
42
|
+
json.dumps({"success": False, "error": "Pallet name cannot be empty."})
|
|
43
|
+
)
|
|
44
|
+
else:
|
|
45
|
+
print_error("[red]Pallet name cannot be empty.[/red]")
|
|
46
|
+
return await prompt_custom_call_params(meshtensor, json_output)
|
|
47
|
+
|
|
48
|
+
method = Prompt.ask("Enter method name")
|
|
49
|
+
if not method.strip():
|
|
50
|
+
if json_output:
|
|
51
|
+
json_console.print(
|
|
52
|
+
json.dumps({"success": False, "error": "Method name cannot be empty."})
|
|
53
|
+
)
|
|
54
|
+
else:
|
|
55
|
+
print_error("[red]Method name cannot be empty.[/red]")
|
|
56
|
+
return await prompt_custom_call_params(meshtensor, json_output)
|
|
57
|
+
|
|
58
|
+
args_input = Prompt.ask(
|
|
59
|
+
"Enter custom call arguments as JSON [dim](or press Enter for empty: {})[/dim]",
|
|
60
|
+
default="{}",
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
call_params = json.loads(args_input)
|
|
65
|
+
except json.JSONDecodeError as e:
|
|
66
|
+
error_msg = f"Invalid JSON: {e}"
|
|
67
|
+
if json_output:
|
|
68
|
+
json_console.print(json.dumps({"success": False, "error": error_msg}))
|
|
69
|
+
else:
|
|
70
|
+
print_error(f"[red]{error_msg}[/red]")
|
|
71
|
+
print_error(
|
|
72
|
+
'[yellow]Please try again. Example: {"param1": "value", "param2": 123}[/yellow]'
|
|
73
|
+
)
|
|
74
|
+
return await prompt_custom_call_params(meshtensor, json_output)
|
|
75
|
+
|
|
76
|
+
call, error_msg = await meshtensor.compose_custom_crowdloan_call(
|
|
77
|
+
pallet_name=pallet,
|
|
78
|
+
method_name=method,
|
|
79
|
+
call_params=call_params,
|
|
80
|
+
)
|
|
81
|
+
if call is None:
|
|
82
|
+
if json_output:
|
|
83
|
+
json_console.print(json.dumps({"success": False, "error": error_msg}))
|
|
84
|
+
else:
|
|
85
|
+
print_error(f"[red]Failed to compose call: {error_msg}[/red]")
|
|
86
|
+
console.print(
|
|
87
|
+
"[yellow]Please check:\n"
|
|
88
|
+
" • Pallet name exists in runtime\n"
|
|
89
|
+
" • Method name exists in the pallet\n"
|
|
90
|
+
" • Arguments match the method's expected parameters[/yellow]\n"
|
|
91
|
+
)
|
|
92
|
+
return await prompt_custom_call_params(meshtensor, json_output)
|
|
93
|
+
|
|
94
|
+
return True, pallet, method, args_input, None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
async def get_constant(
|
|
98
|
+
meshtensor: MeshtensorInterface,
|
|
99
|
+
constant_name: str,
|
|
100
|
+
runtime: Optional[Runtime] = None,
|
|
101
|
+
block_hash: Optional[str] = None,
|
|
102
|
+
) -> int:
|
|
103
|
+
"""
|
|
104
|
+
Get a constant from the Crowdloan pallet.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
meshtensor: MeshtensorInterface object for chain interaction
|
|
108
|
+
constant_name: Name of the constant to get
|
|
109
|
+
runtime: Runtime object
|
|
110
|
+
block_hash: Block hash
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
The value of the constant
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
runtime = runtime or await meshtensor.substrate.init_runtime(block_hash=block_hash)
|
|
117
|
+
|
|
118
|
+
result = await meshtensor.substrate.get_constant(
|
|
119
|
+
module_name="Crowdloan",
|
|
120
|
+
constant_name=constant_name,
|
|
121
|
+
block_hash=block_hash,
|
|
122
|
+
runtime=runtime,
|
|
123
|
+
)
|
|
124
|
+
return getattr(result, "value", result)
|