Skip to Content
Supra Layer 1SupraMVMSupra Multisig Guide

Supra Multisig Guide

The Supra Multisig module control to Multisig accounts unlike traditional Multisig systems that require users to specify public keys, this Module only requires addresses as owners.

This module also supports updating owners without having to change the auth key. It offers two options for storing transaction payloads: either a full on-chain payload (providing full decentralization and transparency) or a lightweight payload hash (saving gas without sacrificing security during execution).

Key Benefits

  1. Dynamic Owner Management: Easily add or remove owners without resetting the auth key.

  2. Flexible Transaction Storage:

    • Full Payload: Maximum transparency and resilience.
    • Hash-only: Saves gas while retaining verification ability.
  3. Nonce-Based Order Enforcement: Transactions execute in order, ensuring predictable behavior.

  4. Simple Onboarding: Create Multisig accounts with a default owner and extend as needed.

Interacting with Multisig via Supra CLI

Supra offers intuitive CLI commands to interact with multisig accounts. Here’s guide for CLI Commands:

Create a new multisig account on-chain:

supra move multisig create \ --timeout-duration <TIMEOUT_DURATION> \ --num-signatures-required <NUM_SIGNATURES_REQUIRED> \ --additional-owners <ADDRESSES OF OTHER OWNER/OWNERS>

Use --additional-owners Even if you want a single threshold multisig, you must add the other owner’s/owners address to not face “MULTISIG_TRANSACTION_INSUFFICIENT_APPROVALS” ERROR while Executing Proposed Transaction.

create prints the transaction result and does not print the multisig address as a separate field. The address is the account_address of the 0x1::account::CoinRegisterEvent event in the output, and it is also the address that derive-multi-sig-address returns for the sender’s address and the sender’s account sequence number (header.sequence_number in the output). Any further transaction on the multisig account takes this address as --multisig-address.

To compute the address a multisig account will have before creating it, or to look up the address of one that an account created earlier, run derive-multi-sig-address. It works offline: it derives the address from the owner’s address and the owner’s account sequence number at the time of creation.

supra move account derive-multi-sig-address \ --owner-address <OWNER_ADDRESS> \ --sequence-number <SEQUENCE_NUMBER>

Approve a pending multisig transaction:

supra move multisig approve \ --multisig-address <MULTISIG_ADDRESS> \ --sequence-number <SEQUENCE_NUMBER>

Propose a new multisig transaction:

supra move multisig create-transaction \ --multisig-address <MULTISIG_ADDRESS> \ entry-function \ --function-id <FUNCTION_ID> \ --type-args <TYPE_ARGS> \ --args <ARGS>

The payload subcommand is entry-function, automation-task or gasless-automation-task. --function-id, --type-args, --args and --json-file are options of that subcommand, so they come after it, while --multisig-address and the transaction options come before it. --json-file <JSON_FILE> can replace --function-id, --type-args and --args.

--function-id is required unless --json-file is given. --type-args and --args can be omitted for a function with no type parameters or arguments. A --json-file must contain all three fields, function_id, type_args and args; a file without type_args or args is rejected with a missing field error.

The output of create-transaction contains a 0x1::multisig_account::CreateTransaction event. Its sequence_number field is the proposal’s sequence number, which approve, reject and verify-proposal take as --sequence-number. The first proposal on a multisig account has sequence number 1, and the proposer’s approval is already counted.

A multisig account pays for a proposed transfer from its own balance, so it must hold the SUPRA that the transfer moves before the transaction is executed. Send that SUPRA to the multisig address first. The owner that runs execute pays only the gas fee.

By default the full payload is stored on-chain. To store only its hash, add --store-hash-only before the payload subcommand:

supra move multisig create-transaction \ --multisig-address <MULTISIG_ADDRESS> \ --store-hash-only \ entry-function \ --function-id <FUNCTION_ID> \ --type-args <TYPE_ARGS> \ --args <ARGS>

Execute a multisig transaction that has its full payload stored on-chain:

supra move multisig execute --multisig-address <MULTISIG_ADDRESS> --max-gas 500

Removes a proposed multisig transaction (finalizes a rejection):

supra move multisig execute-reject --multisig-address <MULTISIG_ADDRESS> --max-gas 500

Execute a multisig transaction where only a payload hash was stored on-chain:

you will provide the full payload at execution time.
supra move multisig execute-with-payload \ --multisig-address <MULTISIG_ADDRESS> \ --max-gas 500 \ entry-function \ --function-id <FUNCTION_ID> \ --type-args <TYPE_ARGS> \ --args <ARGS>

Reject a multisig transaction:

supra move multisig reject \ --multisig-address <MULTISIG_ADDRESS> \ --sequence-number <SEQUENCE_NUMBER>

Verify that the entry function matches the on-chain transaction proposal (serialization check)

supra move multisig serialize-proposal \ entry-function \ --function-id <FUNCTION_ID> \ --type-args <TYPE_ARGS> \ --args <ARGS>

Verify that the proposed transaction (its entry function and parameters) matches the on-chain proposal:

supra move multisig verify-proposal \ --multisig-address <MULTISIG_ADDRESS> \ --sequence-number <SEQUENCE_NUMBER> \ entry-function \ --function-id <FUNCTION_ID> \ --type-args <TYPE_ARGS> \ --args <ARGS>

Transaction Flow Overview

The typical workflow when interacting with a multisig account is as follows:

  1. Creation of the Multisig Account:

    • create: Instantiates a multisig account with a single default owner.
  2. Transaction Creation:

    • create-transaction: An owner can initiate a multisig transaction. By default the entire transaction payload is stored on-chain. With --store-hash-only only the payload’s hash is stored, and the payload must then be supplied at execution with execute-with-payload.
    • A unique transaction Sequence ID is assigned to every newly proposed transaction.
  3. Transaction Voting (Approval/Rejection):

    • approve: Other owners can approve the pending transaction by submitting their approval with the transaction ID.
    • reject: Conversely, owners can reject a transaction if they do not agree with its content.
  4. Transaction Execution:

    • execute or execute_with_payload :
      • If enough approvals have accumulated, any owner can execute the transaction.
      • Depending on whether the full payload or just a hash is stored, the appropriate execute function is called.
    • Execution Details:
      • The multisig module first verifies that the payload has received the required number of signatures.
      • The executing owner pays for the gas fee.
  5. Finalizing Rejected Transactions:

    • execute_reject :
      • Once a transaction accumulates enough rejections, any owner can finalize the rejection of that transaction.

Practical Tips

  • Transaction Proposals: Depending on your cost and transparency preferences, choose between storing the full payload or just the payload hash when using create-transaction (--store-hash-only selects the hash).

  • Voting and Execution: Utilize the approve or reject sub-command with the appropriate sequence numbers. When ready, execute the transaction using either execute (if the full payload is on-chain) or execute-with-payload (if you stored only a hash).

  • Verification: Always verify the proposal details with serialize-proposal or verify-proposal prior to voting, ensuring the on-chain data aligns with your expected parameters.

Last updated on