Skip to content

Commit

Permalink
add contract deploy using multisig
Browse files Browse the repository at this point in the history
  • Loading branch information
popenta committed Jan 15, 2024
1 parent 4d28027 commit f3a9d40
Show file tree
Hide file tree
Showing 10 changed files with 305 additions and 46 deletions.
60 changes: 44 additions & 16 deletions multiversx_sdk_cli/cli_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
from multiversx_sdk_cli.cosign_transaction import cosign_transaction
from multiversx_sdk_cli.dependency_checker import check_if_rust_is_installed
from multiversx_sdk_cli.docker import is_docker_installed, run_docker
from multiversx_sdk_cli.errors import DockerMissingError, NoWalletProvided
from multiversx_sdk_cli.errors import (BadUsage, DockerMissingError,
NoWalletProvided)
from multiversx_sdk_cli.interfaces import IAddress
from multiversx_sdk_cli.multisig import \
prepare_transaction_for_deploying_contract
from multiversx_sdk_cli.projects.core import get_project_paths_recursively
from multiversx_sdk_cli.projects.templates import Contract
from multiversx_sdk_cli.ux import show_message
Expand Down Expand Up @@ -85,6 +88,8 @@ def setup_parser(args: List[str], subparsers: Any) -> Any:
" - only valid if --wait-result is set")
cli_shared.add_broadcast_args(sub)
cli_shared.add_guardian_wallet_args(args, sub)
cli_shared.add_multisig_address_arg(sub)
cli_shared.add_contract_address_for_multisig_deploy(sub)

sub.set_defaults(func=deploy)

Expand Down Expand Up @@ -315,20 +320,43 @@ def deploy(args: Any):
address_computer = AddressComputer(NUMBER_OF_SHARDS)
contract_address = address_computer.compute_contract_address(deployer=sender.address, deployment_nonce=args.nonce)

tx = contract.prepare_deploy_transaction(
owner=sender,
bytecode=Path(args.bytecode),
arguments=args.arguments,
upgradeable=args.metadata_upgradeable,
readable=args.metadata_readable,
payable=args.metadata_payable,
payable_by_sc=args.metadata_payable_by_sc,
gas_limit=int(args.gas_limit),
value=int(args.value),
nonce=int(args.nonce),
version=int(args.version),
options=int(args.options),
guardian=args.guardian)
if args.multisig:
if not args.deployed_contract:
raise BadUsage("`--deployed-contract` needs to be provided when proposing a deploy action for the multisig contract")

tx = prepare_transaction_for_deploying_contract(
sender=sender,
multisig=args.multisig,
deployed_contract=args.deployed_contract,
arguments=args.arguments,
upgradeable=args.metadata_upgradeable,
readable=args.metadata_readable,
payable=args.metadata_payable,
payable_by_sc=args.metadata_payable_by_sc,
chain_id=args.chain,
value=int(args.value),
gas_limit=int(args.gas_limit),
nonce=int(args.nonce),
version=int(args.version),
options=int(args.options),
guardian=args.guardian
)
else:
tx = contract.prepare_deploy_transaction(
owner=sender,
bytecode=Path(args.bytecode),
arguments=args.arguments,
upgradeable=args.metadata_upgradeable,
readable=args.metadata_readable,
payable=args.metadata_payable,
payable_by_sc=args.metadata_payable_by_sc,
gas_limit=int(args.gas_limit),
value=int(args.value),
nonce=int(args.nonce),
version=int(args.version),
options=int(args.options),
guardian=args.guardian)

tx = _sign_guarded_tx(args, tx)

logger.info("Contract address: %s", contract_address.to_bech32())
Expand Down Expand Up @@ -436,7 +464,7 @@ def _send_or_simulate(tx: Transaction, contract_address: IAddress, args: Any):


def verify(args: Any) -> None:
contract = Address.from_bech32(args.contract)
contract = Address.new_from_bech32(args.contract)
verifier_url = args.verifier_url

packaged_src = Path(args.packaged_src).expanduser().resolve()
Expand Down
134 changes: 125 additions & 9 deletions multiversx_sdk_cli/cli_multisig.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from multiversx_sdk_core.transaction_factories.transactions_factory_config import \
TransactionsFactoryConfig

from multiversx_sdk_cli import cli_shared, utils
from multiversx_sdk_cli import cli_shared
from multiversx_sdk_cli.contracts import SmartContract
from multiversx_sdk_cli.errors import BadUsage
from multiversx_sdk_cli.multisig import \
Expand All @@ -16,7 +16,9 @@
logger = logging.getLogger("cli.multisig")

MULTISIG_SIGN_ACTION_FUNCTION = "sign"
MULTISIG_UNSIGN_ACTION_FUNCTION = "unsign"
MULTISIG_PERFORM_ACTION_FUNCTION = "performAction"
MULTISIG_DISCARD_ACTION_FUNCTION = "discardAction"


