如何对合约发起的以太坊交易进行签名?Gnosis多签钱包场景
Great question—let’s break down how signature logic works with Gnosis MultiSigWallet, since it operates differently from standard Ethereum account transactions. The onlyWallet modifier might seem counterintuitive at first, but it’s part of the multi-signer security model: actual transaction execution is triggered by the contract itself, but only after validating that enough owners have signed off on the transaction details.
Here’s a step-by-step breakdown of how to sign and execute transactions for this contract:
1. Construct the Transaction Hash to Sign
First, you need to define the exact transaction you want the multi-sig wallet to execute, then generate a unique hash of its metadata. This hash is what each owner will sign.
Key parameters you’ll need:
destination: The target address (could be an EOA for ETH transfers, or another contract for method calls)value: Amount of ETH (in wei) to send (use0if no ETH is involved)data: Calldata for the target contract method (use0xfor plain ETH transfers)nonce: The wallet’s current transaction sequence number (fetch via the contract’snonce()method—each transaction gets a unique nonce to prevent replay attacks)
You can generate the hash either by calling the contract’s getTransactionHash method directly, or compute it locally to match the contract’s logic:
// Local hash calculation (matches the contract's getTransactionHash logic) bytes32 transactionHash = keccak256( abi.encodePacked( bytes1(0x19), bytes1(0x00), address(yourMultiSigWallet), nonce, destination, value, data // Note: Some older versions include gasPrice/gasLimit—check your deployed contract code! ) );
2. Owners Sign the Transaction Hash Offline
Each required owner uses their private key to sign the transactionHash using standard ECDSA signature. This can be done via wallet libraries (like ethers.js/web3.js) or offline tools for enhanced security:
// Example with ethers.js const signer = new ethers.Wallet(ownerPrivateKey); const signature = await signer.signMessage(ethers.utils.arrayify(transactionHash));
Each signature will be a 65-byte string (in r+s+v format, prefixed with 0x).
3. Collect Required Signatures
Gather signatures from at least required number of owners (this value is set when deploying the contract). Make sure each signature corresponds to a registered owner address in the multi-sig wallet.
4. Submit Signatures and Execute the Transaction
Once you have enough signatures, anyone (not just owners) can submit them to the contract to trigger execution. Use the executeTransaction method (or submitTransaction followed by executeTransaction depending on your contract version) with these parameters:
destinationvaluedatanoncesignatures: A concatenated bytes array of all valid signatures (strip duplicate0xprefixes when joining, e.g.,sig1 + sig2.slice(2)).
The contract will first validate:
- Each signature corresponds to a registered owner
- The number of signatures meets the
requiredthreshold - The
noncematches the wallet’s current sequence number
If all checks pass, the contract will execute the transaction (either transferring ETH or calling the target contract method) — this triggers the onlyWallet modifier to pass, since the execution is initiated by the contract itself.
Common Misconceptions to Clear Up
- Why
onlyWallet? This modifier prevents external accounts from directly calling the wallet’s core execution logic. It ensures that transactions can only be run after the contract has validated the required signatures, enforcing the multi-sig security rule. - You’re not signing a transaction—you’re signing permission. Owners don’t sign the actual on-chain transaction; they sign off on the metadata of what the wallet should do. The contract handles broadcasting the final execution once permissions are confirmed.
内容的提问来源于stack exchange,提问作者Tony Pang

