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,219 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from meshtensor_wallet import Wallet
|
|
6
|
+
from rich.table import Column, Table, box
|
|
7
|
+
|
|
8
|
+
from meshtensor_cli.src import COLORS
|
|
9
|
+
from meshtensor_cli.src.meshtensor.meshtensor_interface import MeshtensorInterface
|
|
10
|
+
from meshtensor_cli.src.commands.crowd.view import show_crowdloan_details
|
|
11
|
+
from meshtensor_cli.src.meshtensor.utils import (
|
|
12
|
+
blocks_to_duration,
|
|
13
|
+
confirm_action,
|
|
14
|
+
console,
|
|
15
|
+
json_console,
|
|
16
|
+
print_extrinsic_id,
|
|
17
|
+
print_error,
|
|
18
|
+
unlock_key,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def dissolve_crowdloan(
|
|
23
|
+
meshtensor: MeshtensorInterface,
|
|
24
|
+
wallet: Wallet,
|
|
25
|
+
proxy: Optional[str],
|
|
26
|
+
crowdloan_id: int,
|
|
27
|
+
wait_for_inclusion: bool = True,
|
|
28
|
+
wait_for_finalization: bool = False,
|
|
29
|
+
prompt: bool = True,
|
|
30
|
+
decline: bool = False,
|
|
31
|
+
quiet: bool = False,
|
|
32
|
+
json_output: bool = False,
|
|
33
|
+
) -> tuple[bool, str]:
|
|
34
|
+
"""Dissolve a non-finalized crowdloan after refunding contributors.
|
|
35
|
+
|
|
36
|
+
The creator can reclaim their deposit once every other contribution has been
|
|
37
|
+
refunded (i.e., the raised amount equals the creator's contribution).
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
meshtensor: MeshtensorInterface object for chain interaction.
|
|
41
|
+
wallet: Wallet object containing the creator's coldkey.
|
|
42
|
+
proxy: Optional proxy to use for this extrinsic submission
|
|
43
|
+
crowdloan_id: ID of the crowdloan to dissolve.
|
|
44
|
+
wait_for_inclusion: Wait for transaction inclusion.
|
|
45
|
+
wait_for_finalization: Wait for transaction finalization.
|
|
46
|
+
prompt: Whether to prompt for confirmation.
|
|
47
|
+
json_output: Whether to output the results as JSON or human-readable.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
tuple[bool, str]: Success status and message.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
creator_ss58 = wallet.coldkeypub.ss58_address
|
|
54
|
+
|
|
55
|
+
crowdloan, current_block = await asyncio.gather(
|
|
56
|
+
meshtensor.get_single_crowdloan(crowdloan_id),
|
|
57
|
+
meshtensor.substrate.get_block_number(None),
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
if not crowdloan:
|
|
61
|
+
error_msg = f"Crowdloan #{crowdloan_id} not found."
|
|
62
|
+
if json_output:
|
|
63
|
+
json_console.print(json.dumps({"success": False, "error": error_msg}))
|
|
64
|
+
else:
|
|
65
|
+
print_error(f"[red]{error_msg}[/red]")
|
|
66
|
+
return False, error_msg
|
|
67
|
+
|
|
68
|
+
if crowdloan.finalized:
|
|
69
|
+
error_msg = (
|
|
70
|
+
f"Crowdloan #{crowdloan_id} is already finalized and cannot be dissolved."
|
|
71
|
+
)
|
|
72
|
+
if json_output:
|
|
73
|
+
json_console.print(json.dumps({"success": False, "error": error_msg}))
|
|
74
|
+
else:
|
|
75
|
+
print_error(f"[red]{error_msg}[/red]")
|
|
76
|
+
return False, f"Crowdloan #{crowdloan_id} is finalized."
|
|
77
|
+
|
|
78
|
+
if creator_ss58 != crowdloan.creator:
|
|
79
|
+
error_msg = f"Only the creator can dissolve this crowdloan. Creator: {crowdloan.creator}, Your address: {creator_ss58}"
|
|
80
|
+
if json_output:
|
|
81
|
+
json_console.print(json.dumps({"success": False, "error": error_msg}))
|
|
82
|
+
else:
|
|
83
|
+
print_error(
|
|
84
|
+
f"[red]Only the creator can dissolve this crowdloan.[/red]\n"
|
|
85
|
+
f"Creator: [blue]{crowdloan.creator}[/blue]\n"
|
|
86
|
+
f"Your address: [blue]{creator_ss58}[/blue]"
|
|
87
|
+
)
|
|
88
|
+
return False, "Only the creator can dissolve this crowdloan."
|
|
89
|
+
|
|
90
|
+
creator_contribution = await meshtensor.get_crowdloan_contribution(
|
|
91
|
+
crowdloan_id, crowdloan.creator
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if creator_contribution != crowdloan.raised:
|
|
95
|
+
error_msg = (
|
|
96
|
+
f"Crowdloan still holds funds from other contributors. "
|
|
97
|
+
f"Raised: {crowdloan.raised.tao}, Creator's contribution: {creator_contribution.tao}. "
|
|
98
|
+
"Run 'meshcli crowd refund' until only the creator's funds remain."
|
|
99
|
+
)
|
|
100
|
+
if json_output:
|
|
101
|
+
json_console.print(json.dumps({"success": False, "error": error_msg}))
|
|
102
|
+
else:
|
|
103
|
+
print_error(
|
|
104
|
+
f"[red]Crowdloan still holds funds from other contributors.[/red]\n"
|
|
105
|
+
f"Raised amount: [yellow]{crowdloan.raised}[/yellow]\n"
|
|
106
|
+
f"Creator's contribution: [yellow]{creator_contribution}[/yellow]\n"
|
|
107
|
+
"Run [cyan]meshcli crowd refund[/cyan] until only the creator's funds remain."
|
|
108
|
+
)
|
|
109
|
+
return False, "Crowdloan not ready to dissolve."
|
|
110
|
+
|
|
111
|
+
await show_crowdloan_details(
|
|
112
|
+
meshtensor=meshtensor,
|
|
113
|
+
crowdloan_id=crowdloan_id,
|
|
114
|
+
wallet=wallet,
|
|
115
|
+
verbose=False,
|
|
116
|
+
crowdloan=crowdloan,
|
|
117
|
+
current_block=current_block,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
summary = Table(
|
|
121
|
+
Column("Field", style=COLORS.G.SUBHEAD),
|
|
122
|
+
Column("Value", style=COLORS.G.TEMPO),
|
|
123
|
+
box=box.SIMPLE,
|
|
124
|
+
show_header=False,
|
|
125
|
+
)
|
|
126
|
+
summary.add_row("Crowdloan ID", f"#{crowdloan_id}")
|
|
127
|
+
summary.add_row("Raised", str(crowdloan.raised))
|
|
128
|
+
summary.add_row("Creator Contribution", str(creator_contribution))
|
|
129
|
+
summary.add_row(
|
|
130
|
+
"Remaining Contributors",
|
|
131
|
+
str(max(0, crowdloan.contributors_count - 1)),
|
|
132
|
+
)
|
|
133
|
+
time_remaining = crowdloan.end - current_block
|
|
134
|
+
summary.add_row(
|
|
135
|
+
"Time Remaining",
|
|
136
|
+
blocks_to_duration(time_remaining) if time_remaining > 0 else "Ended",
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
console.print("\n[bold cyan]Crowdloan Dissolution Summary[/bold cyan]")
|
|
140
|
+
console.print(summary)
|
|
141
|
+
|
|
142
|
+
if prompt and not confirm_action(
|
|
143
|
+
f"\n[bold]Proceed with dissolving crowdloan #{crowdloan_id}?[/bold]",
|
|
144
|
+
default=False,
|
|
145
|
+
decline=decline,
|
|
146
|
+
quiet=quiet,
|
|
147
|
+
):
|
|
148
|
+
if json_output:
|
|
149
|
+
json_console.print(
|
|
150
|
+
json.dumps(
|
|
151
|
+
{"success": False, "error": "Dissolution cancelled by user."}
|
|
152
|
+
)
|
|
153
|
+
)
|
|
154
|
+
else:
|
|
155
|
+
console.print("[yellow]Dissolution cancelled.[/yellow]")
|
|
156
|
+
return False, "Dissolution cancelled by user."
|
|
157
|
+
|
|
158
|
+
unlock_status = unlock_key(wallet)
|
|
159
|
+
if not unlock_status.success:
|
|
160
|
+
if json_output:
|
|
161
|
+
json_console.print(
|
|
162
|
+
json.dumps({"success": False, "error": unlock_status.message})
|
|
163
|
+
)
|
|
164
|
+
else:
|
|
165
|
+
print_error(f"[red]{unlock_status.message}[/red]")
|
|
166
|
+
return False, unlock_status.message
|
|
167
|
+
|
|
168
|
+
with console.status(
|
|
169
|
+
":satellite: Submitting dissolve transaction...", spinner="aesthetic"
|
|
170
|
+
):
|
|
171
|
+
call = await meshtensor.substrate.compose_call(
|
|
172
|
+
call_module="Crowdloan",
|
|
173
|
+
call_function="dissolve",
|
|
174
|
+
call_params={"crowdloan_id": crowdloan_id},
|
|
175
|
+
)
|
|
176
|
+
(
|
|
177
|
+
success,
|
|
178
|
+
error_message,
|
|
179
|
+
extrinsic_receipt,
|
|
180
|
+
) = await meshtensor.sign_and_send_extrinsic(
|
|
181
|
+
call=call,
|
|
182
|
+
wallet=wallet,
|
|
183
|
+
proxy=proxy,
|
|
184
|
+
wait_for_inclusion=wait_for_inclusion,
|
|
185
|
+
wait_for_finalization=wait_for_finalization,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
if not success:
|
|
189
|
+
if json_output:
|
|
190
|
+
json_console.print(
|
|
191
|
+
json.dumps(
|
|
192
|
+
{
|
|
193
|
+
"success": False,
|
|
194
|
+
"error": error_message or "Failed to dissolve crowdloan.",
|
|
195
|
+
}
|
|
196
|
+
)
|
|
197
|
+
)
|
|
198
|
+
else:
|
|
199
|
+
print_error(f"[red]Failed to dissolve crowdloan.[/red]\n{error_message}")
|
|
200
|
+
return False, error_message
|
|
201
|
+
|
|
202
|
+
if json_output:
|
|
203
|
+
extrinsic_id = await extrinsic_receipt.get_extrinsic_identifier()
|
|
204
|
+
output_dict = {
|
|
205
|
+
"success": True,
|
|
206
|
+
"error": None,
|
|
207
|
+
"extrinsic_identifier": extrinsic_id,
|
|
208
|
+
"data": {
|
|
209
|
+
"crowdloan_id": crowdloan_id,
|
|
210
|
+
"creator": crowdloan.creator,
|
|
211
|
+
"total_dissolved": creator_contribution.tao,
|
|
212
|
+
},
|
|
213
|
+
}
|
|
214
|
+
json_console.print(json.dumps(output_dict))
|
|
215
|
+
else:
|
|
216
|
+
await print_extrinsic_id(extrinsic_receipt)
|
|
217
|
+
console.print("[green]Crowdloan dissolved successfully![/green]")
|
|
218
|
+
|
|
219
|
+
return True, "Crowdloan dissolved successfully."
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from meshtensor_wallet import Wallet
|
|
6
|
+
from rich.table import Table, Column, box
|
|
7
|
+
|
|
8
|
+
from meshtensor_cli.src import COLORS
|
|
9
|
+
from meshtensor_cli.src.meshtensor.meshtensor_interface import MeshtensorInterface
|
|
10
|
+
from meshtensor_cli.src.meshtensor.utils import (
|
|
11
|
+
confirm_action,
|
|
12
|
+
console,
|
|
13
|
+
json_console,
|
|
14
|
+
print_extrinsic_id,
|
|
15
|
+
print_error,
|
|
16
|
+
unlock_key,
|
|
17
|
+
)
|
|
18
|
+
from meshtensor_cli.src.commands.crowd.view import show_crowdloan_details
|
|
19
|
+
from meshtensor_cli.src.commands.crowd.utils import get_constant
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def refund_crowdloan(
|
|
23
|
+
meshtensor: MeshtensorInterface,
|
|
24
|
+
wallet: Wallet,
|
|
25
|
+
proxy: Optional[str],
|
|
26
|
+
crowdloan_id: int,
|
|
27
|
+
wait_for_inclusion: bool = True,
|
|
28
|
+
wait_for_finalization: bool = False,
|
|
29
|
+
prompt: bool = True,
|
|
30
|
+
decline: bool = False,
|
|
31
|
+
quiet: bool = False,
|
|
32
|
+
json_output: bool = False,
|
|
33
|
+
) -> tuple[bool, str]:
|
|
34
|
+
"""Refund contributors of a non-finalized crowdloan.
|
|
35
|
+
|
|
36
|
+
This extrinsic refunds all contributors (excluding the creator) up to the
|
|
37
|
+
RefundContributorsLimit. If there are more contributors than the limit,
|
|
38
|
+
this call may need to be executed multiple times until all contributors
|
|
39
|
+
are refunded.
|
|
40
|
+
|
|
41
|
+
Anyone can call this function - it does not need to be the creator.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
meshtensor: MeshtensorInterface object for chain interaction
|
|
45
|
+
wallet: Wallet object containing coldkey (any wallet can call this)
|
|
46
|
+
proxy: Optional proxy to use for extrinsic submission
|
|
47
|
+
crowdloan_id: ID of the crowdloan to refund
|
|
48
|
+
wait_for_inclusion: Wait for transaction inclusion
|
|
49
|
+
wait_for_finalization: Wait for transaction finalization
|
|
50
|
+
prompt: Whether to prompt for confirmation
|
|
51
|
+
json_output: Whether to output as JSON or human-readable
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
tuple[bool, str]: Success status and message
|
|
55
|
+
"""
|
|
56
|
+
creator_ss58 = wallet.coldkeypub.ss58_address
|
|
57
|
+
crowdloan, current_block = await asyncio.gather(
|
|
58
|
+
meshtensor.get_single_crowdloan(crowdloan_id),
|
|
59
|
+
meshtensor.substrate.get_block_number(None),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
if not crowdloan:
|
|
63
|
+
error_msg = f"Crowdloan #{crowdloan_id} not found."
|
|
64
|
+
if json_output:
|
|
65
|
+
json_console.print(json.dumps({"success": False, "error": error_msg}))
|
|
66
|
+
else:
|
|
67
|
+
print_error(f"[red]{error_msg}[/red]")
|
|
68
|
+
return False, error_msg
|
|
69
|
+
|
|
70
|
+
if crowdloan.finalized:
|
|
71
|
+
error_msg = f"Crowdloan #{crowdloan_id} is already finalized. Finalized crowdloans cannot be refunded."
|
|
72
|
+
if json_output:
|
|
73
|
+
json_console.print(json.dumps({"success": False, "error": error_msg}))
|
|
74
|
+
else:
|
|
75
|
+
print_error(f"[red]{error_msg}[/red]")
|
|
76
|
+
return False, f"Crowdloan #{crowdloan_id} is already finalized."
|
|
77
|
+
|
|
78
|
+
if creator_ss58 != crowdloan.creator:
|
|
79
|
+
error_msg = f"Only the creator can refund this crowdloan. Creator: {crowdloan.creator}, Your address: {creator_ss58}"
|
|
80
|
+
if json_output:
|
|
81
|
+
json_console.print(json.dumps({"success": False, "error": error_msg}))
|
|
82
|
+
else:
|
|
83
|
+
print_error(
|
|
84
|
+
f"[red]Only the creator can refund this crowdloan.[/red]\n"
|
|
85
|
+
f"Creator: [blue]{crowdloan.creator}[/blue]\n"
|
|
86
|
+
f"Your address: [blue]{creator_ss58}[/blue]"
|
|
87
|
+
)
|
|
88
|
+
return False, "Only the creator can refund this crowdloan."
|
|
89
|
+
|
|
90
|
+
await show_crowdloan_details(
|
|
91
|
+
meshtensor=meshtensor,
|
|
92
|
+
crowdloan_id=crowdloan_id,
|
|
93
|
+
wallet=wallet,
|
|
94
|
+
verbose=False,
|
|
95
|
+
crowdloan=crowdloan,
|
|
96
|
+
current_block=current_block,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
refund_limit = await get_constant(meshtensor, "RefundContributorsLimit")
|
|
100
|
+
|
|
101
|
+
console.print("\n[bold cyan]Crowdloan Refund Information[/bold cyan]\n")
|
|
102
|
+
|
|
103
|
+
info_table = Table(
|
|
104
|
+
Column("[bold white]Property", style=COLORS.G.SUBHEAD),
|
|
105
|
+
Column("[bold white]Value", style=COLORS.G.TEMPO),
|
|
106
|
+
show_footer=False,
|
|
107
|
+
show_header=False,
|
|
108
|
+
width=None,
|
|
109
|
+
pad_edge=False,
|
|
110
|
+
box=box.SIMPLE,
|
|
111
|
+
show_edge=True,
|
|
112
|
+
border_style="bright_black",
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
info_table.add_row("Crowdloan ID", f"#{crowdloan_id}")
|
|
116
|
+
info_table.add_row("Total Contributors", f"{crowdloan.contributors_count:,}")
|
|
117
|
+
info_table.add_row("Refund Limit (per call)", f"{refund_limit:,} contributors")
|
|
118
|
+
info_table.add_row("Amount to Refund", crowdloan.raised - crowdloan.deposit)
|
|
119
|
+
|
|
120
|
+
if current_block >= crowdloan.end:
|
|
121
|
+
if crowdloan.raised < crowdloan.cap:
|
|
122
|
+
status = "[red]Failed[/red] (Cap not reached)"
|
|
123
|
+
else:
|
|
124
|
+
status = "[yellow]Ended but not finalized[/yellow]"
|
|
125
|
+
else:
|
|
126
|
+
status = "[green]Active[/green] (Still accepting contributions)"
|
|
127
|
+
|
|
128
|
+
info_table.add_row("Status", status)
|
|
129
|
+
|
|
130
|
+
refundable_contributors = max(0, crowdloan.contributors_count)
|
|
131
|
+
estimated_calls = (
|
|
132
|
+
(refundable_contributors + refund_limit) // refund_limit
|
|
133
|
+
if refund_limit > 0
|
|
134
|
+
else 0
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
if estimated_calls > 1:
|
|
138
|
+
info_table.add_row(
|
|
139
|
+
"Estimated Calls Needed",
|
|
140
|
+
f"[yellow]~{estimated_calls}[/yellow] (due to contributor limit)",
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
console.print(info_table)
|
|
144
|
+
|
|
145
|
+
if estimated_calls > 1:
|
|
146
|
+
console.print(
|
|
147
|
+
f"\n[yellow]Note:[/yellow] Due to the [cyan]Refund Contributors Limit[/cyan] of {refund_limit:,} contributors per call,\n"
|
|
148
|
+
f" you may need to execute this command [yellow]{estimated_calls} times[/yellow] to refund all contributors.\n"
|
|
149
|
+
f" Each call will refund up to {refund_limit:,} contributors until all are processed.\n"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
if prompt and not confirm_action(
|
|
153
|
+
f"\n[bold]Proceed with refunding contributors of Crowdloan #{crowdloan_id}?[/bold]",
|
|
154
|
+
default=False,
|
|
155
|
+
decline=decline,
|
|
156
|
+
quiet=quiet,
|
|
157
|
+
):
|
|
158
|
+
if json_output:
|
|
159
|
+
json_console.print(
|
|
160
|
+
json.dumps({"success": False, "error": "Refund cancelled by user."})
|
|
161
|
+
)
|
|
162
|
+
else:
|
|
163
|
+
console.print("[yellow]Refund cancelled.[/yellow]")
|
|
164
|
+
return False, "Refund cancelled by user."
|
|
165
|
+
|
|
166
|
+
unlock_status = unlock_key(wallet)
|
|
167
|
+
if not unlock_status.success:
|
|
168
|
+
if json_output:
|
|
169
|
+
json_console.print(
|
|
170
|
+
json.dumps({"success": False, "error": unlock_status.message})
|
|
171
|
+
)
|
|
172
|
+
else:
|
|
173
|
+
print_error(f"[red]{unlock_status.message}[/red]")
|
|
174
|
+
return False, unlock_status.message
|
|
175
|
+
|
|
176
|
+
with console.status(
|
|
177
|
+
":satellite: Submitting refund transaction...", spinner="aesthetic"
|
|
178
|
+
):
|
|
179
|
+
call = await meshtensor.substrate.compose_call(
|
|
180
|
+
call_module="Crowdloan",
|
|
181
|
+
call_function="refund",
|
|
182
|
+
call_params={
|
|
183
|
+
"crowdloan_id": crowdloan_id,
|
|
184
|
+
},
|
|
185
|
+
)
|
|
186
|
+
(
|
|
187
|
+
success,
|
|
188
|
+
error_message,
|
|
189
|
+
extrinsic_receipt,
|
|
190
|
+
) = await meshtensor.sign_and_send_extrinsic(
|
|
191
|
+
call=call,
|
|
192
|
+
wallet=wallet,
|
|
193
|
+
proxy=proxy,
|
|
194
|
+
wait_for_inclusion=wait_for_inclusion,
|
|
195
|
+
wait_for_finalization=wait_for_finalization,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
if not success:
|
|
199
|
+
if json_output:
|
|
200
|
+
json_console.print(
|
|
201
|
+
json.dumps(
|
|
202
|
+
{
|
|
203
|
+
"success": False,
|
|
204
|
+
"error": error_message or "Failed to refund contributors.",
|
|
205
|
+
}
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
else:
|
|
209
|
+
print_error(f"[red]Failed to refund contributors.[/red]\n{error_message}")
|
|
210
|
+
return False, error_message
|
|
211
|
+
|
|
212
|
+
if json_output:
|
|
213
|
+
extrinsic_id = await extrinsic_receipt.get_extrinsic_identifier()
|
|
214
|
+
output_dict = {
|
|
215
|
+
"success": True,
|
|
216
|
+
"error": None,
|
|
217
|
+
"extrinsic_identifier": extrinsic_id,
|
|
218
|
+
"data": {
|
|
219
|
+
"crowdloan_id": crowdloan_id,
|
|
220
|
+
"refund_limit_per_call": refund_limit,
|
|
221
|
+
"total_contributors": crowdloan.contributors_count,
|
|
222
|
+
"estimated_calls_remaining": max(0, estimated_calls - 1),
|
|
223
|
+
"amount_refunded": (crowdloan.raised - crowdloan.deposit).tao,
|
|
224
|
+
},
|
|
225
|
+
}
|
|
226
|
+
json_console.print(json.dumps(output_dict))
|
|
227
|
+
else:
|
|
228
|
+
console.print(
|
|
229
|
+
f"[green]Contributors have been refunded for Crowdloan #{crowdloan_id}.[/green]"
|
|
230
|
+
)
|
|
231
|
+
await print_extrinsic_id(extrinsic_receipt)
|
|
232
|
+
|
|
233
|
+
return True, f"Contributors have been refunded for Crowdloan #{crowdloan_id}."
|