Add Account framework (#5657)

This commit is contained in:
Ernesto García
2025-06-02 08:22:57 -06:00
committed by GitHub
parent 88962fb5ab
commit 83d2a247be
38 changed files with 3086 additions and 46 deletions

View File

@ -46,10 +46,12 @@ Miscellaneous contracts and libraries containing utility functions you can use t
* {Comparators}: A library that contains comparator functions to use with the {Heap} library.
* {CAIP2}, {CAIP10}: Libraries for formatting and parsing CAIP-2 and CAIP-10 identifiers.
* {Blockhash}: A library for accessing historical block hashes beyond the standard 256 block limit utilizing EIP-2935's historical blockhash functionality.
* {Time}: A library that provides helpers for manipulating time-related objects, including a `Delay` type.
* {AbstractSigner}: Abstract contract for internal signature validation in smart contracts.
* {ERC7739}: An abstract contract to validate signatures following the rehashing scheme from `ERC7739Utils`.
* {ERC7739Utils}: Utilities library that implements a defensive rehashing mechanism to prevent replayability of smart contract signatures based on ERC-7739.
* {Time}: A library that provides helpers for manipulating time-related objects, including a `Delay` type.
* {SignerECDSA}, {SignerP256}, {SignerRSA}: Implementations of an {AbstractSigner} with specific signature validation algorithms.
* {SignerERC7702}: Implementation of {AbstractSigner} that validates signatures using the contract's own address as the signer, useful for delegated accounts following EIP-7702.
[NOTE]
====
@ -90,6 +92,14 @@ Because Solidity does not support generic types, {EnumerableMap} and {Enumerable
{{AbstractSigner}}
{{SignerECDSA}}
{{SignerP256}}
{{SignerERC7702}}
{{SignerRSA}}
== Security
{{ReentrancyGuard}}

View File

@ -0,0 +1,51 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {ECDSA} from "../cryptography/ECDSA.sol";
import {AbstractSigner} from "./AbstractSigner.sol";
/**
* @dev Implementation of {AbstractSigner} using xref:api:utils#ECDSA[ECDSA] signatures.
*
* For {Account} usage, a {_setSigner} function is provided to set the {signer} address.
* Doing so is easier for a factory, who is likely to use initializable clones of this contract.
*
* Example of usage:
*
* ```solidity
* contract MyAccountECDSA is Account, SignerECDSA, Initializable {
* function initialize(address signerAddr) public initializer {
* _setSigner(signerAddr);
* }
* }
* ```
*
* IMPORTANT: Failing to call {_setSigner} either during construction (if used standalone)
* or during initialization (if used as a clone) may leave the signer either front-runnable or unusable.
*/
abstract contract SignerECDSA is AbstractSigner {
address private _signer;
/**
* @dev Sets the signer with the address of the native signer. This function should be called during construction
* or through an initializer.
*/
function _setSigner(address signerAddr) internal {
_signer = signerAddr;
}
/// @dev Return the signer's address.
function signer() public view virtual returns (address) {
return _signer;
}
/// @inheritdoc AbstractSigner
function _rawSignatureValidation(
bytes32 hash,
bytes calldata signature
) internal view virtual override returns (bool) {
(address recovered, ECDSA.RecoverError err, ) = ECDSA.tryRecover(hash, signature);
return signer() == recovered && err == ECDSA.RecoverError.NoError;
}
}

View File

@ -0,0 +1,24 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {ECDSA} from "./ECDSA.sol";
import {AbstractSigner} from "./AbstractSigner.sol";
/**
* @dev Implementation of {AbstractSigner} for implementation for an EOA. Useful for ERC-7702 accounts.
*
* @custom:stateless
*/
abstract contract SignerERC7702 is AbstractSigner {
/**
* @dev Validates the signature using the EOA's address (i.e. `address(this)`).
*/
function _rawSignatureValidation(
bytes32 hash,
bytes calldata signature
) internal view virtual override returns (bool) {
(address recovered, ECDSA.RecoverError err, ) = ECDSA.tryRecover(hash, signature);
return address(this) == recovered && err == ECDSA.RecoverError.NoError;
}
}

View File

@ -0,0 +1,59 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {P256} from "./P256.sol";
import {AbstractSigner} from "./AbstractSigner.sol";
/**
* @dev Implementation of {AbstractSigner} using xref:api:utils#P256[P256] signatures.
*
* For {Account} usage, a {_setSigner} function is provided to set the {signer} public key.
* Doing so is easier for a factory, who is likely to use initializable clones of this contract.
*
* Example of usage:
*
* ```solidity
* contract MyAccountP256 is Account, SignerP256, Initializable {
* function initialize(bytes32 qx, bytes32 qy) public initializer {
* _setSigner(qx, qy);
* }
* }
* ```
*
* IMPORTANT: Failing to call {_setSigner} either during construction (if used standalone)
* or during initialization (if used as a clone) may leave the signer either front-runnable or unusable.
*/
abstract contract SignerP256 is AbstractSigner {
bytes32 private _qx;
bytes32 private _qy;
error SignerP256InvalidPublicKey(bytes32 qx, bytes32 qy);
/**
* @dev Sets the signer with a P256 public key. This function should be called during construction
* or through an initializer.
*/
function _setSigner(bytes32 qx, bytes32 qy) internal {
if (!P256.isValidPublicKey(qx, qy)) revert SignerP256InvalidPublicKey(qx, qy);
_qx = qx;
_qy = qy;
}
/// @dev Return the signer's P256 public key.
function signer() public view virtual returns (bytes32 qx, bytes32 qy) {
return (_qx, _qy);
}
/// @inheritdoc AbstractSigner
function _rawSignatureValidation(
bytes32 hash,
bytes calldata signature
) internal view virtual override returns (bool) {
if (signature.length < 0x40) return false;
bytes32 r = bytes32(signature[0x00:0x20]);
bytes32 s = bytes32(signature[0x20:0x40]);
(bytes32 qx, bytes32 qy) = signer();
return P256.verify(hash, r, s, qx, qy);
}
}

View File

@ -0,0 +1,60 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {RSA} from "./RSA.sol";
import {AbstractSigner} from "./AbstractSigner.sol";
/**
* @dev Implementation of {AbstractSigner} using xref:api:utils#RSA[RSA] signatures.
*
* For {Account} usage, a {_setSigner} function is provided to set the {signer} public key.
* Doing so is easier for a factory, who is likely to use initializable clones of this contract.
*
* Example of usage:
*
* ```solidity
* contract MyAccountRSA is Account, SignerRSA, Initializable {
* function initialize(bytes memory e, bytes memory n) public initializer {
* _setSigner(e, n);
* }
* }
* ```
*
* IMPORTANT: Failing to call {_setSigner} either during construction (if used standalone)
* or during initialization (if used as a clone) may leave the signer either front-runnable or unusable.
*/
abstract contract SignerRSA is AbstractSigner {
bytes private _e;
bytes private _n;
/**
* @dev Sets the signer with a RSA public key. This function should be called during construction
* or through an initializer.
*/
function _setSigner(bytes memory e, bytes memory n) internal {
_e = e;
_n = n;
}
/// @dev Return the signer's RSA public key.
function signer() public view virtual returns (bytes memory e, bytes memory n) {
return (_e, _n);
}
/**
* @dev See {AbstractSigner-_rawSignatureValidation}. Verifies a PKCSv1.5 signature by calling
* xref:api:utils.adoc#RSA-pkcs1Sha256-bytes-bytes-bytes-bytes-[RSA.pkcs1Sha256].
*
* IMPORTANT: Following the RSASSA-PKCS1-V1_5-VERIFY procedure outlined in RFC8017 (section 8.2.2), the
* provided `hash` is used as the `M` (message) and rehashed using SHA256 according to EMSA-PKCS1-v1_5
* encoding as per section 9.2 (step 1) of the RFC.
*/
function _rawSignatureValidation(
bytes32 hash,
bytes calldata signature
) internal view virtual override returns (bool) {
(bytes memory e, bytes memory n) = signer();
return RSA.pkcs1Sha256(abi.encodePacked(hash), signature, e, n);
}
}