Skip to content

Commit

Permalink
feat(op receipt): implement wait_for_user_operation_receipt (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
jackpooleywc authored Sep 16, 2024
1 parent dfe0f88 commit 7bc810d
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import Foundation

public struct UserOperationReceipt: Codable {

public struct Receipt: Codable {
public let transactionHash: String
public let transactionIndex: String
public let block_hash: String
public let block_number: String
public let from: String
public let to: String
public let cumulativeGasUsed: String
public let gas_used: String
public let contractAddress: String?
public let status: String
public let logsBloom: String
public let effectiveGasPrice: String

public init(
transactionHash: String,
transactionIndex: String,
block_hash: String,
block_number: String,
from: String,
to: String,
cumulativeGasUsed: String,
gas_used: String,
contractAddress: String?,
status: String,
logsBloom: String,
effectiveGasPrice: String
) {
self.transactionHash = transactionHash
self.transactionIndex = transactionIndex
self.block_hash = block_hash
self.block_number = block_number
self.from = from
self.to = to
self.cumulativeGasUsed = cumulativeGasUsed
self.gas_used = gas_used
self.contractAddress = contractAddress
self.status = status
self.logsBloom = logsBloom
self.effectiveGasPrice = effectiveGasPrice
}
}

public let userOpHash: String
public let entryPoint: String
public let sender: String
public let nonce: String
public let paymaster: String
public let actualGasCost: String
public let actualGasUsed: String
public let success: Bool
public let receipt: Receipt

public init(
userOpHash: String,
entryPoint: String,
sender: String,
nonce: String,
paymaster: String,
actualGasCost: String,
actualGasUsed: String,
success: Bool,
receipt: Receipt
) {
self.userOpHash = userOpHash
self.entryPoint = entryPoint
self.sender = sender
self.nonce = nonce
self.paymaster = paymaster
self.actualGasCost = actualGasCost
self.actualGasUsed = actualGasUsed
self.success = success
self.receipt = receipt
}
}
13 changes: 13 additions & 0 deletions crates/ffi/src/account_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,19 @@ impl FFIAccountClient {
.sign_message_with_mnemonic(message, mnemonic)
.map_err(|e| FFIError::Unknown(e.to_string()))
}

pub async fn wait_for_user_operation_receipt(
&self,
user_operation_hash: String,
) -> Result<String, FFIError> {
self.account_client
.wait_for_user_operation_receipt(user_operation_hash)
.await
.iter()
.map(serde_json::to_string)
.collect::<Result<String, serde_json::Error>>()
.map_err(|e| FFIError::Unknown(e.to_string()))
}
}

impl From<ffi::FFITransaction> for Transaction {
Expand Down
5 changes: 5 additions & 0 deletions crates/ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ mod ffi {
message: String,
mnemonic: String,
) -> Result<String, FFIError>;

pub async fn wait_for_user_operation_receipt(
&self,
user_operation_hash: String,
) -> Result<String, FFIError>;
}

extern "Rust" {
Expand Down
34 changes: 34 additions & 0 deletions crates/yttrium/src/account_client.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
use crate::bundler::{
client::BundlerClient, config::BundlerConfig,
models::user_operation_receipt::UserOperationReceipt,
};
use crate::config::Config;
use crate::private_key_service::PrivateKeyService;
use crate::sign_service::SignService;
Expand Down Expand Up @@ -161,6 +165,30 @@ impl AccountClient {

Ok(signature)
}

pub async fn wait_for_user_operation_receipt(
&self,
user_operation_hash: String,
) -> eyre::Result<UserOperationReceipt> {
println!("Querying for receipts...");

let bundler_base_url = self.config.clone().endpoints.bundler.base_url;

let bundler_client =
BundlerClient::new(BundlerConfig::new(bundler_base_url.clone()));
let receipt = bundler_client
.wait_for_user_operation_receipt(user_operation_hash.clone())
.await?;

println!("Received User Operation receipt: {:?}", receipt);

let tx_hash = receipt.clone().receipt.transaction_hash;
println!(
"UserOperation included: https://sepolia.etherscan.io/tx/{}",
tx_hash
);
Ok(receipt)
}
}

