-
Notifications
You must be signed in to change notification settings - Fork 2
7702 execution session keys #21
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ members = [ | |
"aa-types", | ||
"aa-core", | ||
"core", | ||
"eip7702-core", | ||
"executors", | ||
"server", | ||
"thirdweb-core", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,36 @@ | ||
use alloy::primitives::Address; | ||
use schemars::JsonSchema; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
use crate::defs::AddressDef; | ||
|
||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, utoipa::ToSchema)] | ||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)] | ||
#[schema(title = "EIP-7702 Execution Options")] | ||
#[serde(rename_all = "camelCase", untagged)] | ||
pub enum Eip7702ExecutionOptions { | ||
/// Execute the transaction as the owner of the account | ||
Owner(Eip7702OwnerExecution), | ||
/// Execute a transaction on a different delegated account (`account_address`), which has granted a session key to the `session_key_address` | ||
/// `session_key_address` is the signer for this transaction | ||
SessionKey(Eip7702SessionKeyExecution), | ||
} | ||
|
||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)] | ||
#[schema(title = "EIP-7702 Owner Execution")] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct Eip7702ExecutionOptions { | ||
/// The EOA address that will sign the EIP-7702 transaction | ||
#[schemars(with = "AddressDef")] | ||
pub struct Eip7702OwnerExecution { | ||
#[schema(value_type = AddressDef)] | ||
/// The delegated EOA address | ||
pub from: Address, | ||
} | ||
|
||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)] | ||
#[schema(title = "EIP-7702 Session Key Execution")] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct Eip7702SessionKeyExecution { | ||
#[schema(value_type = AddressDef)] | ||
/// The session key address is your server wallet, which has been granted a session key to the `account_address` | ||
pub session_key_address: Address, | ||
#[schema(value_type = AddressDef)] | ||
/// The account address is the address of a delegated account you want to execute the transaction on. This account has granted a session key to the `session_key_address` | ||
pub account_address: Address, | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,14 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[package] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
name = "engine-eip7702-core" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
version = "0.1.0" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
edition = "2024" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[dependencies] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
alloy = { workspace = true, features = ["serde"] } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
tokio = "1.44.2" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
engine-core = { path = "../core" } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
serde = { version = "1.0.219", features = ["derive"] } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
serde_json = "1.0" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
tracing = "0.1.41" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
rand = "0.9" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
thiserror = "2.0" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+1
to
+14
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
-edition = "2024"
+edition = "2021"
-tokio = "1.44.2"
+# keep in sync with workspace root
+tokio = "1.38"
Please bump once the official 2024 edition and the referenced Tokio release ship. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
use alloy::primitives::{Address, address}; | ||
|
||
/// The minimal account implementation address used for EIP-7702 delegation | ||
pub const MINIMAL_ACCOUNT_IMPLEMENTATION_ADDRESS: Address = | ||
address!("0xD6999651Fc0964B9c6B444307a0ab20534a66560"); | ||
|
||
/// EIP-7702 delegation prefix bytes | ||
pub const EIP_7702_DELEGATION_PREFIX: [u8; 3] = [0xef, 0x01, 0x00]; | ||
|
||
/// EIP-7702 delegation code length (prefix + address) | ||
pub const EIP_7702_DELEGATION_CODE_LENGTH: usize = 23; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
use alloy::{ | ||
primitives::{Address, FixedBytes}, | ||
providers::Provider, | ||
}; | ||
use engine_core::{ | ||
chain::Chain, | ||
credentials::SigningCredential, | ||
error::{AlloyRpcErrorToEngineError, EngineError}, | ||
signer::{AccountSigner, EoaSigner, EoaSigningOptions}, | ||
}; | ||
use rand::Rng; | ||
|
||
use crate::constants::{ | ||
EIP_7702_DELEGATION_CODE_LENGTH, EIP_7702_DELEGATION_PREFIX, | ||
MINIMAL_ACCOUNT_IMPLEMENTATION_ADDRESS, | ||
}; | ||
|
||
/// Represents an EOA address that can have EIP-7702 delegation, associated with a specific chain | ||
#[derive(Clone, Debug)] | ||
pub struct DelegatedAccount<C: Chain> { | ||
/// The EOA address that may have delegation | ||
pub eoa_address: Address, | ||
/// The chain this account operates on | ||
pub chain: C, | ||
} | ||
|
||
impl<C: Chain> DelegatedAccount<C> { | ||
/// Create a new delegated account from an EOA address and chain | ||
pub fn new(eoa_address: Address, chain: C) -> Self { | ||
Self { eoa_address, chain } | ||
} | ||
|
||
/// Check if the EOA has EIP-7702 delegation to the minimal account implementation | ||
pub async fn is_minimal_account(&self) -> Result<bool, EngineError> { | ||
// Get the bytecode at the EOA address using eth_getCode | ||
let code = self | ||
.chain | ||
.provider() | ||
.get_code_at(self.eoa_address) | ||
.await | ||
.map_err(|e| e.to_engine_error(self.chain()))?; | ||
|
||
tracing::debug!( | ||
eoa_address = ?self.eoa_address, | ||
code_length = code.len(), | ||
code_hex = ?alloy::hex::encode(&code), | ||
"Checking EIP-7702 delegation" | ||
); | ||
|
||
// Check if code exists and starts with EIP-7702 delegation prefix "0xef0100" | ||
if code.len() < EIP_7702_DELEGATION_CODE_LENGTH | ||
|| !code.starts_with(&EIP_7702_DELEGATION_PREFIX) | ||
{ | ||
tracing::debug!( | ||
eoa_address = ?self.eoa_address, | ||
has_delegation = false, | ||
reason = "Code too short or doesn't start with EIP-7702 prefix", | ||
"EIP-7702 delegation check result" | ||
); | ||
return Ok(false); | ||
} | ||
|
||
// Extract the target address from bytes 3-23 (20 bytes for address) | ||
// EIP-7702 format: 0xef0100 + 20 bytes address | ||
let target_bytes = &code[3..23]; | ||
let target_address = Address::from_slice(target_bytes); | ||
|
||
// Compare with the minimal account implementation address | ||
let is_delegated = target_address == MINIMAL_ACCOUNT_IMPLEMENTATION_ADDRESS; | ||
|
||
tracing::debug!( | ||
eoa_address = ?self.eoa_address, | ||
target_address = ?target_address, | ||
minimal_account_address = ?MINIMAL_ACCOUNT_IMPLEMENTATION_ADDRESS, | ||
has_delegation = is_delegated, | ||
"EIP-7702 delegation check result" | ||
); | ||
|
||
Ok(is_delegated) | ||
} | ||
|
||
/// Get the EOA address | ||
pub fn address(&self) -> Address { | ||
self.eoa_address | ||
} | ||
|
||
/// Get the current nonce for the EOA | ||
pub async fn get_nonce(&self) -> Result<u64, EngineError> { | ||
self.chain | ||
.provider() | ||
.get_transaction_count(self.eoa_address) | ||
.await | ||
.map_err(|e| e.to_engine_error(self.chain())) | ||
} | ||
|
||
/// Get a reference to the chain | ||
pub fn chain(&self) -> &C { | ||
&self.chain | ||
} | ||
|
||
/// Sign authorization for EIP-7702 delegation (automatically fetches nonce) | ||
pub async fn sign_authorization( | ||
&self, | ||
eoa_signer: &EoaSigner, | ||
credentials: &SigningCredential, | ||
) -> Result<alloy::eips::eip7702::SignedAuthorization, EngineError> { | ||
let nonce = self.get_nonce().await?; | ||
|
||
let signing_options = EoaSigningOptions { | ||
from: self.eoa_address, | ||
chain_id: Some(self.chain.chain_id()), | ||
}; | ||
|
||
eoa_signer | ||
.sign_authorization( | ||
signing_options, | ||
self.chain.chain_id(), | ||
MINIMAL_ACCOUNT_IMPLEMENTATION_ADDRESS, | ||
nonce, | ||
credentials, | ||
) | ||
.await | ||
} | ||
|
||
/// Generate a random UID for wrapped calls | ||
pub fn generate_random_uid() -> FixedBytes<32> { | ||
let mut rng = rand::rng(); | ||
let mut bytes = [0u8; 32]; | ||
rng.fill(&mut bytes); | ||
FixedBytes::from(bytes) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
pub mod constants; | ||
pub mod delegated_account; | ||
pub mod transaction; |
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.
💡 Verification agent
🧩 Analysis chain
Verify the removal of
JsonSchema
derive is intentional.The
JsonSchema
derive was removed from the enum while being retained on the struct variants. This might affect API documentation generation if schemas are used for OpenAPI specs.🏁 Script executed:
Length of output: 890
🏁 Script executed:
Length of output: 3451
Add missing
JsonSchema
derive to EIP-7702 execution optionsThe
Eip7702ExecutionOptions
enum and its two variant structs currently deriveDebug
,Clone
,Serialize
,Deserialize
, andutoipa::ToSchema
, but omitJsonSchema
. All otherExecutionOptions
types in this module include theJsonSchema
derive to feed Schemars into your OpenAPI docs. To keep schema generation consistent, please update the derives as follows:• In core/src/execution_options/eip7702.rs:
• Apply the same change to the two structs at lines 17 and 26 so they also include
JsonSchema
.This will restore Schemars support for the EIP-7702 options and align with the rest of the execution-options API.
📝 Committable suggestion
🤖 Prompt for AI Agents