Case Study

Multi-Signature Wallet and Shared Transaction Approval

This case study walks through a multisig wallet architecture that keeps owner sign-off apart from whoever sends a request to the chain.

Owners approve an account action, and a relayer can then deliver it to the chain. This contract keeps those two jobs apart. It checks the collected approvals against the owner list and threshold, whoever submits the transaction.

The challenge

The sender can pick the gas and collect a configured refund without owning the account. Submitting a transaction and authorizing it are two different rights. The contract still checks the signed action. It also blocks duplicate signers and copes with a transaction whose inner call reverts.

What we built

A factory creates a minimal proxy and sets its owners and chosen threshold just once at the start. The threshold cannot be zero or larger than the owner count. Any later change needs an authorized account operation.

Owners can sign an EIP-712 hash off-chain, or they can approve a hash directly on-chain. The signing domain includes the wallet address and the chain identifier. Verification covers ordinary signatures, contract signatures and a passkey scheme wherever the chain can check it.

Optional guards inspect the owner actions. Modules add a separate automation route that does not need the ordinary owner-signature bundle.

Technical approach

Each proxy holds its own storage while it runs the shared implementation code. The storage layout is part of that compatibility contract. Removing an owner checks that the remaining owners can still meet the chosen threshold.

The owner-transaction path hashes the action together with the current nonce. It then raises the nonce and checks the signatures. Signers have to be account owners, listed in ascending address order so none of them counts twice. Contract signatures use a static call and must return the expected response. A revert or an invalid response fails verification.

Execution runs the optional guard and the gas checks, then handles the configured refund. A failed inner call does not always leave the nonce unused, because that depends on the signed gas and refund settings.

Modules carry their own optional guard. An owner-transaction guard does not automatically cover those module calls on its own. Delegatecalls can also rewrite wallet storage. The choice of modules, guards and delegatecall targets therefore decides which code receives that authority.

Project success

This multisig wallet architecture can demand distinct owner approvals while still letting someone else submit them. Each approval is tied to the wallet, chain and nonce. Automation stays an explicit authority path with its own controls. Even so, the application interface still has to explain the proposed action to the people signing it.

Technology stack

Contracts

Solidity, Ethereum Virtual Machine

Signing interfaces

EIP-712, EIP-1271, ECDSA

Account architecture

Minimal proxies, CREATE2, delegatecall

Development tooling

Hardhat, TypeScript, ethers

Verification tooling

Certora Prover

Recognize the pattern?

If one of these looks like your situation, the discovery call is where we find out how close.