0

I want to craft the calldata for a Safe transaction programmatically (from a forge script, specifically), then just copy/paste it into the UI. To do this, I'd expect to be able to either:

  1. Call execTransaction and specify each input, or
  2. Use the Transaction Builder app, but instead of building calls in the UI, just paste the encoded data for the calls.

However, it seems I am unable to do either of these in the UI. So, given raw calldata that I want to execute, what's the easiest way to queue that transaction in the UI? (I am open to queueing it programmatically instead, but that seems like more overhead that wouldn't be worth the effort)

1 Answer 1

0

Posting transactions through the API is very much worth the effort, especially if you need to generate transactions programmatically, and/or use complex operations like you imply.

First, you should know how to build a transaction on EVM networks using any web3 library of choice. You can find many examples for your chosen language and library online, so I will consider that out of scope for this question.

Once you can generate a transaction locally, you can post the details directly to the TXS
(Note: Ignore the Gas estimation section and the "Lazy way" of signing the txn. Sign the message with a valid key (signer or delegate) and leave gas at 0, which will be calculated at execute time. The core concepts in the doc are still valid.)

Posting this calldata directly to execTransaction() would require that everyone sign the transaction offline, and provide those signatures along with the transaction data. It's an option available to you, but I personally find it more convenient to use the Safe API to submit and the UI for other owners to sign.

The Transaction Builder is a slightly different beast. While typical Multicall contracts simply accept an array of calldata, the deployed MultiSend contract requires more transaction metadata and has its own rules on how transaction data should be formatted.

It's not openly and definitively documented very well as far as I can tell, but the gist is that you generate one or more "Meta Transactions" (a keccak packed object comprising of a uint8 operation (just an Enum of call or delegatecall, but since the current contract doesn't accept delegatecalls, this should always be 0), address to, uint256 value, uint256 calldata length, and bytes calldata), then concatenate them together to serve as the argument for the contract's multiSend(bytes) method.

There are several implementations that may help you get started:

For instance, if you're using Ethers.js, Safe has an entire MultiSend library.
The Safe Contracts repo has some generic TypeScript code
I was able to find a Python implementation, also in Safe's repo.

With some work, you could extract the logic to any backing library or language.

1
  • Hmm, this doesn't really answer the question. Encoding the multisend data is not very difficult, as the authors clearly explain the encoding scheme in the comments at the top of the contract. So generating the calldata I want is the easy part. But it seems there's no existing way for me to send that encoded data without using the API, which is not worth the effort to integrate in my particular case. Copy/pasting bytecode sounds gross, but really isn't too bad and there are some valid use cases :) Commented Jan 3, 2023 at 23:16

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.