impl AccountClient {
Expand Down Expand Up @@ -268,6 +296,12 @@ mod tests {

println!("user_operation_hash: {:?}", user_operation_hash);

let receipt = account_client
.wait_for_user_operation_receipt(user_operation_hash)
.await?;

println!("receipt: {:?}", receipt);

Ok(())
}

Expand Down
4 changes: 2 additions & 2 deletions crates/yttrium/src/bundler/models/user_operation_receipt.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use alloy::primitives::Address;
use serde::{Deserialize, Serialize};

#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UserOperationReceiptReceipt {
pub transaction_hash: String,
Expand All @@ -19,7 +19,7 @@ pub struct UserOperationReceiptReceipt {
pub effective_gas_price: String,
}

#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UserOperationReceipt {
pub user_op_hash: String,
Expand Down
18 changes: 0 additions & 18 deletions crates/yttrium/src/transaction/send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,24 +349,6 @@ mod tests {

println!("Received User Operation hash: {:?}", user_operation_hash);

// let receipt = bundler_client
// .get_user_operation_receipt(user_operation_hash.clone())
// .await?;

// println!("Received User Operation receipt: {:?}", receipt);

// println!("Querying for receipts...");

// let receipt = bundler_client
// .wait_for_user_operation_receipt(user_operation_hash.clone())
// .await?;

// let tx_hash = receipt.receipt.transaction_hash;
// println!(
// "UserOperation included: https://sepolia.etherscan.io/tx/{}",
// tx_hash
// );

Ok(user_operation_hash)
}

Expand Down
22 changes: 0 additions & 22 deletions crates/yttrium/src/transaction/send/simple_account_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,28 +252,6 @@ pub async fn send_transaction_with_signer(

println!("Received User Operation hash: {:?}", user_operation_hash);

// TODO convert to polling
use std::time::Duration;
tokio::time::sleep(Duration::from_secs(2)).await;

let receipt = bundler_client
.get_user_operation_receipt(user_operation_hash.clone())
.await?;

println!("Received User Operation receipt: {:?}", receipt);

println!("Querying for receipts...");

let receipt = bundler_client
.wait_for_user_operation_receipt(user_operation_hash.clone())
.await?;

let tx_hash = receipt.receipt.transaction_hash;
println!(
"UserOperation included: https://sepolia.etherscan.io/tx/{}",
tx_hash
);

Ok(user_operation_hash)
}

Expand Down
16 changes: 16 additions & 0 deletions platforms/swift/Sources/Yttrium/AccountClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,22 @@ public final class AccountClient: AccountClientProtocol {
)
.toString()
}

public func waitForUserOperationReceipt(
userOperationHash: String
) async throws -> UserOperationReceipt {
let jsonString = try await coreAccountClient
.wait_for_user_operation_receipt(
userOperationHash.intoRustString()
)
.toString()
let jsonData = Data(jsonString.utf8)
let receipt = try JSONDecoder().decode(
UserOperationReceipt.self,
from: jsonData
)
return receipt
}
}

extension Transaction {
Expand Down
6 changes: 4 additions & 2 deletions platforms/swift/Tests/YttriumTests/AccountClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ final class AccountClientTests: XCTestCase {
entryPoint: Self.entryPoint,
chainId: Self.chainId,
config: config,
signerType: .privateKey
signerType: .privateKey,
safe: false
)
accountClient.register(privateKey: Self.privateKeyHex)

Expand All @@ -44,7 +45,8 @@ final class AccountClientTests: XCTestCase {
entryPoint: Self.entryPoint,
chainId: Self.chainId,
config: config,
signerType: .privateKey
signerType: .privateKey,
safe: false
)
accountClient.register(privateKey: Self.privateKeyHex)

Expand Down

0 comments on commit 7bc810d

Please sign in to comment.