General Concepts
Transaction Unwrapping

Transaction Unwrapping

In certain scenarios, the raw transaction data might not lend itself to be checked against the configured role permissions directly. An example are batch transactions through the MultiSend contract (opens in a new tab). Instead of scoping the data encoding the full batch of transactions, permissions should be checked individually for each call in the batch.

Transaction unwrapping can be configured as a pre-processing step to be applied for specific target addresses. The unwrapping logic is implemented in adapter contracts.

MultiSendUnwrapper

The MultiSendUnwrapper adapter contract at the official deployment address 0x93B7fCbc63ED8a3a24B59e1C3e6649D50B7427c0 can be used to support unwrapping of batch transaction through the MultiSend contract (opens in a new tab).

To enable multi-send unwrapping call the setTransactionUnwrapper function as the owner of the Roles mod:

rolesMod.setTransactionUnwrapper(
    address(0x9641d764fc13c8B624c04430C7356C1C7C8102e2)),  // offical MultiSendCallOnly deployment on mainnet
    bytes4(keccak256('multiSend(bytes)'),  // multiSend function selectot
    address(0x93B7fCbc63ED8a3a24B59e1C3e6649D50B7427c0)  // address of the MultiSendUnwrapper
)

Addresses of the official MultiSend deployments on all chains are listed at https://github.com/safe-global/safe-deployments (opens in a new tab).

Custom unwrappers

Custom transaction unwrapping logic can be implemented as contracts conforming to the ITransactionUnwrapper interface.

interface ITransactionUnwrapper {
    function unwrap(
        address to,
        uint256 value,
        bytes calldata data,
        Enum.Operation operation
    ) external view returns (UnwrappedTransaction[] memory result);
}