github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/src/Safe/SafeSigners.sol (about) 1 // SPDX-License-Identifier: MIT 2 pragma solidity ^0.8.0; 3 4 library SafeSigners { 5 /// @notice Splits signature bytes into `uint8 v, bytes32 r, bytes32 s`. 6 /// Copied directly from 7 /// https://github.com/safe-global/safe-contracts/blob/e870f514ad34cd9654c72174d6d4a839e3c6639f/contracts/common/SignatureDecoder.sol 8 /// @dev Make sure to perform a bounds check for @param pos, to avoid out of bounds access on @param signatures 9 /// The signature format is a compact form of {bytes32 r}{bytes32 s}{uint8 v} 10 /// Compact means uint8 is not padded to 32 bytes. 11 /// @param pos Which signature to read. 12 /// A prior bounds check of this parameter should be performed, to avoid out of bounds access. 13 /// @param signatures Concatenated {r, s, v} signatures. 14 /// @return v Recovery ID or Safe signature type. 15 /// @return r Output value r of the signature. 16 /// @return s Output value s of the signature. 17 function signatureSplit( 18 bytes memory signatures, 19 uint256 pos 20 ) 21 internal 22 pure 23 returns (uint8 v, bytes32 r, bytes32 s) 24 { 25 assembly { 26 let signaturePos := mul(0x41, pos) 27 r := mload(add(signatures, add(signaturePos, 0x20))) 28 s := mload(add(signatures, add(signaturePos, 0x40))) 29 /** 30 * Here we are loading the last 32 bytes, including 31 bytes 31 * of 's'. There is no 'mload8' to do this. 32 * 'byte' is not working due to the Solidity parser, so lets 33 * use the second best option, 'and' 34 */ 35 v := and(mload(add(signatures, add(signaturePos, 0x41))), 0xff) 36 } 37 } 38 39 /// @notice Extract the signers from a set of signatures. 40 /// This method is based closely on the code in the Safe.checkNSignatures() method. 41 /// https://github.com/safe-global/safe-contracts/blob/e870f514ad34cd9654c72174d6d4a839e3c6639f/contracts/Safe.sol#L274 42 /// It has been modified by removing all signature _validation_ code. We trust the Safe to properly validate 43 /// the signatures. 44 /// This method therefore simply extracts the addresses from the signatures. 45 function getNSigners( 46 bytes32 dataHash, 47 bytes memory signatures, 48 uint256 requiredSignatures 49 ) 50 internal 51 pure 52 returns (address[] memory _owners) 53 { 54 _owners = new address[](requiredSignatures); 55 56 address currentOwner; 57 uint8 v; 58 bytes32 r; 59 bytes32 s; 60 uint256 i; 61 for (i = 0; i < requiredSignatures; i++) { 62 (v, r, s) = signatureSplit(signatures, i); 63 if (v == 0) { 64 // If v is 0 then it is a contract signature 65 // When handling contract signatures the address of the contract is encoded into r 66 currentOwner = address(uint160(uint256(r))); 67 } else if (v == 1) { 68 // If v is 1 then it is an approved hash 69 // When handling approved hashes the address of the approver is encoded into r 70 currentOwner = address(uint160(uint256(r))); 71 } else if (v > 30) { 72 // If v > 30 then default va (27,28) has been adjusted for eth_sign flow 73 // To support eth_sign and similar we adjust v and hash the messageHash with the Ethereum message prefix 74 // before applying ecrecover 75 currentOwner = 76 ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s); 77 } else { 78 // Default is the ecrecover flow with the provided data hash 79 // Use ecrecover with the messageHash for EOA signatures 80 currentOwner = ecrecover(dataHash, v, r, s); 81 } 82 _owners[i] = currentOwner; 83 } 84 } 85 }