Introduction

On December 1st, 2025, Aleo Foundation tasked zkSecurity with auditing their Multisig Wallet program and its integration into several applications and protocols. The specific code to review was shared via both public and private GitHub repositories. The audit lasted one week with two consultants.

Scope

The main purpose of multisig wallet currently boils down to two use cases:

  1. Requiring multiple signatures for program upgrades.
  2. Requiring multiple signatures for admin-specific functions of several different applications and protocols.

For this reason, this audit not only focused on the multisig core engine, multisig_core, but also covered how this engine is utilized by other applications and protocols to gate upgrades (e.g., in test_upgrade) and admin functions (e.g., in Hyperlane’s warp tokens).

More specifically, we reviewed the following programs:

Repo: https://github.com/AleoNet/aleo-multisig

  • The multisig_core program, which is the core multisignature engine, at commit 940050959f5877ba59f2889430f5b0e7a6a6e96f.
  • The test_upgrades program, which shows how program upgrades can be gated behind a multisig operation, at commit 940050959f5877ba59f2889430f5b0e7a6a6e96f.

Repo: https://github.com/eranrund/hyperlane-aleo/tree/eran/multisig

  • The hyp_multisig program, which can be used to gate privileged operations on Hyperlane’s mailbox, ism_manager, and hook_manager programs, at commit a978035035900d72ea68073ccb23cda73bf5a68f.
  • The hyp_warp_multisig program, which represents the interface program between multisig_core and Hyperlane’s warp token programs, at commit a978035035900d72ea68073ccb23cda73bf5a68f.

Repo: https://github.com/sealance-io/compliant-transfer-aleo

  • The compliant_token_template program, which provides a token template that includes freeze list enforcement, at commit 052bbd9d26336ac3f9cefae2c9595df7c26bcaaf.
  • The sealance_freezelist_registry program, which is the freeze list registry used by the compliant_token_template, at commit 052bbd9d26336ac3f9cefae2c9595df7c26bcaaf.

Repo: private

  • The circle_bridge program, which handles cross-chain token transfers between Ethereum and Aleo, at commit 45e0aa94d6041cd0874c0c73eec697c53decc8ed.

Scope Variance

This section outlines several changes present in the deployed commit that were not included in the original audit scope, while noting that the deployed version continues to use multisig program upgrades covered in the audit.

More specifically, the differences are as follows:

  • The same multisig_core program, which differs only by the removal of the UPGRADER_ADDRESS constant. Instead, the UPGRADER_ADDRESS is stored in a mapping and is initialized as part of the init() transition. It can later be updated via the set_upgrader_address() transition (until upgrades are disabled by disallow_upgrades()).
  • The stablecoin_program, which is equivalent to the audited compliant_token_template, but differs by removing multisig administrative actions (not to be confused with multisig-controlled program upgrades, which remain intact), and removing multisig version of mint and burn.
  • The freezelist_program, which is equivalent to the audited sealance_freezelist_registry, which differs only by the removal of multisig administrative actions.
  • The same circle_bridge program, which incorporates the same difference as above: removal of multisig administrative actions.

Although these variants were not part of the original audit scope, they rely on the same multisig upgrade flow and structural assumptions evaluated in the audited codebase.

