If your data was in calldata (data location) you could simply use slices.
Unfortunately, slices are not available on memory arrays.
how would I be able to retrieve the string "hello"? There is no
abi.decodeWithSelector, or is there? For my use-case it would even be
sufficient to just shift the entire data by 8 bytes to the left, but
then shl(n,data) (assembly) doesn't work for reference types.
shl
doesn't care about reference or value types, it just takes a value and shifts it. Your issue is that you didn't gave it the right value : data
is a memory pointer, while you want to shift the actual data contained there.
Now you will need to do a bit more than just shifting if your data is more than 32 bytes long (which is always true with dynamic types such as string
due to the way abi encoding works).
To do so, you could use the following function, which works regardless of length, type or number of arguments :
function extractCalldata(bytes memory calldataWithSelector) internal pure returns (bytes memory) {
bytes memory calldataWithoutSelector;
require(calldataWithSelector.length >= 4);
assembly {
let totalLength := mload(calldataWithSelector)
let targetLength := sub(totalLength, 4)
calldataWithoutSelector := mload(0x40)
// Set the length of callDataWithoutSelector (initial length - 4)
mstore(calldataWithoutSelector, targetLength)
// Mark the memory space taken for callDataWithoutSelector as allocated
mstore(0x40, add(calldataWithoutSelector, add(0x20, targetLength)))
// Process first 32 bytes (we only take the last 28 bytes)
mstore(add(calldataWithoutSelector, 0x20), shl(0x20, mload(add(calldataWithSelector, 0x20))))
// Process all other data by chunks of 32 bytes
for { let i := 0x1C } lt(i, targetLength) { i := add(i, 0x20) } {
mstore(add(add(calldataWithoutSelector, 0x20), i), mload(add(add(calldataWithSelector, 0x20), add(i, 0x04))))
}
}
return calldataWithoutSelector;
}
And call it to extract the part of interest :
function example() public pure returns (string memory) {
string memory helloString = "hello";
bytes memory dataWithSelector = abi.encodeWithSelector(0x12345678, helloString);
return abi.decode(extractCalldata(dataWithSelector), (string));
}
Which outputs hello
as expected.
I hope this answers your question.