-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
use credential ID to track signers in Account contract #19
base: main
Are you sure you want to change the base?
Conversation
WalkthroughThe changes encompass several updates across configuration, smart contracts, and testing modules. A new entry is added to Changes
Sequence Diagram(s)sequenceDiagram
participant U as User
participant AF as AccountFactory
participant A as Account Contract
U->>AF: deployAccount(credentialId, publicKey, registry)
AF->>AF: Compute salt from parameters
AF->>A: Deploy Account using CREATE2 opcode
A-->>AF: Return deployed account address
AF->>U: Return account address and emit AccountDeployed event
sequenceDiagram
participant U as User
participant AR as AccountRegistry
participant AF as AccountFactory
participant A as Account Contract
U->>AR: createUser(keyId, publicKey)
AR->>AF: Call deployAccount(credentialId, publicKey, registry)
AF->>A: Deploy new Account contract
A-->>AF: Return account address
AR->>AR: Store user & key information, emit UserCreated event
AR-->>U: Return userId and account address
Poem
Tip ⚡🧪 Multi-step agentic review comment chat (experimental)
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🔭 Outside diff range comments (1)
packages/contracts/ignition/modules/P256Precompile.ts (1)
2-2
:⚠️ Potential issueRemove stray numeral at the end of the file.
The extra line containing just the numeral "2" appears to be an accidental artifact. This could lead to a syntax error in TypeScript since it is interpreted as an extraneous statement following the export. Please remove this line to ensure the file contains only the intended export.
🧹 Nitpick comments (21)
packages/contracts/ignition/modules/Testing.ts (1)
1-7
: Consider adding documentation for the Testing moduleThe module structure follows Hardhat Ignition patterns correctly, but lacks documentation explaining its purpose and how it relates to the credential ID tracking feature mentioned in the PR title.
import { buildModule } from '@nomicfoundation/hardhat-ignition/modules'; +/** + * Testing module for deploying test contracts + * Used for testing account functionality including credential ID tracking + */ export default buildModule('Testing', (m) => { const testContract = m.contract('TestContract'); return { testContract }; });packages/contracts/.cursorrules (1)
21-21
: Consider allowing minimal inline code comments for clarity.Line 21's restriction on adding comments (beyond NatSpec) could hamper maintainability when dealing with complex logic. A minimal approach aligning with readability is recommended.
packages/contracts/contracts/P256.sol (1)
31-33
: Userequire(success, "Precompile call failed")
for clarity.While the call to the precompile is expected to succeed,
require
is often safer thanassert
to give a clear error message if unexpected failures occur.packages/contracts/ignition/modules/Giano.ts (1)
8-13
: Consider awaiting the precompile deployment.Using
void
to ignore the Promise might lead to race conditions if subsequent operations depend on the precompile being deployed first. Consider making this an awaited call or ensuring proper sequencing.packages/contracts/contracts/WebAuthn.sol (2)
53-59
: Clarify partial spec coverage
The doc block forcheckAuthFlags
is thorough. For completeness, consider reiterating in the doc that some other WebAuthn checks (e.g., attestation validations) lie outside this function’s scope.
91-115
: Verbose signing verification documentation
The expanded comments forverifySignature
clearly indicate which portions of the WebAuthn flow are covered. You might wish to add a brief reminder that certain aspects (e.g., attestation or advanced hashing checks) are not performed.packages/contracts/contracts/AbstractAccountFactory.sol (1)
14-22
: Added function doc
Great explanations for the parameters and return value indeployAccount
. Consider specifying whether or not the caller must have special permissions to invoke this function (access control).packages/contracts/test/utils.ts (2)
49-57
: Avoid suppressing TypeScript checks
//@ts-ignore
might hide potential typing issues and hamper future maintainability. Consider defining the proper interface or type for these properties to prevent ignoring checks.- //@ts-ignore clientDataJSON: clientDataJSON, - //@ts-ignore authenticatorData: authenticatorData, - //@ts-ignore signature: signature,
59-82
: New event extraction utility
This function effectively parses logs for a specified event. However, you may want to filter by the contract’s address to avoid parsing logs from other contracts in the receipt, which can lead to unexpected outcomes.+ // Optionally, filter by log.address == contract.address before parsing
packages/contracts/test/AccountRegistry.test.ts (3)
9-13
: Add negative test for an uninitialized factory.
Currently, the test verifies that theAccountRegistry
initializes with the correct factory address but does not verify behavior when the factory is absent or incorrectly deployed. Consider adding a test to ensure the contract reverts or behaves as expected if the factory address is invalid.
17-38
: Enhance test coverage by checking for non-zero userID.
You're already validating that the account is non-zero. As an additional safeguard, you could confirm that the emitteduserId
is non-zero to reinforce that user creation is properly registered.
79-99
: Check for credentialId in UserCreated event assertions.
This test verifies public key fields but does not assert that thecredentialId
matches what's passed tocreateUser
. Including a check for thecredentialId
in theUserCreated
event would further strengthen the test accuracy.packages/contracts/contracts/AccountFactory.sol (1)
31-41
: Consider restricting public access or clarifying its intent.
ThedeployAccount
function is markedpublic
, allowing anyone to deploy accounts. This might be intentional if you want it to be fully permissionless. If not, consider adding an access control modifier or clarifying in documentation that this is by design.packages/contracts/test/helpers/testSetup.ts (1)
39-67
: Consider adding error handling around deployment.
ThedeployContracts
function wraps calls tohre.ignition.deploy(ignitionModules)
without catching potential failures. While failures may be rare in local testing, adding a try/catch or expanded error reporting can make it easier to debug if deployment fails unexpectedly.packages/contracts/contracts/test/TestContract.sol (3)
25-28
: Consider adding access control forsetValue
.Currently, any address can call
setValue
, which might not be an issue for a testing scenario. However, if you plan to reuse this contract in more controlled environments, consider restricting who can updatevalue
.
52-57
: Reentrancy consideration inwithdraw
.Using
call{value: _amount}("")
can introduce reentrancy vectors if internal state updates or additional logic are added later. Although this is primarily a test contract, it’s safer to consider using a nonReentrant mechanism (e.g., OpenZeppelin’s ReentrancyGuard) or the checks-effects-interactions pattern.
92-97
: Use caution when relying onreceive()
.The
receive()
fallback to emitEtherReceived
is a convenient way to capture Ether transfers. Just ensure that the contract does not perform any heavyweight logic here, to guard against issues if the contract is used in scenarios with multiple or frequent ETH sends.packages/contracts/contracts/AccountRegistry.sol (2)
125-131
: Non-sequential ID generation logic.Using
block.timestamp
,block.prevrandao
,address(this)
, andmsg.sender
is a practical approach for uniqueness under typical conditions. However, these on-chain values don't guarantee strong randomness properties. If strict uniqueness or randomness is mission-critical, consider additional off-chain or VRF-based randomness.
278-299
: Key removal consistency innotifyKeyRemoved
.Unlinking the key from
keyToAccount
ensures that the system remains consistent. Just confirm that child contracts or external references don’t rely on the old key mapping after removal. The usage of the eventKeyUnlinked
also helps external systems track updates.packages/contracts/contracts/Account.sol (1)
495-517
: Potential partial cleanup inremoveKey
.After
removeKey
sets the key’s role toRole.NONE
, theKeyInfo
struct remains in thekeys
mapping. If you want to free up storage or ensure no residual data (publicKey, etc.) remains, consider deletingkeys[credentialId]
entirely once you no longer need that data.- keys[credentialId].role = Role.NONE; + delete keys[credentialId];packages/contracts/ignition/modules/P256Precompile.ts (1)
1-1
: Add file-level documentation for clarity.
Consider adding a brief comment at the top of the file describing the purpose of this exported hexadecimal string. For example, explain that the string represents the precompiled bytecode for the P256 precompile used in the Account contract deployment process. This will improve maintainability and ease the onboarding of future contributors.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (29)
.gitignore
(1 hunks)packages/common/src/encoding/encodeChallenge.ts
(1 hunks)packages/contracts/.cursorignore
(1 hunks)packages/contracts/.cursorrules
(1 hunks)packages/contracts/.mocharc.json
(1 hunks)packages/contracts/contracts/AbstractAccountFactory.sol
(1 hunks)packages/contracts/contracts/Account.sol
(2 hunks)packages/contracts/contracts/AccountFactory.sol
(1 hunks)packages/contracts/contracts/AccountRegistry.sol
(1 hunks)packages/contracts/contracts/Base64.sol
(1 hunks)packages/contracts/contracts/ERC721SafeTransferAccount.sol
(0 hunks)packages/contracts/contracts/ERC721SafeTransferAccountFactory.sol
(0 hunks)packages/contracts/contracts/P256.sol
(2 hunks)packages/contracts/contracts/Types.sol
(1 hunks)packages/contracts/contracts/WebAuthn.sol
(3 hunks)packages/contracts/contracts/test/TestContract.sol
(1 hunks)packages/contracts/hardhat.config.ts
(0 hunks)packages/contracts/ignition/modules/ERC721SafeTransferAccount.ts
(0 hunks)packages/contracts/ignition/modules/Giano.ts
(1 hunks)packages/contracts/ignition/modules/P256Precompile.ts
(1 hunks)packages/contracts/ignition/modules/Testing.ts
(1 hunks)packages/contracts/ignition/modules/index.ts
(1 hunks)packages/contracts/package.json
(1 hunks)packages/contracts/test/Account.ts
(0 hunks)packages/contracts/test/AccountFactory.ts
(0 hunks)packages/contracts/test/AccountRegistry.test.ts
(1 hunks)packages/contracts/test/ERC721SafeTransferAccount.ts
(0 hunks)packages/contracts/test/helpers/testSetup.ts
(1 hunks)packages/contracts/test/utils.ts
(3 hunks)
💤 Files with no reviewable changes (7)
- packages/contracts/hardhat.config.ts
- packages/contracts/contracts/ERC721SafeTransferAccountFactory.sol
- packages/contracts/test/ERC721SafeTransferAccount.ts
- packages/contracts/test/AccountFactory.ts
- packages/contracts/ignition/modules/ERC721SafeTransferAccount.ts
- packages/contracts/test/Account.ts
- packages/contracts/contracts/ERC721SafeTransferAccount.sol
🔇 Additional comments (37)
.gitignore (1)
5-5
: Appropriate .pnpm-store Ignored EntryThe addition of
.pnpm-store/
is a good practice when using pnpm, ensuring that cached dependency data isn’t tracked in version control. This helps maintain a clean repository.packages/contracts/.cursorignore (1)
1-4
: Appropriate directory exclusions addedThese directories contain generated files, dependencies, and cache data that should be excluded from the cursor tool's processing. This improves editor performance and prevents unnecessary processing of non-source files.
packages/contracts/.mocharc.json (1)
1-7
: Well-configured Mocha test settingsThis configuration properly sets up Hardhat integration with Mocha and includes a reasonable timeout value (40s) for blockchain tests that may involve complex transactions. The test pattern correctly targets TypeScript tests.
packages/contracts/package.json (1)
42-42
: Good practice keeping Mocha updatedUpdating from 10.2.0 to 10.7.3 brings in bug fixes and improvements to the testing framework. This is a positive maintenance change that supports the new Mocha configuration file.
packages/contracts/ignition/modules/index.ts (1)
1-10
: Clean modular structure for contract deploymentThis new file creates a modular structure to organize Hardhat Ignition deployment configurations. It effectively combines the Giano and Testing modules into a single export, making it easier to manage deployment configurations across different environments.
The pattern follows best practices by:
- Using the Hardhat Ignition
buildModule
function- Properly importing and composing sub-modules
- Using object spread to merge module exports cleanly
packages/common/src/encoding/encodeChallenge.ts (4)
3-3
: Appropriate import for new parameter typeThe addition of
BytesLike
import from ethers is necessary to support the new credential ID parameter.
6-6
: Function signature updated to include credential IDThe function signature has been updated to accept a credential ID parameter, which aligns with the PR objective of tracking signers by their credential IDs.
13-15
: Updated tuple encoding structure to include credential IDThe ABI encoding structure has been appropriately modified to include the credential ID as part of the tuple type.
17-18
: Added credential ID to encoded dataThe credential ID is now included as the first element in the encoded data array, ensuring it's captured in the WebAuthn signature challenge.
packages/contracts/contracts/Types.sol (6)
4-9
: Improved documentation for Types libraryThe enhanced documentation clearly explains the purpose and usage of the Types library, which improves code readability and maintainability.
11-20
: Well-documented Signature structThe detailed documentation for each field in the Signature struct makes it clear what each parameter represents and how it's used in WebAuthn signatures.
22-22
: New credential ID field added to Signature structThe addition of the
credentialId
field to the Signature struct is consistent with the changes inencodeChallenge.ts
and supports the PR's objective of tracking signers by their credential IDs.
31-41
: Well-structured Call struct for contract interactionsThe new Call struct is properly documented and provides a clean way to represent external contract calls with target address, value, and calldata.
43-51
: SignedCall struct for authorized transactionsThe SignedCall struct combines call parameters with the signature in a well-organized structure, facilitating authenticated contract interactions.
53-61
: PublicKey struct for ECDSA P-256 keysThe PublicKey struct is well-defined for representing ECDSA P-256 public keys with proper documentation of the x and y coordinates.
packages/contracts/contracts/Base64.sol (5)
8-13
: Enhanced library documentationThe improved documentation includes title, author, and a clearer description of the library's purpose with reference to RFC 4648, making it more professional and informative.
16-16
: Clarified table commentChanged from singular to plural to correctly indicate multiple encoding/decoding tables.
23-26
: Better function documentation for encode methodThe enhanced documentation clearly explains that this method produces standard Base64 encoding with padding.
33-36
: Better function documentation for encodeURL methodThe enhanced documentation clearly explains that this method produces URL-safe Base64 encoding without padding.
43-48
: Improved internal function documentationThe documentation for the internal
_encode
function now clearly explains its dual purpose for both standard and URL-safe encoding, as well as its optimization using assembly.packages/contracts/.cursorrules (1)
4-22
: Comprehensive guidelines documented.These lines set forth a clear set of coding principles and best practices. No functional code concerns; everything neatly organized.
packages/contracts/contracts/P256.sol (1)
5-9
: Excellent documentation updates.These doc additions provide clarity on the intended usage, parameters, and behavior of the signature verification functions.
Also applies to: 14-23, 42-51
packages/contracts/ignition/modules/Giano.ts (2)
3-3
: Import statement looks good.The dedicated module for the precompile is a clean, modular approach.
15-17
: Smooth integration of account contracts.Registering
AccountRegistry
with theaccountFactory
constructor parameter is consistent and maintains clarity in your deployment module.packages/contracts/contracts/WebAuthn.sol (3)
8-13
: Enhanced high-level library documentation
These doc updates thoroughly explain the library’s purpose and reference the WebAuthn specification. This context helps developers better understand the scope and usage of this library.
15-22
: Improved function doc block
The added explanations for parameters and return values incontains
provide clarity on its purpose and expected usage.
47-51
: Constants documentation
Detailing the authenticator-data flag bits makes the code self-explanatory and reduces confusion regarding the WebAuthn bitmask definitions.packages/contracts/contracts/AbstractAccountFactory.sol (1)
6-12
: Comprehensive contract-level doc improvements
The high-level comments describing this abstract contract enhance visibility into its intended usage and architecture.packages/contracts/test/utils.ts (1)
18-22
: Add type definition for WebAuthn response
Documenting the new WebAuthn response format clarifies the structure of returned data.packages/contracts/contracts/test/TestContract.sol (1)
72-82
: Multi-operation approach is well-structured.The
multiOperation
function cleanly aggregates multiple actions (value update, message update, Ether deposit) in a single transaction. Maintaining clear event emissions for each action within the function is a good practice for transparency in testing.packages/contracts/contracts/AccountRegistry.sol (2)
245-271
: Key addition requests handled well.The
requestAddKey
function appropriately checks for a non-zero user ID and whether the key is already linked. Emitting aKeyRequestCreated
event also improves on-chain traceability. This is consistent with the principle of single-responsibility for user and key management.
205-234
:✅ Verification successful
Race condition concern in
createUser
.Although highly unlikely, there is a theoretical race where two concurrent transactions generate the same
userId
. You already mitigate this by reverting ifusers[userId].account
is nonzero. Just be aware of this edge case.Would you like to confirm by scanning the transaction flow or relevant logs to detect any repeated userId collisions in subsequent deployments?
🏁 Script executed:
#!/bin/bash # Searching logs for repeated userId usage in deployment or creation rg "UserAlreadyExists"Length of output: 207
User Collision Race Condition: No Incidents Detected
Based on the log scan, there is no evidence of repeated
UserAlreadyExists
collisions in practice. The revert mechanism when encountering a duplicateuserId
is functioning as intended, mitigating the theoretical race condition. Please continue monitoring, but no further action is needed at this time.packages/contracts/contracts/Account.sol (5)
26-33
: Clear error-handling patterns.Defining explicit errors (e.g.,
InvalidSignature
,NotAuthorized
) provides a well-structured approach to revert conditions. This is a best practice for readability and troubleshooting.
205-209
: Events for key updates enhance auditability.Emitting events like
KeyAdded
,KeyRemoved
, andKeyRoleChanged
facilitates transparent on-chain monitoring. This design choice simplifies external auditing and off-chain indexing of key state changes.
445-467
:approveKeyRequest
flow is well-defined.The function carefully checks:
- Admin operation type
- Request existence
- Key uniqueness
- Registry notification (
notifyKeyAdded
)These checks align with a secure multi-step key generation flow. Ensure downstream references to the new key are consistent.
584-596
: Verification ofexecute
flow is robust.The
execute
function includes:
validSignature
checknonReentrant
guardwhenNotPaused
check- Rethrowing of revert reason
This is a thorough approach to secure transaction execution.
751-768
:❓ Verification inconclusive
pauseAccount
/unpauseAccount
usage.Pausing logic is straightforward. The indefinite pause feature (
_until == 0 ? type(uint256).max : _until;
) is useful for emergency scenarios. Just note that this contract design grants full pause capability to any admin key. Ensure that the risk of losing all keys or having a malicious admin is addressed operationally.Would you like to confirm that each admin key is properly secured off-chain to mitigate accidental or malicious indefinite pauses?
🏁 Script executed:
#!/bin/bash # Potential approach to see if there's a documented emergency policy for admin keys rg "admin key" -A 8Length of output: 17769
Action Required: Confirm Robust Off-Chain Security for Admin Keys
The implementation for
pauseAccount
andunpauseAccount
is clear and functions as intended—setting the pause indefinitely by assigningtype(uint256).max
when_until
is 0, which is valuable for emergency interventions. However, our review indicates that admin keys, which grant full pause capability, are only referenced within the test suites without explicit documentation of off-chain security measures or emergency policies. Please confirm that there is an established, robust operational procedure to secure admin keys to prevent accidental or malicious indefinite pauses.
- Verify that off-chain key management practices and emergency policies for admin keys are clearly documented.
- Consider adding inline notes or reference documentation in the codebase to highlight these operational safeguards.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (4)
packages/contracts/contracts/Types.sol (1)
11-20
: Consider off-chain storage of large WebAuthn data.
Storing extensive data such asclientDataJSON
on-chain can be expensive in terms of gas and storage. Consider storing large or unstructured data (e.g., JSON) off-chain or using a more compact data structure if feasible.packages/contracts/contracts/AbstractAccountFactory.sol (1)
14-22
: Abstract deploy function is well-defined.
The parameters and return value are clearly specified. Consider whether you want to restrict access to specific roles or addresses.packages/contracts/contracts/Account.sol (2)
7-8
: Confirm intent to handle token transfers
By importing and implementing bothIERC721Receiver
andIERC1155Receiver
, this contract can receive ERC721 and ERC1155 tokens. While this broadens functionality, it also can introduce reentrancy concerns and complexity in scenarios where token transfers trigger external calls. Verify that this expanded scope is intentional and that use ofReentrancyGuard
(or equivalent measures) covers all relevant paths.
417-437
: Block timestamp usage for request ID
Line 430 usesblock.timestamp
to generaterequestId
, which risks collisions when multiple requests occur in the same block from the same sender. Consider including a counter or combining additional entropy to mitigate this edge case.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
packages/contracts/contracts/AbstractAccountFactory.sol
(1 hunks)packages/contracts/contracts/Account.sol
(2 hunks)packages/contracts/contracts/AccountFactory.sol
(1 hunks)packages/contracts/contracts/AccountRegistry.sol
(1 hunks)packages/contracts/contracts/Types.sol
(1 hunks)packages/contracts/test/AccountRegistry.test.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/contracts/test/AccountRegistry.test.ts
🔇 Additional comments (21)
packages/contracts/contracts/Types.sol (4)
4-9
: Documentation well-written and comprehensive.
These lines provide clear, structured documentation for the library, promoting ease of usage and maintainability.
31-41
: Clear struct definitions for call parameters.
Using a dedicatedCall
struct to encapsulate call information is clean and intuitive. It promotes reusability and clarifies the code’s intent.
43-51
: Signed call design is consistent and straightforward.
Encapsulating signed calls in a dedicated struct is a good pattern, simplifying verification logic elsewhere in the code.
53-61
: Public key struct is well-defined.
Storing x and y coordinates for an ECDSA P-256 key clarifies the curve parameters, though consider referencing or validating the curve explicitly if needed.packages/contracts/contracts/AbstractAccountFactory.sol (1)
6-12
: Robust documentation for the abstract factory.
Providing clear descriptions of purpose and design expectations fosters consistent implementations in derived contracts.packages/contracts/contracts/AccountFactory.sol (4)
8-14
: Documentation aligns with factory pattern.
These lines accurately describe the factory contract’s purpose and usage.
16-22
: Event emission improves auditability.
EmittingAccountDeployed
helps external services and users track deployments. Good use of indexed parameters.
23-41
: Deterministic deployment via CREATE2 is correctly implemented.
Usingkeccak256
of the parameters as the salt is a robust approach to produce predictable addresses. Ensure collisions won’t occur from identical inputs in different contexts.
43-62
: Predictable address computation function is added value.
Allowing clients to compute the address before deployment is useful. Implementation follows the standard CREATE2 formula.packages/contracts/contracts/AccountRegistry.sol (7)
8-16
: Comprehensive contract documentation.
The high-level overview and usage notes provide context and prevent misuse, which benefits external integrators.
17-29
: User struct naming is consistent and descriptive.
StoringcredentialId
,publicKey
, andaccount
together clarifies ownership. The approach fits well with your account-based model.
31-41
: Mappings are well-organized for user-credential-account relationships.
Each mapping clearly represents a specific relationship, reducing confusion.
73-80
: EventAddCredentialRequestCreated
fosters transparency.
Emitting this event helps external clients track credential assignment states.
197-234
: New user creation flow is logically sound.
The steps for generating a user ID, deploying the account, and linking a credential are transparent. Checking for duplicate credentials ensures uniqueness.
245-271
: Credential addition request flow is well-structured.
The registry enforces a single credential-to-account mapping before delegating the actual addition to theAccount
contract. This pattern is secure and modular.
278-299
: Credential linking/unlinking notifications are consistent.
ThenotifyKeyAdded
andnotifyCredentialRemoved
callbacks keep the registry up to date. Reverts on mismatched credential ownership protect against misuse.packages/contracts/contracts/Account.sol (5)
92-96
: Clear and contextual pause error
Defining a specificAccountIsPaused
error with a timestamp parameter helps convey precise pausing conditions to callers.
87-90
: Protecting against removal of the final admin key
TheLastAdminCredential
error prevents losing the final administrative credential. This is an important safeguard to maintain continuous governance control.
445-467
: Good adherence to checks-effects-interactions pattern
InapproveKeyRequest
, storage is updated and the credential request is removed before callingnotifyKeyAdded
. This sequence reduces reentrancy risks and ensures consistent storage in case of external call failures.
745-758
:✅ Verification successful
Indefinite pause mechanism
Usingtype(uint256).max
for_until = 0
effectively locks the contract indefinitely. Verify that at least one active admin key remains accessible to callunpauseAccount
, preventing a scenario of permanent deadlock.
🏁 Script executed:
#!/bin/bash # Checking presence of unpause references in the code and ensuring admin can still call it rg "unpauseAccount" -A 5Length of output: 2342
Indefinite Pause Safety Validation
The use of
type(uint256).max
for_until == 0
to represent an indefinite pause is intentional. Our verification confirms that theunpauseAccount
function is implemented inpackages/contracts/contracts/Account.sol
and is exercised by tests inpackages/contracts/test/Account.test.ts
, ensuring that an admin can always unpause the account. Please continue to ensure that admin key management is robust so that at least one valid admin key remains active to avoid any potential deadlock in production.
98-106
:❓ Verification inconclusive
Ensure role checks align with intended privileges
Enumeration of roles (NONE, EXECUTOR, ADMIN) is straightforward, and higher levels inherit lower-level permissions. However, confirm at runtime thatexecute
andexecuteBatch
robustly enforce a minimum ofEXECUTOR
privileges rather than just checking for non-NONE.Use the following script to confirm references to
_hasRole
in the code:
🏁 Script executed:
#!/bin/bash rg "_hasRole" -A 5Length of output: 937
Action Required: Verify Role Enforcement in Execution Functions
The_hasRole
implementation appears to correctly compare roles using their underlyinguint8
values. We see a check enforcing an admin-level requirement (e.g.,if (!_hasRole(sig.credentialId, Role.ADMIN))
) that properly validates that the credential meets or exceeds the required role. However, as noted in the original comment, please ensure that theexecute
andexecuteBatch
functions (or any other execution-related methods) are not merely checking for non-NONE
but explicitly enforce a minimum ofEXECUTOR
privileges by calling_hasRole
withRole.EXECUTOR
.
- Confirm that both
execute
andexecuteBatch
invoke_hasRole
with a minimum requirement ofRole.EXECUTOR
.- Verify that no execution pathway inadvertently allows roles below
EXECUTOR
by only checking againstNONE
.
function executeBatch( | ||
BatchCall calldata batch | ||
) external payable validSignature(bytes.concat(getBatchChallenge(batch.calls)), batch.signature) nonReentrant whenNotPaused { | ||
uint256 callsLength = batch.calls.length; | ||
require(callsLength > 0, 'No calls to execute'); | ||
|
||
for (uint256 i = 0; i < callsLength; i++) { | ||
Types.Call calldata currentCall = batch.calls[i]; | ||
(bool success, bytes memory result) = currentCall.target.call{value: currentCall.value}(currentCall.data); | ||
if (!success) { | ||
assembly { | ||
revert(add(result, 32), mload(result)) | ||
} | ||
} | ||
} | ||
|
||
emit Executed(currentNonce, address(0), 0, abi.encode(batch.calls)); | ||
currentNonce++; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Atomic batch execution
executeBatch
reverts on the first failing call, causing the entire batch to revert. While this ensures atomicity, it may be overly restrictive if partial successes are acceptable in some workflows.
Consider adding a variant function that can handle individual call failures more gracefully if needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (4)
packages/contracts/test/Account.test.ts (1)
2120-2123
: TODO: Implement reentrancy test.
The stub at line 2121 indicates an intention to verify reentrancy protection, but it’s currently unimplemented. Completing this test will help ensure robust security coverage.Would you like me to propose a reentrancy scenario for thorough testing?
packages/contracts/contracts/AccountRegistry.sol (1)
125-127
: Note on randomness.
Usingblock.prevrandao
as part of the salt generation can be manipulated by block producers and is not cryptographically secure. If true randomness or strong uniqueness is critical, consider using a more secure source.Would you like me to propose an alternative approach or integrate an oracle-based VRF?
packages/contracts/contracts/Account.sol (2)
557-574
: Consider storing the revert reason in a more explicit event.Reverting via inline assembly is a typical way to propagate the target contract’s revert reason. For verbose debugging and better off-chain tracing, consider emitting an additional event or logging the revert reason in a more structured format.
608-613
: Optional stricter requirement for admin usage.By default,
_validateAdminSignature
allows admin signatures without requiring user verification in WebAuthn. If desired, togglingrequireUserVerification
totrue
could further strengthen the admin signature checks, though this depends on your project’s security policies.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
packages/contracts/contracts/Account.sol
(2 hunks)packages/contracts/contracts/AccountRegistry.sol
(1 hunks)packages/contracts/test/Account.test.ts
(1 hunks)packages/contracts/test/AccountRegistry.test.ts
(1 hunks)
🔇 Additional comments (9)
packages/contracts/test/AccountRegistry.test.ts (2)
1-360
: Thorough test coverage.
This test suite thoroughly covers user creation, credential management, account lookup, and credential request flows, including event emission and revert conditions. The structure is well organized and consistent with Hardhat best practices.
102-115
: Naming mismatch “CredentialAlreadyUnlinked”.
These tests expect theCredentialAlreadyUnlinked
error even though the docstrings in your contract indicate the credential is already linked. This was flagged in a past review. Consider renaming the error toCredentialAlreadyLinked
to avoid confusion.packages/contracts/test/Account.test.ts (1)
1-2124
: Impressive test coverage.
This test suite methodically validates critical functionalities (credential requests, role changes, transaction execution, pausing, etc.). It’s well structured with meaningful test names that provide comprehensive coverage.packages/contracts/contracts/AccountRegistry.sol (1)
98-98
: Naming discrepancy inCredentialAlreadyUnlinked
error.
The docstring states it is thrown “when a credential is already linked,” but the error name suggests the opposite. This was previously flagged and still remains. Rename toCredentialAlreadyLinked
for better clarity.packages/contracts/contracts/Account.sol (5)
7-8
: Great addition for NFT compatibility.By importing and implementing the ERC721 and ERC1155 receiver interfaces, the contract can safely accept NFT transfers. This change is aligned with common multi-token wallet practices.
18-18
: Interface expansion is well-structured.Declaring the contract to implement
IERC1271
,IERC721Receiver
, andIERC1155Receiver
in addition toReentrancyGuard
cleanly consolidates multi-token and signature-verification functionality into a single contract.
66-70
: Restrict credential creation to registry.The
OnlyRegistryCanAddCredentials
error and its usage inmodifier onlyRegistry()
effectively prevents unauthorized credential management from external callers. This is a solid security measure.
223-228
: Separate admin and transaction nonces is a good pattern.Storing
adminNonce
andcurrentNonce
distinctly prevents replay attacks on both admin actions and normal calls, enhancing security. The explicitadminKeyCount
also makes it straightforward to enforce the “last admin” guard.
576-599
: Atomic batch mode can be restrictive.This contract reverts on the first failing call in a batch, ensuring atomicity. However, if partial executions are acceptable for your use case, consider providing an alternate function that continues executing and surfaces individual failures.
constructor(Types.PublicKey memory _initialAdminKey, bytes memory _initialCredentialId, address _registry) { | ||
credentials[_initialCredentialId] = CredentialInfo({publicKey: _initialAdminKey, role: Role.ADMIN, credentialId: _initialCredentialId, pending: false}); | ||
registry = _registry; | ||
adminKeyCount = 1; | ||
|
||
emit CredentialAdded(_initialAdminKey, Role.ADMIN); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Validate initial parameters.
While the constructor properly sets an initial admin credential, consider adding checks to ensure _registry
is not the zero address and the initial credential/key are non-trivial. This avoids accidental deployment misconfiguration.
constructor(Types.PublicKey memory _initialAdminKey, bytes memory _initialCredentialId, address _registry) {
+ require(_registry != address(0), "Registry address cannot be zero");
+ // Optionally check that _initialAdminKey is not empty
credentials[_initialCredentialId] = CredentialInfo({
...
});
registry = _registry;
...
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
constructor(Types.PublicKey memory _initialAdminKey, bytes memory _initialCredentialId, address _registry) { | |
credentials[_initialCredentialId] = CredentialInfo({publicKey: _initialAdminKey, role: Role.ADMIN, credentialId: _initialCredentialId, pending: false}); | |
registry = _registry; | |
adminKeyCount = 1; | |
emit CredentialAdded(_initialAdminKey, Role.ADMIN); | |
} | |
constructor(Types.PublicKey memory _initialAdminKey, bytes memory _initialCredentialId, address _registry) { | |
require(_registry != address(0), "Registry address cannot be zero"); | |
// Optionally check that _initialAdminKey is not empty | |
credentials[_initialCredentialId] = CredentialInfo({ | |
publicKey: _initialAdminKey, | |
role: Role.ADMIN, | |
credentialId: _initialCredentialId, | |
pending: false | |
}); | |
registry = _registry; | |
adminKeyCount = 1; | |
emit CredentialAdded(_initialAdminKey, Role.ADMIN); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
packages/contracts/test/Account.test.ts (2)
2137-2139
: Implement or remove the placeholder reentrancy test.The "should prevent reentrancy attacks" test is not implemented. Consider either implementing reentrancy scenarios to validate that
Account
is protected, or removing the placeholder if no reentrancy vectors exist.
1146-1147
: Use deterministic block timestamps instead of real-time values.Relying on
Math.floor(Date.now() / 1000)
can produce non-deterministic test behavior if the system clock and Hardhat test environment diverge. Consider usinghardhat_setNextBlockTimestamp
or a fixture-based time progression approach for stable test outcomes.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/contracts/contracts/Account.sol
(2 hunks)packages/contracts/test/Account.test.ts
(1 hunks)
🔇 Additional comments (10)
packages/contracts/test/Account.test.ts (4)
1683-1724
: Validate indefinite pause behavior.Setting
_until
to 0 triggers an indefinite pause by assigningtype(uint256).max
. Ensure this aligns with your business logic, especially if indefinite pauses might lock out legitimate users permanently.
1009-1029
: Single transaction execution looks correct.The test validates successful calls, revert correctness, and result verification. No issues are found for these lines.
1050-1071
: Nonce incrementation test is robust.The test properly checks that the nonce updates only after a successful transaction. This ensures replay protection.
1345-1374
: Batch calls revert atomically.The test confirms an all-or-nothing approach for batch calls, enhancing atomicity. This is correct if partial successes are unwelcome.
packages/contracts/contracts/Account.sol (6)
241-247
: Validate constructor parameters.Consider requiring
_registry != address(0)
or checking if_initialCredentialId
is empty. This helps avoid deploying an account with an invalid registry reference or missing admin credential.+ require(_registry != address(0), "Registry address cannot be zero"); + require(_initialCredentialId.length != 0, "Initial credential cannot be empty");
607-629
: Confirm skipping user verification is intended.Currently,
requireUserVerification: false
might allow credentials without user presence checks. If user verification is required, adjust your WebAuthn signature checks to match stricter security needs.
557-573
: Execution logic appears correct.
execute
properly checks signatures, handles reverts, and increments the nonce. No issues found with concurrency or security here.
703-711
: Interface support is accurately declared.The contract correctly reports support for ERC1155Receiver, ERC721Receiver, and ERC1271, and rejects unknown interface IDs.
549-554
: Pause validation is streamlined.The
whenNotPaused
modifier reverts on paused state usingAccountIsPaused
for clarity. This effectively blocks execution calls when the contract is paused.
728-735
: Double-check indefinite pause assignment.When
_until == 0
, the contract assignstype(uint256).max
topausedUntil
. Verify that an indefinite pause meets the project’s operational and support needs.
Summary by CodeRabbit
New Features
AccountRegistry
contract.Refactor
Chore