def setup_parser(args: List[str], subparsers: Any) -> Any:
Expand All @@ -26,7 +28,7 @@ def setup_parser(args: List[str], subparsers: Any) -> Any:
sub = cli_shared.add_command_subparser(subparsers, "multisig", "sign", f"Sign a proposed action.")
cli_shared.add_multisig_address_arg(sub)
cli_shared.add_multisig_view_address_arg(sub)
cli_shared.add_sign_multisig_action_arg(sub)
cli_shared.add_multisig_action_arg(sub)
cli_shared.add_wallet_args(args, sub)
cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False, with_guardian=True)

Expand All @@ -40,10 +42,27 @@ def setup_parser(args: List[str], subparsers: Any) -> Any:
" - only valid if --wait-result is set")
sub.set_defaults(func=sign_action)

sub = cli_shared.add_command_subparser(subparsers, "multisig", "unsign", f"Unsign a previously signed proposed action.")
cli_shared.add_multisig_address_arg(sub)
cli_shared.add_multisig_view_address_arg(sub)
cli_shared.add_multisig_action_arg(sub)
cli_shared.add_wallet_args(args, sub)
cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False, with_guardian=True)

cli_shared.add_outfile_arg(sub, what="signed transaction, hash")
cli_shared.add_broadcast_args(sub, relay=True)
cli_shared.add_proxy_arg(sub)
cli_shared.add_guardian_wallet_args(args, sub)
sub.add_argument("--wait-result", action="store_true", default=False,
help="signal to wait for the transaction result - only valid if --send is set")
sub.add_argument("--timeout", default=100, help="max num of seconds to wait for result"
" - only valid if --wait-result is set")
sub.set_defaults(func=unsign_action)

sub = cli_shared.add_command_subparser(subparsers, "multisig", "perform-action", f"Perform an action that has reached quorum.")
cli_shared.add_multisig_address_arg(sub)
cli_shared.add_multisig_view_address_arg(sub)
cli_shared.add_sign_multisig_action_arg(sub)
cli_shared.add_multisig_action_arg(sub)
cli_shared.add_wallet_args(args, sub)
cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False, with_guardian=True)

Expand All @@ -57,6 +76,23 @@ def setup_parser(args: List[str], subparsers: Any) -> Any:
" - only valid if --wait-result is set")
sub.set_defaults(func=perform_action)

sub = cli_shared.add_command_subparser(subparsers, "multisig", "discard-action", f"Discard a proposed action.")
cli_shared.add_multisig_address_arg(sub)
cli_shared.add_multisig_view_address_arg(sub)
cli_shared.add_multisig_action_arg(sub)
cli_shared.add_wallet_args(args, sub)
cli_shared.add_tx_args(args, sub, with_receiver=False, with_data=False, with_guardian=True)

cli_shared.add_outfile_arg(sub, what="signed transaction, hash")
cli_shared.add_broadcast_args(sub, relay=True)
cli_shared.add_proxy_arg(sub)
cli_shared.add_guardian_wallet_args(args, sub)
sub.add_argument("--wait-result", action="store_true", default=False,
help="signal to wait for the transaction result - only valid if --send is set")
sub.add_argument("--timeout", default=100, help="max num of seconds to wait for result"
" - only valid if --wait-result is set")
sub.set_defaults(func=discard_action)

sub = cli_shared.add_command_subparser(subparsers, "multisig", "deposit", f"Deposit assets into the multisig contract.")
cli_shared.add_multisig_address_arg(sub)
cli_shared.add_multisig_view_address_arg(sub)
Expand All @@ -79,8 +115,6 @@ def setup_parser(args: List[str], subparsers: Any) -> Any:


def sign_action(args: Any):
args = utils.as_object(args)

cli_shared.check_guardian_and_options_args(args)
cli_shared.check_broadcast_args(args)
cli_shared.prepare_chain_id_in_args(args)
Expand All @@ -100,7 +134,7 @@ def sign_action(args: Any):
caller=sender,
contract=contract_address,
function=MULTISIG_SIGN_ACTION_FUNCTION,
arguments=args.action_id,
arguments=[args.action_id],
gas_limit=int(args.gas_limit),
value=int(args.value),
transfers=None,
Expand All @@ -109,16 +143,56 @@ def sign_action(args: Any):
options=int(args.options),
guardian=args.guardian)

if tx.guardian:
tx = sign_tx_by_guardian(args, tx)

if hasattr(args, "relay") and args.relay:
args.outfile.write(compute_relayed_v1_data(tx))
return

cli_shared.send_or_simulate(tx, args)


def perform_action(args: Any):
args = utils.as_object(args)
def unsign_action(args: Any):
cli_shared.check_guardian_and_options_args(args)
cli_shared.check_broadcast_args(args)
cli_shared.prepare_chain_id_in_args(args)
cli_shared.prepare_nonce_in_args(args)

config = TransactionsFactoryConfig(args.chain)
contract = SmartContract(config)

sender = cli_shared.prepare_account(args)
contract_address = Address.new_from_bech32(args.multisig)

