Skip to content

Commit

Permalink
Merge pull request #17 from alloy-rs/zerosnacks/add-keystore-example
Browse files Browse the repository at this point in the history
feat(wallets): add keystore example
  • Loading branch information
zerosnacks authored Mar 25, 2024
2 parents 2c009eb + 02802aa commit f2c7292
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 5 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ alloy = { git = "https://github.com/alloy-rs/alloy", rev = "fd8f065", features =
"signers",
# "signer-aws",
# "signer-gcp",
"signer-keystore",
"signer-ledger",
"signer-mnemonic",
"signer-trezor",
"signer-wallet",
# "signer-keystore",
"signer-mnemonic",
"signer-yubihsm",
"transports",
"transport-http",
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,5 @@ cargo run --example mnemonic_signer
- [x] [Sign permit hash](./examples/wallets/examples/sign_permit_hash.rs)
- [x] [Trezor signer](./examples/wallets/examples/trezor_signer.rs)
- [x] [Yubi signer](./examples/wallets/examples/yubi_signer.rs)
- [ ] Keystore signer
- [x] [Keystore signer](./examples/wallets/examples/keystore_signer.rs)
- [x] [Create keystore](./examples/wallets/examples/create_keystore.rs)
2 changes: 2 additions & 0 deletions examples/wallets/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ repository.workspace = true
alloy.workspace = true

eyre.workspace = true
rand = "0.8.5"
reqwest.workspace = true
serde.workspace = true
tempfile = "3.10.1"
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
46 changes: 46 additions & 0 deletions examples/wallets/examples/create_keystore.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//! Example of creating a keystore file from a private key and password, and then reading it back.
use alloy::{primitives::hex, signers::wallet::Wallet};
use eyre::Result;
use rand::thread_rng;
use std::fs::read_to_string;
use tempfile::tempdir;

#[tokio::main]
async fn main() -> Result<()> {
let dir = tempdir()?;
let mut rng = thread_rng();

// Private key of Alice, the first default Anvil account.
let private_key = hex!("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80");

// Password to encrypt the keystore file with.
let password = "test";

// Create a keystore file from the private key of Alice, returning a [Wallet] instance.
let (wallet, file_path) =
Wallet::encrypt_keystore(&dir, &mut rng, private_key, password, None).unwrap();

let keystore_file_path = dir.path().join(file_path.clone());

println!("Wrote keystore for {:?} to {:?}", wallet.address(), keystore_file_path);

// Read the keystore file back.
let recovered_wallet = Wallet::decrypt_keystore(keystore_file_path.clone(), password)?;

println!(
"Read keystore from {:?}, recovered address: {:?}",
keystore_file_path,
recovered_wallet.address()
);

// Assert that the address of the original key and the recovered key are the same.
assert_eq!(wallet.address(), recovered_wallet.address());

// Display the contents of the keystore file.
let keystore_contents = read_to_string(keystore_file_path)?;

println!("Keystore file contents: {:?}", keystore_contents);

Ok(())
}
20 changes: 20 additions & 0 deletions examples/wallets/examples/keystore/alice.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"crypto": {
"cipher": "aes-128-ctr",
"cipherparams": {
"iv": "93f3de411f33a2027780f4b758fc79a7"
},
"ciphertext": "92dd80b88bc29c4814ba2df782962d8a61bc8171e2da4504f7c86abf87c700a2",
"kdf": "scrypt",
"kdfparams": {
"dklen": 32,
"n": 8192,
"p": 1,
"r": 8,
"salt": "048ddfcdbc645b3f25de25fa5e1c22e2cf66f1c842cade848b55aab8ef74b9e7"
},
"mac": "b65b8441b6d778247d72d5f46a9fe5ee3311c80391f023b9e034dee0b400c091"
},
"id": "fa6c4d94-1f55-4959-bdc9-ef82e59f9ae2",
"version": 3
}
50 changes: 50 additions & 0 deletions examples/wallets/examples/keystore_signer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//! Example of using a keystore wallet to sign and broadcast a transaction on a local Anvil node.
use alloy::{
network::EthereumSigner,
node_bindings::Anvil,
primitives::{address, U256},
providers::{Provider, ProviderBuilder},
rpc::{client::RpcClient, types::eth::request::TransactionRequest},
signers::wallet::Wallet,
};
use eyre::Result;
use std::{env, path::PathBuf};

#[tokio::main]
async fn main() -> Result<()> {
// Spin up an Anvil node.
let anvil = Anvil::new().block_time(1).try_spawn()?;

// Password to decrypt the keystore file with.
let password = "test";

// Set up a wallet using Alice's keystore file.
// The private key belongs to Alice, the first default Anvil account.
let keystore_file_path =
PathBuf::from(env::var("CARGO_MANIFEST_DIR")?).join("examples/keystore/alice.json");
let wallet = Wallet::decrypt_keystore(keystore_file_path, password)?;

// Create a provider with the signer.
let http = anvil.endpoint().parse()?;
let provider = ProviderBuilder::new()
.signer(EthereumSigner::from(wallet))
.on_client(RpcClient::new_http(http));

// Create a transaction.
let tx = TransactionRequest {
value: Some(U256::from(100)),
to: address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into(),
nonce: Some(0),
gas_price: Some(U256::from(20e9)),
gas: Some(U256::from(21000)),
..Default::default()
};

// Broadcast the transaction and wait for the receipt.
let receipt = provider.send_transaction(tx).await?.get_receipt().await?;

println!("Send transaction: {:?}", receipt.transaction_hash);

Ok(())
}
4 changes: 2 additions & 2 deletions examples/wallets/examples/private_key_signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ async fn main() -> Result<()> {
// Spin up an Anvil node.
let anvil = Anvil::new().block_time(1).try_spawn()?;

// Set up the wallets.
// Set up a local wallet from the first default Anvil account (Alice).
let wallet: LocalWallet = anvil.keys()[0].clone().into();

// Create a provider with the signer.
Expand All @@ -35,7 +35,7 @@ async fn main() -> Result<()> {
};

// Broadcast the transaction and wait for the receipt.
let receipt = provider.send_transaction(tx).await?.with_confirmations(1).get_receipt().await?;
let receipt = provider.send_transaction(tx).await?.get_receipt().await?;

println!("Send transaction: {:?}", receipt.transaction_hash);

Expand Down

0 comments on commit f2c7292

Please sign in to comment.