For completeness in the report, we also reference the corresponding commits in the Provable compliant-stablecoin repository (https://github.com/ProvableHQ/compliant-stablecoin) at commit d3dda6117c47d043f12b448e6c8f912f519bddb1 and note that these versions have been reviewed at a high level as part of this variance assessment.

Overview

Multisig core

multisig_core.aleo implements a reusable t-of-n threshold multi-signature engine for Aleo programs. It manages wallets (identified by Aleo addresses), tracks authorized signers (both Aleo signature and ECDSA), enforces signature thresholds for arbitrary operations, and exposes admin transitions to adjust signer sets or thresholds. State lives in the following mappings:

  • wallets_map: stores each wallet’s threshold and signer count
  • signers_map: tracks hashed signer identities
  • pending_signing_ops: tracks signing rounds
  • completed_signing_ops: tracks completion status

Program Flow

  1. Wallet setup: Use create_wallet to register a wallet ID, threshold, and signer set. This populates wallets_map and hashes each signer into signers_map.
  2. Operation initiation: A participant calls initiate_signing_op from their Aleo account. The transition hashes (wallet_id, signing_op_id) into wallet_signing_op_id_hash, ensures no active/completed round exists, creates a new pending_signing_op struct, and, if the caller is a registered signer, records their signature as the first confirmation.
  3. Collect signatures: Each authorized signer calls sign (for Aleo signature) or sign_ecdsa (for ECDSA) with the same wallet_id and signing_op_id. The contract confirms the signer is registered, the pending op exists and hasn’t expired, the round value matches (if specified), and the signer hasn’t already signed this round. It increments confirmations, and once the count meets the current threshold, records the op in completed_signing_ops.
  4. Execution in consuming program: Program can then call assert_signing_completed(wallet_id, signing_op_id) which checks if wallet_signing_op_id_hash exist in the completed_signing_ops, before performing any sensitive action. This ensures that the specific action (bound to the signing_op_id) is gated by the multisignature approval before proceeding.

In addition to the basic flow, the program also handles administrative actions for updating wallet policy — such as adding or removing signers and increasing or decreasing the signing threshold — following the same flow as normal wallet-signing operations. This ensures that governance updates are subject to the same multisig protections as any other action.

Note that during wallet setup, the program also supports a Guarded wallet configuration, where any call to create_wallet must be pre-approved by the “guard wallet,” which is simply the multisig_core.aleo program’s own address used as the wallet_id. This special wallet can only be created by the program deployer when the Guarded wallet setting is enabled.

Multisig Program Upgrades

The multisig functionality can be used to gate program upgrades behind multisignature approvals.

It is demonstrated in the test_upgrades example below:

program test_upgrades.aleo {
    struct ChecksumEdition {
        checksum: [u8; 32],
        edition: u16,
    }

    // A helper for calculating a signing_op_id from the program's checksum and edition.
    // By deriving the signing_op_id from both we ensure that downgrades cannot take place.
    transition get_signing_op_id_for_deploy(checksum: [u8; 32], edition: u16) -> field {
        return BHP256::hash_to_field(ChecksumEdition { checksum: checksum, edition: edition });
    }

    @custom
    async constructor() {
        // Only require multisig for upgrades - initial deployment has no checks.
        if self.edition > 0u16 {
            let signing_op_id = BHP256::hash_to_field(ChecksumEdition { checksum: self.checksum, edition: self.edition });

            let wallet_signing_op_id_hash = BHP256::hash_to_field(WalletSigningOpId { wallet_id: self.address, signing_op_id: signing_op_id });
            let signing_complete = multisig_core.aleo/completed_signing_ops.contains(wallet_signing_op_id_hash);
            assert(signing_complete);
        }
    }

    transition main(public a: u32, b: u32) -> u32 {
        let c: u32 = a + b;
        return c;
    }
}

Before deploying a new program edition, maintainers derive a signing_op_id from the target edition and the bytecode checksum, and then run a multisig round using a wallet_id equal to the program’s address.

Once enough signatures have accumulated, the constructor of the upgraded program checks completed_signing_ops for the corresponding wallet_signing_op_id_hash, which binds together the program address, program edition, and its checksum. If the signing operation has not been approved, the constructor rejects the deployment.

Hyperlane Multisig

The Hyperlane Protocol

Hyperlane is a permissionless cross-chain messaging protocol that enables applications to send arbitrary messages between blockchains.

Conceptually, the protocol has two main flows:

  1. The Dispatch Flow (on the origin chain) An application calls dispatch to initiate the sending of a message from the origin chain to the destination chain.
  2. The Process Flow (on the destination chain) An off-chain relayer collects that message and calls process to submit the message on the destination chain, where it is verified and then executed by a target application.

The Hyperlane protocol has three main actors: applications, validators, and relayers. Applications are the “users” of the protocol. They are the ones initiating and receiving cross-chain messages. On the origin chain, an application’s entry point for sending a message is Hyperlane’s Mailbox. This program records the message and triggers post-dispatch hooks such as an IGP Hook to charge and account for gas on the destination, and a Merkle Tree Hook to append the message to a Merkle tree whose root will later be signed by validators. Validators watch the Mailbox, compute Merkle roots over dispatched messages, sign these roots, and make these signatures available for the relayers. An off-chain relayer can then fetch the signed Merkle root and the corresponding message, and then provide these to the process function on the destination chain’s Mailbox. During that call, the relayer will additionally pass the address of an Interchain Security Module (ISM) that specifies verification rules for the message to be considered valid (e.g., what validator quorum is required). The Mailbox then delegates verification to the ISM, and on success, invokes the destination application to apply the message.

image

Lastly, it’s important to note that the architecture of Hyperlane’s Aleo integration differs slightly from the standard Hyperlane protocol due to constraints imposed by the Leo programming language. For instance, to dispatch messages, applications on Aleo don’t call dispatch directly on the mailbox but rather on a dedicated dispatch_proxy program. However, these Aleo-specific differences are not really relevant to this audit, so we omit them here. For a more detailed account on this topic, we refer the reader to our previous audit report for Hyperlane’s Aleo integration.

The hyp_multisig Program

The hyp_multisig program represents the multisig-controlled governance layer of Hyperlane’s core programs on Aleo. More specifically, it will become the owner of the mailbox, the ism_manager, and the hook_manager. Once in place, all privileged operations on these programs are routed through hyp_multisig and are only executed if a corresponding multisig operation has been correctly initialized and fully signed via multisig_core. The high-level usage pattern is as follows:

  1. Wallet Setup After deployment, init is called to create a wallet in multisig_core with a specified threshold and signer set. The corresponding wallet_id is deterministically derived from self.address.
  2. Operation Initialization To initialize a multisig operation, an admin proposes by calling init_multisig_op and providing a signing_op_id, an expiration block, and an op::Op struct that encodes what will eventually be executed (e.g., “set the default ISM to address X”).
  3. Signing (off-program, in multisig_core) The signers sign the operation with multisig_core tracking the signatures and completion status.
  4. Execution When enough signatures have been collected, an executor can call the corresponding exec_* transition (e.g., exec_mailbox_set_default_ism) to execute the proposed multisig operation.

Using this pattern, the following Hyperlane admin actions will be gated by multisig:

Mailbox

  • set_dispatch_proxy
  • set_owner
  • set_default_ism
  • set_default_hook
  • set_required_hook

ISM Manager

  • set_domain
  • remove_domain
  • transfer_routing_ism_ownership

Hook Manager

  • set_destination_gas_config
  • remove_destination_gas_config
  • transfer_igp_ownership
  • claim

The hook manager’s claim will send accumulated fees to the hyp_multisig program. To withdraw these funds, admins can use the exec_credits_transfer_to_caller transition, provided they first initiated and signed a corresponding multisig operation.

Lastly, the hyp_multisig program has a @custom constructor which enforces that program upgrades are gated by multisig: for editions beyond the initial deployment (i.e., self.edition > 0), the constructor checks that an associated signing operation has been completed in multisig_core.

The hyp_warp_multisig Program

Hyperlane Warp Routes (HWRs) are cross-chain asset bridges that enable the transfer of tokens between chains using Hyperlane. Developers can permissionlessly deploy HWRs to move assets between chains. Hyperlane’s Aleo integration currently provides three different program templates that can be used to create HWRs. These are:

  • The hyp_native template. This can be used to interact with the native currency of Aleo, i.e., the credits program.
  • The hyp_synthetic template. This enables bridging of synthetic (wrapped) representations of external assets on Aleo. On inbound transfers, synthetic tokens are minted to mirror the origin chain asset. On outbound transfers, they are burned before dispatch.
  • The hyp_collateral template. This enables bridging of existing assets that are already registered in the token_registry program. On inbound transfers, tokens are transferred from the hyp_collateral program to the recipient. On outbound transfers, they are transferred from the sender to the hyp_collateral program before dispatch.

The hyp_warp_multisig represents the interface program between multisig_core and any one of the above warp-token programs. This way, all admin-level operations on a given warp token can be gated by multisig logic. Developers will have to deploy an instance of hyp_warp_multisig for each warp token they are deploying.

The usage of hyp_warp_multisig follows the same “Setup-OpInit-Signing-Execution” pattern as in hyp_multisig, which is why we don’t repeat it here.

The specific warp-token actions that can be multisig-gated by hyp_warp_multisig are:

  • set_custom_hook
  • set_custom_ism
  • set_owner
  • enroll_remote_router
  • unroll_remote_router

As with hyp_multisig, the constructor enforces multisig-based upgrades: for program editions beyond the initial deployment, it checks that the upgrade has been approved as a completed signing operation in multisig_core.

Compliant Token Template

This token program is a token that allows addresses to transfer tokens between accounts, supporting public to public, public to private, private to public and private to private transfers.

In addition to the privacy-preserving transfers, the program has its compliance enforced with a freeze list, where frozen accounts cannot move funds. The latest pull request has multisig supported, where a completed signing request is required in order to:

  • Upgrade the compliant token program to the next edition,
  • Update the role of a wallet or an address, given that the operating wallet has the MANAGER_ROLE,
  • Mint publicly or privately given that the operating wallet has the MINTER_ROLE,
  • Burn publicly or privately given that the operating wallet has the BURNER_ROLE,
  • Pause (or unpause) the compliant token program, which disables (resp. enables) the exchange of tokens, given that the operating wallet has the PAUSE_ROLE.

Except program upgrade, each of the other functions already had a counterpart that allows an address with appropriate roles to perform the same action.

Freeze List

The freeze list is maintained as an ordered Merkle tree of frozen addresses in sealance_freezelist_registry.leo. Multisig is enabled in the latest pull request, where the below features are implemented:

  • Upgrade the freeze list program to the next edition,
  • Update the roles of a wallet or an address, given that the operating wallet has the MANAGER_ROLE,
  • Update the block height window given that the operating wallet has the FREEZELIST_MANAGER_ROLE, and
  • Update the freeze list, given that the operating wallet has the FREEZELIST_MANAGER_ROLE.

Bridge Program

The bridge program (circle_bridge.aleo) implements the integration of Circle’s XReserve protocol, enabling minting and burning of wrapped USDC in a way consistent with Circle’s attestation model. At its core, the contract processes deposit payloads signed by Circle, verifies freeze-list requirements, ensures replay protection through a deposit nonce, and initiates mint or burn operations in the downstream bridged_usdc.aleo program, which based from Compliant Token Template.

The integration of multisig in the current audit is to gate administrative actions behind multisignature approvals, which enforces correct binding, expiry, and role authorization before applying changes.

There are three administrative operations that utilize multisig:

  • Pause bridge: Pause/unpause bridge by the pause admin role.
  • Update Circle attester: Rotate and update Circle attester key used for ECDSA verification.
  • Update role: Allows reassignment of wallet roles by the manager role.

In addition to those administrative operations, the multisig is also used to gate program upgrades as explained in the multisig program upgrades section.