/** * @dev Reads `length` bytes of storage in the currents contract * @param offset - the offset in the current contract's storage in words to start reading from * @param length - the number of words (32 bytes) of data to read * @return the bytes that were read. */ function getStorageAt(uint256 offset, uint256 length) public view returns (bytes memory) { bytes memory result = new bytes(length * 32); for (uint256 index = 0; index < length; index++) { // solhint-disable-next-line no-inline-assembly assembly { let word := sload(add(offset, index)) mstore(add(add(result, 0x20), mul(index, 0x20)), word) } } return result; }
getStorageAt 可以获取指定 slot
位置(offset)后续 length 个 word 的具体内容。
首先第 8 行申明一个 result
变量作为最终的返回字段,长度就是调用者期望的长度,然后 for 循环挨个将
slot 的变量以 32bytes 为单位从 storage 里(其实就是 slot
区域)逐个拷贝到内存区域的 result 变量里
第 12 行意思是将指定位置(offset + index)的 slot 上存的内容赋值给
word 临时变量
13 行将 word 的内容存到正确的 result 数组里
add(result, 0x20) 意思是跳过 result 的前 0x20 个字节,因为这前 0x20
个字节存的是变长数组 result 的长度值,也就是 length * 32
/** * @dev Performs a delegetecall on a targetContract in the context of self. * Internally reverts execution to avoid side effects (making it static). * * This method reverts with data equal to `abi.encode(bool(success), bytes(response))`. * Specifically, the `returndata` after a call to this method will be: * `success:bool response.length:uint256 response:bytes`. * * @param targetContract Address of the contract containing the code to execute. * @param calldataPayload Calldata that should be sent to the target contract (encoded method name and arguments). */ function simulateAndRevert(address targetContract, bytes memory calldataPayload) external { // solhint-disable-next-line no-inline-assembly assembly { let success := delegatecall(gas(), targetContract, add(calldataPayload, 0x20), mload(calldataPayload), 0, 0)
/// @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`. /// @notice Make sure to peform a bounds check for @param pos, to avoid out of bounds access on @param signatures /// @param pos which signature to read. A prior bounds check of this parameter should be performed, to avoid out of bounds access /// @param signatures concatenated rsv signatures function signatureSplit(bytes memory signatures, uint256 pos) internal pure returns ( uint8 v, bytes32 r, bytes32 s ) { // The signature format is a compact form of: // {bytes32 r}{bytes32 s}{uint8 v} // Compact means, uint8 is not padded to 32 bytes. // solhint-disable-next-line no-inline-assembly assembly { let signaturePos := mul(0x41, pos) r := mload(add(signatures, add(signaturePos, 0x20))) s := mload(add(signatures, add(signaturePos, 0x40))) // Here we are loading the last 32 bytes, including 31 bytes // of 's'. There is no 'mload8' to do this. // // 'byte' is not working due to the Solidity parser, so lets // use the second best option, 'and' v := and(mload(add(signatures, add(signaturePos, 0x41))), 0xff) } }
/// @dev Allows a Module to execute a Safe transaction without any further confirmations and return data /// @param to Destination address of module transaction. /// @param value Ether value of module transaction. /// @param data Data payload of module transaction. /// @param operation Operation type of module transaction. function execTransactionFromModuleReturnData( address to, uint256 value, bytes memory data, Enum.Operation operation ) public returns (bool success, bytes memory returnData) { success = execTransactionFromModule(to, value, data, operation); // solhint-disable-next-line no-inline-assembly assembly { // Load free memory location let ptr := mload(0x40) // We allocate memory for the return data by setting the free memory location to // current free memory location + data size + 32 bytes for data size value mstore(0x40, add(ptr, add(returndatasize(), 0x20))) // Store the size mstore(ptr, returndatasize()) // Store the data returndatacopy(add(ptr, 0x20), 0, returndatasize()) // Point the return data to the correct memory location returnData := ptr } }
/// @dev Returns array of modules. /// @param start Start of the page. /// @param pageSize Maximum number of modules that should be returned. /// @return array Array of modules. /// @return next Start of the next page. function getModulesPaginated(address start, uint256 pageSize) external view returns (address[] memory array, address next) { // Init array with max page size array = new address[](pageSize);
上面的例子只用到了一行汇编,但是用到一个小 trick,首先用 new
关键字创建的数组都是变长的动态数组,然后变长数组的内存布局里(上一遍文章
有提到),第一个 0x20 个字节存的是数组的长度。
这里就是利用了这个特点,直接修改第一个 0x20
的内容为最终的长度值,不然的话,常规做法就是要新申明一个定长的临时数组,然后通过
for 循环一一拷贝到临时数组,最后 return 这个临时数组。
但我不确定直接修改变长数组的 length
属性是否也同样可以达到这个效果
GnosisSafe
1 2 3 4 5 6 7 8 9
/// @dev Returns the chain id used by this contract. function getChainId() public view returns (uint256) { uint256 id; // solhint-disable-next-line no-inline-assembly assembly { id := chainid() } return id; }