bytes1
, bytes16
, bytes32
bytes[n]
are fixed length byte arrays, as opposed to bytes
that are variable length byte arrays. The storage of bytes[n]
in storage is such that it takes up a slot when it is of bytes32
and is packed in cases of bytes below 32
. It is very similar to the uint
type.
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract Yul {
bytes1 public a = 0xaa;
bytes1 public b = 0xbb;
bytes1 public c = 0xcc;
function getBytes1() public view returns (bytes32) {
assembly {
let val := sload(0x00)
mstore(0x80, val)
return(0x80, 0x20)
}
}
}
Returns
0x0000000000000000000000000000000000000000000000000000000000ccbbaa
, just like inuint
's case.
However, there is some bit of caution to be proceeded with when dealing with bytes[n]
. Take a look at these three pieces of code written using bytes4
.
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract Yul {
bytes4 public a = 0xaabbccdd;
function getBytes4() public view returns (bytes32) {
assembly {
let val := sload(0x00)
mstore(0x80, val)
return(0x80, 0x20)
}
}
}
A call to the getBytes4
function would return 0x00000000000000000000000000000000000000000000000000000000aabbccdd
, which is what we expect, according to what we have learnt.
Take a look at the second one.
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract Yul {
bytes4 public a;
function setAndReturnBytes4() public returns (bytes32) {
bytes4 f = 0xaabbccdd;
assembly {
sstore(0x00, f)
mstore(0x80, sload(0x00))
return(0x80, 0x20)
}
}
}
This will return 0xaabbccdd00000000000000000000000000000000000000000000000000000000
. You can find this by checking the Remix Console, expanding the transaction and checking the decoded output
object. This is wrong, and will set the value of a
to 0x00000000
.
And a look at the final one.
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract Yul {
bytes4 public a;
function setAndReturnBytes4() public returns (bytes32) {
assembly {
sstore(0x00, 0xaabbccdd)
mstore(0x80, sload(0x00))
return(0x80, 0x20)
}
}
}
This will return 0x00000000000000000000000000000000000000000000000000000000aabbccdd
.
Setting a bytes[n]
variable in a function will right-pad it to 32 bytes. Whereas, directly assigning the variable in a Yul block or in storage will handle it normally by left padding it.
To get a knowledge of which type of bytes[n]
does what, refer to this part of the book.
bytes16
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract Yul {
bytes16 public a;
function setAndReturnBytes16() public returns (bytes32) {
assembly {
sstore(0x00, 0x0011223344556677889900aabbccddeeff)
mstore(0x80, sload(0x00))
return(0x80, 0x20)
}
}
}
Returns
0x0000000000000000000000000000000011223344556677889900aabbccddeeff
.
bytes32
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract Yul {
bytes32 public a;
function setAndReturnBytes32() public returns (bytes32) {
assembly {
sstore(0x00, 0x003344556677889900aabbccddeeff0011223344556677889900aabbccddeeff)
mstore(0x80, sload(0x00))
return(0x80, 0x20)
}
}
}
Returns
0x003344556677889900aabbccddeeff0011223344556677889900aabbccddeeff
.