Skip to content

Instantly share code, notes, and snippets.

@ernestognw
Created October 24, 2025 16:21
Show Gist options
  • Save ernestognw/af0e14e819a57004308bcc528b693d84 to your computer and use it in GitHub Desktop.
Save ernestognw/af0e14e819a57004308bcc528b693d84 to your computer and use it in GitHub Desktop.
function _tryDecodeInstallModules(
bytes calldata signature
)
private
pure
returns (
bool success,
uint256[] calldata moduleTypeIds,
address[] calldata modules,
bytes[] calldata initDatas,
bytes calldata installationSignature,
bytes calldata userOpSignature
)
{
// Minimum length to hold 5 offset pointers: 5 * 32 = 160 bytes (0xa0)
if (signature.length < 0xa0) {
return _emptyDecodeInstallModules();
}
uint256 moduleTypeIdsOffset = uint256(bytes32(signature[0x00:0x20]));
uint256 modulesOffset = uint256(bytes32(signature[0x20:0x40]));
uint256 initDatasOffset = uint256(bytes32(signature[0x40:0x60]));
uint256 installationSignatureOffset = uint256(bytes32(signature[0x60:0x80]));
uint256 userOpSignatureOffset = uint256(bytes32(signature[0x80:0xa0]));
// Check all offsets are within bounds (need at least 32 bytes for length)
if (
signature.length < moduleTypeIdsOffset + 0x20 ||
moduleTypeIdsOffset < 0xa0 ||
signature.length < modulesOffset + 0x20 ||
modulesOffset < 0xa0 ||
signature.length < initDatasOffset + 0x20 ||
initDatasOffset < 0xa0 ||
signature.length < installationSignatureOffset + 0x20 ||
installationSignatureOffset < 0xa0 ||
signature.length < userOpSignatureOffset + 0x20 ||
userOpSignatureOffset < 0xa0
) {
return _emptyDecodeInstallModules();
}
// Get array lengths
uint256 moduleTypeIdsLength = uint256(bytes32(signature[moduleTypeIdsOffset:moduleTypeIdsOffset + 0x20]));
uint256 modulesLength = uint256(bytes32(signature[modulesOffset:modulesOffset + 0x20]));
uint256 initDatasLength = uint256(bytes32(signature[initDatasOffset:initDatasOffset + 0x20]));
if (moduleTypeIdsLength != modulesLength || modulesLength != initDatasLength) {
return _emptyDecodeInstallModules();
}
// Check bounds for fixed-size arrays (uint256[] and address[])
if (
signature.length < moduleTypeIdsOffset + 0x20 + moduleTypeIdsLength * 0x20 ||
signature.length < modulesOffset + 0x20 + modulesLength * 0x20
) {
return _emptyDecodeInstallModules();
}
// Extract arrays directly from calldata
assembly ("memory-safe") {
moduleTypeIds.offset := add(signature.offset, add(moduleTypeIdsOffset, 0x20))
moduleTypeIds.length := moduleTypeIdsLength // Length in elements
modules.offset := add(signature.offset, add(modulesOffset, 0x20))
modules.length := modulesLength // Length in elements
}
initDatas = _extractBytesArray(signature, initDatasOffset, initDatasLength);
if (initDatas.length == 0 && initDatasLength > 0) {
return _emptyDecodeInstallModules();
}
// Get lengths of the signature data
uint256 installationSignatureLength = uint256(
bytes32(signature[installationSignatureOffset:installationSignatureOffset + 0x20])
);
uint256 userOpSignatureLength = uint256(bytes32(signature[userOpSignatureOffset:userOpSignatureOffset + 0x20]));
// Check bounds for signature data
if (
installationSignatureLength > signature.length ||
userOpSignatureLength > signature.length ||
signature.length < installationSignatureOffset + 0x20 + installationSignatureLength ||
signature.length < userOpSignatureOffset + 0x20 + userOpSignatureLength
) {
return _emptyDecodeInstallModules();
}
// Extract signature data (skip the 32-byte length prefix)
installationSignature = signature[
installationSignatureOffset + 0x20:installationSignatureOffset + 0x20 + installationSignatureLength
];
userOpSignature = signature[userOpSignatureOffset + 0x20:userOpSignatureOffset + 0x20 + userOpSignatureLength];
return (true, moduleTypeIds, modules, initDatas, installationSignature, userOpSignature);
}
/**
* @dev Validates and determines the end offset of a bytes[] array structure.
*
* This function checks that:
* 1. The array length is within bounds
* 2. All offset pointers are valid
* 3. All individual bytes elements are within bounds
*/
function _extractBytesArray(
bytes calldata data,
uint256 arrayOffset,
uint256 arrayLength
) private pure returns (bytes[] calldata result) {
// Start after the array length (32 bytes)
uint256 currentOffset = arrayOffset + 0x20;
// Check if we have enough space for all offset pointers (arrayLength * 32 bytes)
if (data.length < currentOffset + arrayLength * 0x20) return _emptyBytesArray();
// Track the maximum end position we've seen
uint256 maxEndOffset = currentOffset + arrayLength * 0x20;
// Validate each bytes element
for (uint256 i = 0; i < arrayLength; (i++, currentOffset += 0x20)) {
// Get the offset for this bytes element (relative to array start)
uint256 elementOffset = arrayOffset + uint256(bytes32(data[currentOffset:currentOffset + 0x20]));
// Check if the element offset is within bounds (need at least 32 bytes for length)
if (data.length < elementOffset + 0x20) return _emptyBytesArray();
// Get the length of this bytes element
uint256 elementLength = uint256(bytes32(data[elementOffset:elementOffset + 0x20]));
// Check if the element data is within bounds
uint256 elementEnd = elementOffset + 0x20 + elementLength;
if (data.length < elementEnd) return _emptyBytesArray();
// Update max end offset
if (elementEnd > maxEndOffset) maxEndOffset = elementEnd;
}
// Extract bytes array directly from calldata
assembly ("memory-safe") {
result.offset := add(data.offset, arrayOffset)
result.length := sub(maxEndOffset, arrayOffset)
}
return result;
}
function _emptyDecodeInstallModules()
private
pure
returns (
bool success,
uint256[] calldata moduleTypeIds,
address[] calldata modules,
bytes[] calldata initDatas,
bytes calldata installationSignature,
bytes calldata userOpSignature
)
{
return (
false,
_emptyUint256Array(),
_emptyAddressArray(),
_emptyBytesArray(),
Calldata.emptyBytes(),
Calldata.emptyBytes()
);
}
function _emptyBytesArray() private pure returns (bytes[] calldata result) {
assembly ("memory-safe") {
result.offset := 0
result.length := 0
}
}
function _emptyAddressArray() private pure returns (address[] calldata result) {
assembly ("memory-safe") {
result.offset := 0
result.length := 0
}
}
function _emptyUint256Array() private pure returns (uint256[] calldata result) {
assembly ("memory-safe") {
result.offset := 0
result.length := 0
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment