-
Notifications
You must be signed in to change notification settings - Fork 5
feat: introduce owner rotation for Safe account #90
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
base: main
Are you sure you want to change the base?
Conversation
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.
Pull Request Overview
This PR introduces owner rotation functionality for Safe smart accounts by implementing a new tx_swap_safe_owner
method that enables key rotation through the Safe contract's swapOwner
function. The implementation includes comprehensive integration tests and refactors existing code for better reusability.
- Adds Safe owner swap functionality with
SafeOwner
transaction type andtx_swap_safe_owner
method - Refactors
Is4337Encodable
trait to provide sharedas_execute_user_op_call_data
implementation - Renames
transaction_transfer
totx_transfer
for consistency and brevity
Reviewed Changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
File | Description |
---|---|
bedrock/src/transaction/mod.rs | Exports SafeOwner and adds tx_swap_safe_owner method to SafeSmartAccount |
bedrock/src/transaction/contracts/safe_owner.rs | Implements SafeOwner struct and Is4337Encodable trait for owner swap functionality |
bedrock/src/transaction/contracts/mod.rs | Adds safe_owner module export |
bedrock/src/transaction/contracts/erc20.rs | Refactors to use new Is4337Encodable trait methods |
bedrock/src/smart_account/transaction_4337.rs | Refactors Is4337Encodable trait with shared implementation |
bedrock/src/smart_account/nonce.rs | Adds SwapOwner transaction type ID |
bedrock/tests/test_smart_account_safe_owner_swap.rs | Comprehensive integration test for Safe owner swap functionality |
bedrock/tests/common.rs | Adds AnvilBackedHttpClient mock for testing 4337 operations |
bedrock/tests/test_smart_account_transfer.rs | Updates method name from transaction_transfer to tx_transfer |
sol! { | ||
///Owner Manager Interface for the Safe | ||
/// | ||
/// Reference: <https://github.com/safe-global/safe-smart-account/blob/v1.4.1/contracts/base/OwnerManager.sol> |
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.
I'm assuming there is no difference here between 1.3.0 and 1.4.1 OwnerManager? Just wanted to ask
|
||
/// Represents a Safe owner swap transaction for key rotation. | ||
pub struct SafeOwner { | ||
/// The inner call data for the ERC-20 `transferCall` function. |
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.
comment is wrong
/// - `new_owner`: The new owner to replace the old owner | ||
#[must_use] | ||
pub fn new( | ||
wallet_address: Address, |
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.
lets use safe_address
, wallet_address is unclear
}, | ||
}; | ||
|
||
const SENTINEL_ADDRESS: Address = |
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 you add a doc here on what this magic address is
) -> Self { | ||
Self { | ||
call_data: IOwnerManager::swapOwnerCall { | ||
prev_owner: SENTINEL_ADDRESS, |
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.
is this always the case? I'm not familiar with this logic in the contract
What if a user rotates for the 2nd/3rd time
} | ||
.abi_encode() | ||
.into() | ||
fn target_address(&self) -> Address { |
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.
Better to define these fns on the Is4337Encodable instead of duplicating inside each impl?
/// - Will throw an RPC error if the transaction submission fails. | ||
/// - Will throw an error if the global HTTP client has not been initialized. | ||
pub async fn transaction_transfer( | ||
pub async fn tx_transfer( |
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.
nit: lets remove tx_
prefix? Its already on the safe_account object
/// # Errors | ||
/// - Will throw a parsing error if any of the provided attributes are invalid. | ||
/// - Will throw an RPC error if the transaction submission fails. | ||
pub async fn tx_swap_safe_owner( |
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.
nit: swap_owner
?
/// This is used to allow key rotation. The EOA signer that can act on behalf of the Safe is rotated. | ||
/// | ||
/// # Arguments | ||
/// - `old_owner`: The EOA of the old owner (address). |
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 can fetch this from on-chain instead of having to take in as input
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.
do we need anything to enforce that this only done for world app safes? Ie check that threshold/signers length is 1
|
||
// Create SafeSmartAccount instance | ||
let safe_account = | ||
SafeSmartAccount::new(initial_owner_key_hex, &safe_address.to_string()) |
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 make the parameter be Address
instead of hex?
* refactors * improvements * clarify for tests
prev_owner: SENTINEL_ADDRESS, | ||
old_owner, | ||
new_owner, | ||
} |
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.
Bug: Incorrect prev_owner
Handling in SafeOwner::new
The SafeOwner::new
function hardcodes prev_owner
to SENTINEL_ADDRESS
(0x1) for the swapOwner
call. This is only correct for single-owner Safes or the first owner swap. For multi-owner Safes or subsequent rotations, prev_owner
must be the actual preceding owner, causing transactions to fail on-chain.
Changes
swapOwner
on the Safe contract to rotate the owner of the Safe.as_execute_user_op_call_data
in theIs4337Encodable
as for types of transactions this code will be exactly the same.transaction_transfer
totx_transfer
. Less verbosity, especially as we introduce other types of transactions.🔜 Changes coming soon
Notes