Signing multisig with private key from nodejs service


I am very keen to work with defender and use the multisig option (with gnosis safe) but not over the admin website of defender. In the typical workflow you initiate a multisig transaction over the defender admin website and then sign it via metamask and then execute the function. I do not want to manually to this.

What I want to accomplish:
A custom function in my smart contract should require two signatures before it can be executed.
I would like to either sign the transaction (two times) by myself (through controlling the private key) or sign it using a relayer (component of defender). These two signings/transactions should be initiated from two different servers. I would like to automate this process as well. I want to do this to increase security (to tamper/ attack this transaction you would have to have control over two servers/private keys).

My questions:

  • Is it possible to initiate a multisig transaction from a nodejs service (ethers.js/ web3.js).
  • Is it possible to sign one multisig transaction from a nodejs service.
  • Is it possible to subscribe to the first signing (event, log) and right after that sign the second part of the transaction in the nodejs service.
  • Is it possible to propagate the transaction (now signed by two private keys) to the blockchain.

How can I accomplish this.
If it is not possible and/or you have a better idea to accomplish this goal (besides from implementing such a logic by myself into my smart contract) I woud also be happy to hear about it.

Thanks a lot in advance.
Kind regards Tim

1 Like

The general answer to your question is that your specific contract function which requires multi-sig access restriction, should be restricted to a specific account, and that account needs to be yet another contract which implements multi-sig infrastructure.

For example:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/access/Ownable.sol";

contract MyContract is Ownable {
    constructor(address initialOwner) Ownable(initialOwner) {}
    function myFunction() external onlyOwner {
        // whatever you want the function to do

The owner of your contract should be a contract which implements multi-sig infrastructure, which is controlled by one or more private keys under your possession.

You can set that either when you deploy your contract, by passing the address of the multi-sig contract as the value of initialOwner, or after you deploy your contract, via function transferOwnership.

This access-control mechanism, called Ownable, is the simpler one out of of two paradigms implemented by OpenZeppelin.

The other one is called Roles, and you can use OpenZeppelin Contract Wizard in order to auto-generate your desired scheme.

Finally, an example of a contract which implements multi-sig infrastructure can be found here.

Thanks a lot. You really helped getting me on the right path.

1 Like