1

I followed the steps on the answer for Deploying contracts from safe wallet and I'm able to use protocol-kit to create, sign and execute transactions. This will work if I want to perform signing using these scripts.

However, I can't broadcast these transactions to the other wallets so they can sign this from the UI.

I believe we have to use the ApiKit for this. Here's what I'm using as script


  const apiKit = new SafeApiKit({
    chainId: 47646289n,
    txServiceUrl: "http://localhost:8000/txs/api", // http:localhost:8000/txs don't work
  });

  const txData = createDeployContractTransaction(BYTECODE, 0n);
  const tx = await protocolKitOwner.createTransaction({
    transactions: [
      {
        to: CREATECALL_CONTRACT_ADDRESS,
        data: txData,
        value: 0n,
        operation: 1,
      },
    ],
  });
  const txHash = await protocolKitOwner.getTransactionHash(tx);
  const txResponse = await protocolKitOwner.approveTransactionHash(txHash);

  const safeTxHash = await protocolKitOwner.getTransactionHash(
    tx
  );
  const signature = await protocolKitOwner.signHash(safeTxHash);
  
  await apiKit.proposeTransaction({
    safeAddress: SAFE_ADDRESS,
    safeTransactionData: txData, // Contract creation byte code for the contract to deploy
    safeTxHash,
    senderSignature: signature.data,
    senderAddress: SIGNER_ADDRESS
  });
  // await protocolKitOwner.signTransaction({transaction: tx})
  // await protocolKitOwner.executeTransaction(tx) // Successfully executes if Safe has a single owner

The errors I get on proposeTransaction line are

Error: Unprocessable Entity
    at sendRequest (/Users/arpitkarnatak/Desktop/personal/node-pg/node_modules/@safe-global/api-kit/dist/src/utils/httpRequests.js:56:11)

2 Answers 2

1

Thanks again for your question.

I could not verify your code 100%, as I don't know what your createDeployContractTransaction function is doing.

But you had some small errors in which data you were handing over to the API Kit.

Here is a corrected and commented code:

const txData = createDeployContractTransaction(BYTECODE, 0n);

// you are creating a SafeTransaction correctly here:
const tx = await protocolKitOwner.createTransaction({
  transactions: [
    {
      to: CREATECALL_CONTRACT_ADDRESS,
      data: txData,
      value: '0', // this should be a string
      operation: 1,
    },
  ],
});
})

const txHash = await protocolKitOwner.getTransactionHash(tx)

// you don't need this call here, so you can remove it.
// (Approving transactions with a hash is a different method)
const txResponse = await protocolKitOwner.approveTransactionHash(txHash)

const safeTxHash = await protocolKitOwner.getTransactionHash(tx)
const signature = await protocolKitOwner.signHash(safeTxHash)

await apiKit.proposeTransaction({
  safeAddress: SAFE_ADDRESS,
  // The next line is where you were handing over the wrong data
  // You have to pass the SafeTransactionData object of the SafeTransaction you created
  safeTransactionData: tx.data, 
  safeTxHash,
  senderSignature: signature.data,
  senderAddress: SIGNER_ADDRESS
})

I have to admit that our documentation around these functions is not the best, so we will improve this for the future.

1
  • Thank you Valentin, this is exactly what I did wrong. Appreciate it Commented Aug 30 at 19:42
2

Try using the protocolOwnerKit's signer address instead of a random SIGNER_ADDRESS if any in proposeTransaction() function.

Furthermore, I have a working code for the your usecase which I built few weeks ago. Hope that helps you.

Here's the gist - https://gist.github.com/pankajjagtapp/db3e30e5ce0028b285177491c42b5eed

2
  • 1
    Thanks for jumping in here, Pankaj! Commented Aug 30 at 17:46
  • 1
    Thanks Pankaj, I was using the encodedData instead of the safeTransaction.data in proposeTransaction. Commented Aug 30 at 19:23

Your Answer

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

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