draft-ERC7579Utils.sol 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. // SPDX-License-Identifier: MIT
  2. // OpenZeppelin Contracts (last updated v5.2.0-rc.1) (account/utils/draft-ERC7579Utils.sol)
  3. pragma solidity ^0.8.20;
  4. import {Execution} from "../../interfaces/draft-IERC7579.sol";
  5. import {Packing} from "../../utils/Packing.sol";
  6. import {Address} from "../../utils/Address.sol";
  7. type Mode is bytes32;
  8. type CallType is bytes1;
  9. type ExecType is bytes1;
  10. type ModeSelector is bytes4;
  11. type ModePayload is bytes22;
  12. /**
  13. * @dev Library with common ERC-7579 utility functions.
  14. *
  15. * See https://eips.ethereum.org/EIPS/eip-7579[ERC-7579].
  16. */
  17. // slither-disable-next-line unused-state
  18. library ERC7579Utils {
  19. using Packing for *;
  20. /// @dev A single `call` execution.
  21. CallType internal constant CALLTYPE_SINGLE = CallType.wrap(0x00);
  22. /// @dev A batch of `call` executions.
  23. CallType internal constant CALLTYPE_BATCH = CallType.wrap(0x01);
  24. /// @dev A `delegatecall` execution.
  25. CallType internal constant CALLTYPE_DELEGATECALL = CallType.wrap(0xFF);
  26. /// @dev Default execution type that reverts on failure.
  27. ExecType internal constant EXECTYPE_DEFAULT = ExecType.wrap(0x00);
  28. /// @dev Execution type that does not revert on failure.
  29. ExecType internal constant EXECTYPE_TRY = ExecType.wrap(0x01);
  30. /**
  31. * @dev Emits when an {EXECTYPE_TRY} execution fails.
  32. * @param batchExecutionIndex The index of the failed call in the execution batch.
  33. * @param returndata The returned data from the failed call.
  34. */
  35. event ERC7579TryExecuteFail(uint256 batchExecutionIndex, bytes returndata);
  36. /// @dev The provided {CallType} is not supported.
  37. error ERC7579UnsupportedCallType(CallType callType);
  38. /// @dev The provided {ExecType} is not supported.
  39. error ERC7579UnsupportedExecType(ExecType execType);
  40. /// @dev The provided module doesn't match the provided module type.
  41. error ERC7579MismatchedModuleTypeId(uint256 moduleTypeId, address module);
  42. /// @dev The module is not installed.
  43. error ERC7579UninstalledModule(uint256 moduleTypeId, address module);
  44. /// @dev The module is already installed.
  45. error ERC7579AlreadyInstalledModule(uint256 moduleTypeId, address module);
  46. /// @dev The module type is not supported.
  47. error ERC7579UnsupportedModuleType(uint256 moduleTypeId);
  48. /// @dev Input calldata not properly formatted and possibly malicious.
  49. error ERC7579DecodingError();
  50. /// @dev Executes a single call.
  51. function execSingle(
  52. bytes calldata executionCalldata,
  53. ExecType execType
  54. ) internal returns (bytes[] memory returnData) {
  55. (address target, uint256 value, bytes calldata callData) = decodeSingle(executionCalldata);
  56. returnData = new bytes[](1);
  57. returnData[0] = _call(0, execType, target, value, callData);
  58. }
  59. /// @dev Executes a batch of calls.
  60. function execBatch(
  61. bytes calldata executionCalldata,
  62. ExecType execType
  63. ) internal returns (bytes[] memory returnData) {
  64. Execution[] calldata executionBatch = decodeBatch(executionCalldata);
  65. returnData = new bytes[](executionBatch.length);
  66. for (uint256 i = 0; i < executionBatch.length; ++i) {
  67. returnData[i] = _call(
  68. i,
  69. execType,
  70. executionBatch[i].target,
  71. executionBatch[i].value,
  72. executionBatch[i].callData
  73. );
  74. }
  75. }
  76. /// @dev Executes a delegate call.
  77. function execDelegateCall(
  78. bytes calldata executionCalldata,
  79. ExecType execType
  80. ) internal returns (bytes[] memory returnData) {
  81. (address target, bytes calldata callData) = decodeDelegate(executionCalldata);
  82. returnData = new bytes[](1);
  83. returnData[0] = _delegatecall(0, execType, target, callData);
  84. }
  85. /// @dev Encodes the mode with the provided parameters. See {decodeMode}.
  86. function encodeMode(
  87. CallType callType,
  88. ExecType execType,
  89. ModeSelector selector,
  90. ModePayload payload
  91. ) internal pure returns (Mode mode) {
  92. return
  93. Mode.wrap(
  94. CallType
  95. .unwrap(callType)
  96. .pack_1_1(ExecType.unwrap(execType))
  97. .pack_2_4(bytes4(0))
  98. .pack_6_4(ModeSelector.unwrap(selector))
  99. .pack_10_22(ModePayload.unwrap(payload))
  100. );
  101. }
  102. /// @dev Decodes the mode into its parameters. See {encodeMode}.
  103. function decodeMode(
  104. Mode mode
  105. ) internal pure returns (CallType callType, ExecType execType, ModeSelector selector, ModePayload payload) {
  106. return (
  107. CallType.wrap(Packing.extract_32_1(Mode.unwrap(mode), 0)),
  108. ExecType.wrap(Packing.extract_32_1(Mode.unwrap(mode), 1)),
  109. ModeSelector.wrap(Packing.extract_32_4(Mode.unwrap(mode), 6)),
  110. ModePayload.wrap(Packing.extract_32_22(Mode.unwrap(mode), 10))
  111. );
  112. }
  113. /// @dev Encodes a single call execution. See {decodeSingle}.
  114. function encodeSingle(
  115. address target,
  116. uint256 value,
  117. bytes calldata callData
  118. ) internal pure returns (bytes memory executionCalldata) {
  119. return abi.encodePacked(target, value, callData);
  120. }
  121. /// @dev Decodes a single call execution. See {encodeSingle}.
  122. function decodeSingle(
  123. bytes calldata executionCalldata
  124. ) internal pure returns (address target, uint256 value, bytes calldata callData) {
  125. target = address(bytes20(executionCalldata[0:20]));
  126. value = uint256(bytes32(executionCalldata[20:52]));
  127. callData = executionCalldata[52:];
  128. }
  129. /// @dev Encodes a delegate call execution. See {decodeDelegate}.
  130. function encodeDelegate(
  131. address target,
  132. bytes calldata callData
  133. ) internal pure returns (bytes memory executionCalldata) {
  134. return abi.encodePacked(target, callData);
  135. }
  136. /// @dev Decodes a delegate call execution. See {encodeDelegate}.
  137. function decodeDelegate(
  138. bytes calldata executionCalldata
  139. ) internal pure returns (address target, bytes calldata callData) {
  140. target = address(bytes20(executionCalldata[0:20]));
  141. callData = executionCalldata[20:];
  142. }
  143. /// @dev Encodes a batch of executions. See {decodeBatch}.
  144. function encodeBatch(Execution[] memory executionBatch) internal pure returns (bytes memory executionCalldata) {
  145. return abi.encode(executionBatch);
  146. }
  147. /// @dev Decodes a batch of executions. See {encodeBatch}.
  148. ///
  149. /// NOTE: This function runs some checks and will throw a {ERC7579DecodingError} if the input is not properly formatted.
  150. function decodeBatch(bytes calldata executionCalldata) internal pure returns (Execution[] calldata executionBatch) {
  151. unchecked {
  152. uint256 bufferLength = executionCalldata.length;
  153. // Check executionCalldata is not empty.
  154. if (bufferLength < 32) revert ERC7579DecodingError();
  155. // Get the offset of the array (pointer to the array length).
  156. uint256 arrayLengthOffset = uint256(bytes32(executionCalldata[0:32]));
  157. // The array length (at arrayLengthOffset) should be 32 bytes long. We check that this is within the
  158. // buffer bounds. Since we know bufferLength is at least 32, we can subtract with no overflow risk.
  159. if (arrayLengthOffset > bufferLength - 32) revert ERC7579DecodingError();
  160. // Get the array length. arrayLengthOffset + 32 is bounded by bufferLength so it does not overflow.
  161. uint256 arrayLength = uint256(bytes32(executionCalldata[arrayLengthOffset:arrayLengthOffset + 32]));
  162. // Check that the buffer is long enough to store the array elements as "offset pointer":
  163. // - each element of the array is an "offset pointer" to the data.
  164. // - each "offset pointer" (to an array element) takes 32 bytes.
  165. // - validity of the calldata at that location is checked when the array element is accessed, so we only
  166. // need to check that the buffer is large enough to hold the pointers.
  167. //
  168. // Since we know bufferLength is at least arrayLengthOffset + 32, we can subtract with no overflow risk.
  169. // Solidity limits length of such arrays to 2**64-1, this guarantees `arrayLength * 32` does not overflow.
  170. if (arrayLength > type(uint64).max || bufferLength - arrayLengthOffset - 32 < arrayLength * 32)
  171. revert ERC7579DecodingError();
  172. assembly ("memory-safe") {
  173. executionBatch.offset := add(add(executionCalldata.offset, arrayLengthOffset), 32)
  174. executionBatch.length := arrayLength
  175. }
  176. }
  177. }
  178. /// @dev Executes a `call` to the target with the provided {ExecType}.
  179. function _call(
  180. uint256 index,
  181. ExecType execType,
  182. address target,
  183. uint256 value,
  184. bytes calldata data
  185. ) private returns (bytes memory) {
  186. (bool success, bytes memory returndata) = target.call{value: value}(data);
  187. return _validateExecutionMode(index, execType, success, returndata);
  188. }
  189. /// @dev Executes a `delegatecall` to the target with the provided {ExecType}.
  190. function _delegatecall(
  191. uint256 index,
  192. ExecType execType,
  193. address target,
  194. bytes calldata data
  195. ) private returns (bytes memory) {
  196. (bool success, bytes memory returndata) = target.delegatecall(data);
  197. return _validateExecutionMode(index, execType, success, returndata);
  198. }
  199. /// @dev Validates the execution mode and returns the returndata.
  200. function _validateExecutionMode(
  201. uint256 index,
  202. ExecType execType,
  203. bool success,
  204. bytes memory returndata
  205. ) private returns (bytes memory) {
  206. if (execType == ERC7579Utils.EXECTYPE_DEFAULT) {
  207. Address.verifyCallResult(success, returndata);
  208. } else if (execType == ERC7579Utils.EXECTYPE_TRY) {
  209. if (!success) emit ERC7579TryExecuteFail(index, returndata);
  210. } else {
  211. revert ERC7579UnsupportedExecType(execType);
  212. }
  213. return returndata;
  214. }
  215. }
  216. // Operators
  217. using {eqCallType as ==} for CallType global;
  218. using {eqExecType as ==} for ExecType global;
  219. using {eqModeSelector as ==} for ModeSelector global;
  220. using {eqModePayload as ==} for ModePayload global;
  221. /// @dev Compares two `CallType` values for equality.
  222. function eqCallType(CallType a, CallType b) pure returns (bool) {
  223. return CallType.unwrap(a) == CallType.unwrap(b);
  224. }
  225. /// @dev Compares two `ExecType` values for equality.
  226. function eqExecType(ExecType a, ExecType b) pure returns (bool) {
  227. return ExecType.unwrap(a) == ExecType.unwrap(b);
  228. }
  229. /// @dev Compares two `ModeSelector` values for equality.
  230. function eqModeSelector(ModeSelector a, ModeSelector b) pure returns (bool) {
  231. return ModeSelector.unwrap(a) == ModeSelector.unwrap(b);
  232. }
  233. /// @dev Compares two `ModePayload` values for equality.
  234. function eqModePayload(ModePayload a, ModePayload b) pure returns (bool) {
  235. return ModePayload.unwrap(a) == ModePayload.unwrap(b);
  236. }