Skip to content

Commit 388c9c7

Browse files
authored
feat: browser wallet (#12302)
* start drafting * establish very basic flow * nits * clean up api * create basic test suite * make timeout configurable, improve types, extend tests * fix test * clean up * add transaction accept test * clean up tests * add basic setup test * solidify api * simplify and harden api * tweaks * move tests * expand test suite * clean up * nit * add basic session token * apply session token, fix tests by adding session token to reqwest * fix test * fix reqwest openssl issue, use workspace version * harden CSP, inject session token into JS, define 0 cache policy * remove flawed session token for now, to implement actual /session route & cookie token * port interface * replace with v0.0.0 release of foundry-browser-wallet * host interface * ignore build files from typos * update * fix CSP to allow for RPC calls * update wallet v0.0.0 * nits * add development mode to relax certain security restrictions such as origin check, enforce injected session token protected by strict CSP in production * update browser wallet v0.0.0, includes session token support * add notice * add signing flow * restructure sign message * serialize camelcase * bump version * prefer fields * prefer non-blocking async api * remove unintended bump of proptest
1 parent 25196fa commit 388c9c7

File tree

30 files changed

+2544
-130
lines changed

30 files changed

+2544
-130
lines changed

Cargo.lock

Lines changed: 244 additions & 99 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -285,13 +285,14 @@ op-alloy-consensus = "0.22.0"
285285
op-alloy-rpc-types = "0.22.0"
286286
op-alloy-flz = "0.13.1"
287287

288-
## revm
288+
## alloy-evm
289+
alloy-evm = "0.23.2"
290+
alloy-op-evm = "0.23.2"
291+
292+
# revm
289293
revm = { version = "31.0.0", default-features = false }
290294
revm-inspectors = { version = "0.32.0", features = ["serde"] }
291295
op-revm = { version = "12.0.0", default-features = false }
292-
## alloy-evm
293-
alloy-evm = "0.23.1"
294-
alloy-op-evm = "0.23.1"
295296

296297
## cli
297298
anstream = "0.6"

crates/cast/src/cmd/send.rs

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
use crate::{
2-
Cast,
3-
tx::{self, CastTxBuilder},
4-
};
1+
use std::{path::PathBuf, str::FromStr, time::Duration};
2+
53
use alloy_ens::NameOrAddress;
64
use alloy_network::{AnyNetwork, EthereumWallet};
75
use alloy_provider::{Provider, ProviderBuilder};
@@ -15,7 +13,12 @@ use foundry_cli::{
1513
utils,
1614
utils::LoadConfig,
1715
};
18-
use std::{path::PathBuf, str::FromStr, time::Duration};
16+
use foundry_wallets::WalletSigner;
17+
18+
use crate::{
19+
Cast,
20+
tx::{self, CastTxBuilder},
21+
};
1922

2023
/// CLI arguments for `cast send`.
2124
#[derive(Debug, Parser)]
@@ -158,7 +161,7 @@ impl SendTxArgs {
158161
// Default to sending via eth_sendTransaction if the --unlocked flag is passed.
159162
// This should be the only way this RPC method is used as it requires a local node
160163
// or remote RPC with unlocked accounts.
161-
if unlocked {
164+
if unlocked && !eth.wallet.browser {
162165
// only check current chain id if it was specified in the config
163166
if let Some(config_chain) = config.chain {
164167
let current_chain_id = provider.get_chain_id().await?;
@@ -192,14 +195,33 @@ impl SendTxArgs {
192195

193196
tx::validate_from_address(eth.wallet.from, from)?;
194197

195-
let (tx, _) = builder.build(&signer).await?;
198+
// Browser wallets work differently as they sign and send the transaction in one step.
199+
if eth.wallet.browser
200+
&& let WalletSigner::Browser(ref browser_signer) = signer
201+
{
202+
let (tx_request, _) = builder.build(from).await?;
203+
let tx_hash = browser_signer.send_transaction_via_browser(tx_request.inner).await?;
204+
205+
if cast_async {
206+
sh_println!("{tx_hash:#x}")?;
207+
} else {
208+
let receipt = Cast::new(&provider)
209+
.receipt(format!("{tx_hash:#x}"), None, confirmations, Some(timeout), false)
210+
.await?;
211+
sh_println!("{receipt}")?;
212+
}
213+
214+
return Ok(());
215+
}
216+
217+
let (tx_request, _) = builder.build(&signer).await?;
196218

197219
let wallet = EthereumWallet::from(signer);
198220
let provider = ProviderBuilder::<_, _, AnyNetwork>::default()
199221
.wallet(wallet)
200222
.connect_provider(&provider);
201223

202-
cast_send(provider, tx, cast_async, confirmations, timeout).await
224+
cast_send(provider, tx_request, cast_async, confirmations, timeout).await
203225
}
204226
}
205227
}

crates/cast/src/cmd/wallet/list.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::env;
44

55
use foundry_common::{fs, sh_err, sh_println};
66
use foundry_config::Config;
7-
use foundry_wallets::multi_wallet::MultiWalletOptsBuilder;
7+
use foundry_wallets::wallet_multi::MultiWalletOptsBuilder;
88

99
/// CLI arguments for `cast wallet list`.
1010
#[derive(Clone, Debug, Parser)]

crates/cheatcodes/src/inspector.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ use foundry_evm_core::{
4646
use foundry_evm_traces::{
4747
TracingInspector, TracingInspectorConfig, identifier::SignaturesIdentifier,
4848
};
49-
use foundry_wallets::multi_wallet::MultiWallet;
49+
use foundry_wallets::wallet_multi::MultiWallet;
5050
use itertools::Itertools;
5151
use proptest::test_runner::{RngAlgorithm, TestRng, TestRunner};
5252
use rand::Rng;

crates/cheatcodes/src/script.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use alloy_rpc_types::Authorization;
77
use alloy_signer::SignerSync;
88
use alloy_signer_local::PrivateKeySigner;
99
use alloy_sol_types::SolValue;
10-
use foundry_wallets::{WalletSigner, multi_wallet::MultiWallet};
10+
use foundry_wallets::{WalletSigner, wallet_multi::MultiWallet};
1111
use parking_lot::Mutex;
1212
use revm::{
1313
bytecode::Bytecode,

crates/lint/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
[package]
32
name = "forge-lint"
43

crates/wallets/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ alloy-consensus.workspace = true
2525
alloy-sol-types.workspace = true
2626
alloy-dyn-abi.workspace = true
2727

28+
# browser wallet
29+
alloy-rpc-types.workspace = true
30+
axum.workspace = true
31+
foundry-common.workspace = true
32+
serde_json.workspace = true
33+
tokio = { workspace = true, features = ["macros"] }
34+
uuid.workspace = true
35+
webbrowser = "1.0.6"
36+
2837
# aws-kms
2938
alloy-signer-aws = { workspace = true, features = ["eip712"], optional = true }
3039
aws-config = { version = "1", default-features = true, optional = true }
@@ -42,11 +51,14 @@ eyre.workspace = true
4251
rpassword = "7"
4352
serde.workspace = true
4453
thiserror.workspace = true
54+
tower.workspace = true
55+
tower-http = { workspace = true, features = ["cors", "set-header"] }
4556
tracing.workspace = true
4657
eth-keystore = "0.5.0"
4758

4859
[dev-dependencies]
4960
tokio = { workspace = true, features = ["macros"] }
61+
reqwest = { workspace = true, features = ["json"] }
5062

5163
[features]
5264
aws-kms = ["dep:alloy-signer-aws", "dep:aws-config"]

crates/wallets/src/error.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ use alloy_signer_gcp::GcpSignerError;
1313
#[cfg(feature = "turnkey")]
1414
use alloy_signer_turnkey::TurnkeySignerError;
1515

16+
use crate::wallet_browser::error::BrowserWalletError;
17+
1618
#[derive(Debug, thiserror::Error)]
1719
pub enum PrivateKeyError {
1820
#[error("Failed to create wallet from private key. Private key is invalid hex: {0}")]
@@ -43,6 +45,8 @@ pub enum WalletSignerError {
4345
#[cfg(feature = "turnkey")]
4446
Turnkey(#[from] TurnkeySignerError),
4547
#[error(transparent)]
48+
Browser(#[from] BrowserWalletError),
49+
#[error(transparent)]
4650
Io(#[from] std::io::Error),
4751
#[error(transparent)]
4852
InvalidHex(#[from] FromHexError),
@@ -64,4 +68,8 @@ impl WalletSignerError {
6468
pub fn turnkey_unsupported() -> Self {
6569
Self::UnsupportedSigner("Turnkey")
6670
}
71+
72+
pub fn browser_unsupported() -> Self {
73+
Self::UnsupportedSigner("Browser Wallet")
74+
}
6775
}

crates/wallets/src/lib.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,24 @@
55
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
66
#![cfg_attr(docsrs, feature(doc_cfg))]
77

8+
#[macro_use]
9+
extern crate foundry_common;
10+
811
#[macro_use]
912
extern crate tracing;
1013

1114
pub mod error;
12-
pub mod multi_wallet;
13-
pub mod raw_wallet;
15+
pub mod opts;
16+
pub mod signer;
1417
pub mod utils;
15-
pub mod wallet;
16-
pub mod wallet_signer;
18+
pub mod wallet_browser;
19+
pub mod wallet_multi;
20+
pub mod wallet_raw;
1721

18-
pub use multi_wallet::MultiWalletOpts;
19-
pub use raw_wallet::RawWalletOpts;
20-
pub use wallet::WalletOpts;
21-
pub use wallet_signer::{PendingSigner, WalletSigner};
22+
pub use opts::WalletOpts;
23+
pub use signer::{PendingSigner, WalletSigner};
24+
pub use wallet_multi::MultiWalletOpts;
25+
pub use wallet_raw::RawWalletOpts;
2226

2327
#[cfg(feature = "aws-kms")]
2428
use aws_config as _;

0 commit comments

Comments
 (0)