-
Notifications
You must be signed in to change notification settings - Fork 29
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
feat(v2): multi owner light account #32
Conversation
4b1bb9d
to
dea597b
Compare
3e79e59
to
d60557c
Compare
d60557c
to
6749293
Compare
bytes32 private constant _LA_MSG_TYPEHASH = keccak256("MultiOwnerLightAccountMessage(bytes message)"); | ||
bytes32 private constant _NAME_HASH = keccak256("MultiOwnerLightAccount"); | ||
bytes32 private constant _VERSION_HASH = keccak256("1"); |
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.
Note: this is going to get refactored later in the ERC-1271 PR (upcoming). I chose to make these different from the vanilla LightAccount here, but would love thoughts on the safety of just keeping it consistent across the different flavors.
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.
hm... I think this might make a difference if the LA is upgraded between the single owner and the multi owner versions. If they're the same, it would allow a "replay" of previous signatures using only one of the owners, but that might be OK? Since 1271 signatures expect the caller to perform the replay protections.
I think the bigger concerns are around SDK usage, where the signing requests might need to be aware of the version, which could cause debugging issues if signatures aren't working due to using the wrong LA version 712 wrapper.
I'm leaning towards making it the same for ease of SDK integration, but could be convinced otherwise.
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.
So #36 now removes the dependency on the contract to define the typehash, and pushes that responsibility to the client. Each flavor of account still defines its own name and version to prevent replays against different account versions or implementations.
Solady's EIP712 that we use implements EIP-5267 so clients can call eip712Domain
to get the name and version, which should make it easier on clients.
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.
Looks good, had some comments.
bytes32 private constant _LA_MSG_TYPEHASH = keccak256("MultiOwnerLightAccountMessage(bytes message)"); | ||
bytes32 private constant _NAME_HASH = keccak256("MultiOwnerLightAccount"); | ||
bytes32 private constant _VERSION_HASH = keccak256("1"); |
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.
hm... I think this might make a difference if the LA is upgraded between the single owner and the multi owner versions. If they're the same, it would allow a "replay" of previous signatures using only one of the owners, but that might be OK? Since 1271 signatures expect the caller to perform the replay protections.
I think the bigger concerns are around SDK usage, where the signing requests might need to be aware of the version, which could cause debugging issues if signatures aren't working due to using the wrong LA version 712 wrapper.
I'm leaning towards making it the same for ease of SDK integration, but could be convinced otherwise.
|
||
function _initialize(address[] calldata owners_) internal virtual { | ||
emit LightAccountInitialized(_ENTRY_POINT, owners_); | ||
_updateOwners(owners_, new address[](0)); |
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.
Can we replace this with _addOwnersOrRevert
? It would remove the OwnersUpdated
event, but this data is present already in the LightAccountInitialized
event.
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.
We emit OwnershipTransferred
for LightAccount on initialize, so this was an attempt to make things consistent there. Shall we remove it there as well?
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.
ah... from an integration standpoint, it's probably easier to just index the one event OwnershipTransferred
. Arguably the LightAccountInitialized
event isn't really needed, but I guess it's fine to keep as-is and have some small duplcation.
/// @dev Revert if the caller is not one of the owners or the account itself (when redirected through `execute`). | ||
function _onlyOwners() internal view { | ||
if (msg.sender != address(this) && !_getStorage().owners.contains(msg.sender.toSetValue())) { | ||
revert NotAuthorized(msg.sender); | ||
} | ||
} | ||
|
||
/// @dev Require that the call is from the entry point or an owner. | ||
function _requireFromEntryPointOrOwner() internal view { | ||
if (msg.sender != address(entryPoint()) && !_getStorage().owners.contains(msg.sender.toSetValue())) { | ||
revert NotAuthorized(msg.sender); | ||
} | ||
} |
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.
Would it make sense to merge these two modifiers? There are some workflows where needing the call to be wrapped in execute
is just an added step, like updateOwners
.
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.
Yeah. Might be good to just authorize all of (self, owner, entrypoint). Will think about this and address up the stack.
for (uint256 i = 0; i < length; ++i) { | ||
if (SignatureChecker.isValidERC1271SignatureNow(owners_[i], digest, signature)) { | ||
return true; | ||
} |
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.
In a later PR, we should split the signature checking based on a provided address, rather than iterating.
); | ||
} | ||
|
||
/// @dev `owners` must be in strictly ascending order and not include the 0 address. Its length must not be empty |
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.
Maybe add a comment about how the sorting ensures a canonical counterfactual for a given set of starting owners.
6749293
to
cb7b2f6
Compare
LinkedListSetLib
frommodular-account
to optimize storing multiple owners. We'll probably want to pull this out to a separate repo eventually.Next up:
///
instead of/** */
) that was applied here. style(v2): consistent natspec style #34