I am trying to sign a valid transaction hash (obtained from the onchain Gnosis Safe wallet itself) but when I sign the hash and then provide it to the multisig-transactions endpoint to propose the tx, the signing address that is recovered from the signature I provide does not match the address that I actually used to sign.

*note I unfortunately cannot use the SDK in this scenario

The signature is wrong because of some reason like ethersJS is prepending 'x19 Ethereum Signed Message' to the hash or because I am signing an ethers.utils.arrayify (ie bytes) version of the hexstring hash. I have tried all permutations I can think of to fix the invalid signature but I keep receiving response 422: "Signer=0xC2278C77001F5c5164ed97Ff945f6B0E6e006363 is not an owner or delegate. Current owners=[${owners}] The code in question:

const getAndSignTxHash = () => {
  // get txhash from contract
  const safe = new ethers.Contract(state.safeAddress, abiJson, signer);

  // obtain txHash
  const txHash = safe
      "0x", //state.data,
    .then((res) => {
      State.update({ txHash: res });
      // sign contractTransactionHash using private key of Gnosis Safe owner (or deployer)
      const signature = signer
        .then((sig) => {
          State.update({ signature: sig });

const postToSafeApi = () => {
  // craft transaction from state vars
  const transaction = {
    safe: state.safeAddress,
    to: state.recipient,
    value: state.value,
    data: "0x", //state.data,
    operation: state.operation,
    gasToken: state.gasToken,
    safeTxGas: state.safeTxGas,
    baseGas: state.baseGas,
    gasPrice: state.gasPrice,
    refundReceiver: state.refundReceiver,
    nonce: state.nonce, 
    contractTransactionHash: state.txHash,
    sender: state.sender,
    signature: state.signature, 
    origin: state.origin,

  const transactionsUrl = state.baseUrl + `multisig-transactions/`;
  const params = JSON.stringify(transaction);
  const proposalOptions = {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    mode: "no-cors",
    body: params,

  // post to gnosis API backend
  const proposed = asyncFetch(transactionsUrl, proposalOptions).then((res) =>

If there is a better place to ask this question, please let me know since I have been stuck for a long time on this

1 Answer 1


Constant part per signature: {(max) 64-bytes signature data}{1-byte signature type}

eth_sign signature signature type > 30 To be able to use eth_sign we need to take the parameters r, s and v from calling eth_sign and set v = v + 4

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.