action_id = args.action_id
if action_id == "all":
raise BadUsage("`all` is not supported at the moment. Please use a specific action id")

tx = contract.prepare_execute_transaction(
caller=sender,
contract=contract_address,
function=MULTISIG_UNSIGN_ACTION_FUNCTION,
arguments=[args.action_id],
gas_limit=int(args.gas_limit),
value=int(args.value),
transfers=None,
nonce=int(args.nonce),
version=int(args.version),
options=int(args.options),
guardian=args.guardian)

if tx.guardian:
tx = sign_tx_by_guardian(args, tx)

if hasattr(args, "relay") and args.relay:
args.outfile.write(compute_relayed_v1_data(tx))
return

cli_shared.send_or_simulate(tx, args)


def perform_action(args: Any):
cli_shared.check_guardian_and_options_args(args)
cli_shared.check_broadcast_args(args)
cli_shared.prepare_chain_id_in_args(args)
Expand All @@ -138,7 +212,46 @@ def perform_action(args: Any):
caller=sender,
contract=contract_address,
function=MULTISIG_PERFORM_ACTION_FUNCTION,
arguments=args.action_id,
arguments=[args.action_id],
gas_limit=int(args.gas_limit),
value=int(args.value),
transfers=None,
nonce=int(args.nonce),
version=int(args.version),
options=int(args.options),
guardian=args.guardian)

if tx.guardian:
tx = sign_tx_by_guardian(args, tx)

if hasattr(args, "relay") and args.relay:
args.outfile.write(compute_relayed_v1_data(tx))
return

cli_shared.send_or_simulate(tx, args)


def discard_action(args: Any):
cli_shared.check_guardian_and_options_args(args)
cli_shared.check_broadcast_args(args)
cli_shared.prepare_chain_id_in_args(args)
cli_shared.prepare_nonce_in_args(args)

config = TransactionsFactoryConfig(args.chain)
contract = SmartContract(config)

sender = cli_shared.prepare_account(args)
contract_address = Address.new_from_bech32(args.multisig)

action_id = args.action_id
if action_id == "all":
raise BadUsage("`all` is not supported at the moment. Please use a specific action id")

tx = contract.prepare_execute_transaction(
caller=sender,
contract=contract_address,
function=MULTISIG_DISCARD_ACTION_FUNCTION,
arguments=[args.action_id],
gas_limit=int(args.gas_limit),
value=int(args.value),
transfers=None,
Expand All @@ -147,6 +260,9 @@ def perform_action(args: Any):
options=int(args.options),
guardian=args.guardian)

if tx.guardian:
tx = sign_tx_by_guardian(args, tx)

if hasattr(args, "relay") and args.relay:
args.outfile.write(compute_relayed_v1_data(tx))
return
Expand Down
8 changes: 6 additions & 2 deletions multiversx_sdk_cli/cli_shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,16 @@ def add_multisig_address_arg(sub: Any):
sub.add_argument("--multisig", help="the address of the multisig contract")


def add_contract_address_for_multisig_deploy(sub: Any):
sub.add_argument("--deployed-contract", help="the address of the already deployed contract to be deployed by the multisig")


def add_multisig_view_address_arg(sub: Any):
sub.add_argument("--multisig-view", help="the address of the multisig-view contract")


def add_sign_multisig_action_arg(sub: Any):
sub.add_argument("--action-id", help="an integer representing the ID of the action; cand also be `all`")
def add_multisig_action_arg(sub: Any):
sub.add_argument("--action-id", help="an integer representing the ID of the action; can also be `all`")


def parse_omit_fields_arg(args: Any) -> List[str]:
Expand Down
2 changes: 1 addition & 1 deletion multiversx_sdk_cli/contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ def _to_hex(arg: str):
if arg.isnumeric():
return _prepare_decimal(arg)
elif arg.startswith(DEFAULT_HRP):
addr = Address.from_bech32(arg)
addr = Address.new_from_bech32(arg)
return _prepare_hexadecimal(f"{HEX_PREFIX}{addr.hex()}")
elif arg.lower() == FALSE_STR_LOWER or arg.lower() == TRUE_STR_LOWER:
as_str = f"{HEX_PREFIX}01" if arg.lower() == TRUE_STR_LOWER else f"{HEX_PREFIX}00"
Expand Down
4 changes: 2 additions & 2 deletions multiversx_sdk_cli/dns.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ def resolve(name: str, proxy: INetworkProvider) -> Address:

result = query_contract(dns_address, proxy, "resolve", [name_arg])
if len(result) == 0:
return Address.from_bech32(ADDRESS_ZERO_BECH32)
return Address.from_hex(result[0].hex, DEFAULT_HRP)
return Address.new_from_bech32(ADDRESS_ZERO_BECH32)
return Address.new_from_hex(result[0].hex, DEFAULT_HRP)


def validate_name(name: str, shard_id: int, proxy: INetworkProvider):
Expand Down
Loading

0 comments on commit f3a9d40

Please sign in to comment.