Skip to content

Commit

Permalink
Improve API consistency (#85)
Browse files Browse the repository at this point in the history
* Add withdraw request builder

* New module for specific transaction requests

* Improve return type on send functions

* Delete duplicated code to get providers

* Add function to provider to send eip712 transactions

* Add from trait for eip712 transactions with every request type

* Revert returning transaction hash from send functions

* Add transfer request type

* Add defaults for withdraw request

* Update wallet to use every transaction type

* Add deploy request implementation

* Use deploy request in wallet deploy function

* Add call request implementation

* Fix unused imports

* Fix deploy test

* Fix clippy lints

* Add request conversion error

* Fix unused import

* Update README and payment example

* Return contract address in deploy function

* Remove unnecesary sleeps in tests

* Update request builder methods

* Change return type to pending transaction

* Return transaction ID in every wallet operation

* Update README and payment example

* Empty commit
  • Loading branch information
IAvecilla authored Aug 29, 2023
1 parent ae3afd8 commit e0ef89e
Show file tree
Hide file tree
Showing 16 changed files with 676 additions and 462 deletions.
24 changes: 16 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
- [Importing dependencies](#importing-dependencies)
- [Connecting to the zkSync Network](#connecting-to-the-zksync-network)
- [Creating a ZK-Wallet](#creating-a-zk-wallet)
- [Creating a Payment Parameters](#creating-a-payment-parameters)
- [Creating a Payment Transaction](#creating-a-payment-transaction)
- [Sending the Transaction](#sending-the-transaction)
- [Checking zkSync account balance](#checking-zksync-account-balance)
- [Simple Transfer Example](#simple-transfer-example)
Expand Down Expand Up @@ -82,32 +82,40 @@ let wallet = zksync::Wallet::with_chain_id(private_key, zksync_era_chain_id);
let zk_wallet = zksync::ZKSWallet::new(wallet, None, Some(provider), None).unwrap();
```
#### Creating a Payment Parameters
#### Creating a Payment Transaction
To create a payment transaction, you need to provide the receiver's address, and the amount to transfer. The sender address will be derived from the private key used to create the wallet.
To create a payment transaction, you need to provide the sender's address, the receiver's address, and the amount to transfer. You can create a payment transaction using the following code:
```rust
use zksync::zks_provider::ZKSProvider;

let receiver_address: zksync::Address = "0xa61464658AfeAf65CccaaFD3a512b69A83B77618".parse().unwrap();
let amount_to_transfer = zksync::U256::from(1);

let mut payment_request = zksync::zks_wallet::TransferRequest::new(amount_to_transfer)
.to(receiver_address)
.from(sender_address); // Use zk_wallet.l2_address() method to send it from the wallet address.
```
#### Sending the Transaction
To send the payment transaction, you need to use the wallet and the transfer parameters. You can send the transaction using the following code:
To send the payment transaction, you need to use the wallet and the transfer request. You can send the transaction using the following code:
> In case you are wondering, the transaction is signed in the `send_transaction` method inside the transfer process.
```rust
let pending_payment =
zk_wallet.transfer(receiver_address, amount_to_transfer, None).await.unwrap();
let payment_transaction_id =
zk_wallet.transfer(payment_request, None).await.unwrap();
```
This will send the transaction to the node but the transaction will not be mined until we `await` on it. That will resolve to a `TransactionReceipt` confirming that the transfer went fine.
This will send the transaction to the node and return its ID (hash). To get more information about the transaction we can ask for the `TransactionReceipt` with the following lines:
```rust
let payment_response: zksync::TransactionReceipt = pending_payment.await.unwrap().unwrap();
let payment_transaction_receipt = provider
.get_transaction_receipt(payment_transaction_id)
.await
.unwrap()
.unwrap();
```
#### Checking zkSync account balance
Expand Down
20 changes: 14 additions & 6 deletions examples/simple_payment/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use ethers::{
prelude::k256::ecdsa::SigningKey,
providers::{Middleware, Provider},
signers::{Signer, Wallet},
types::{TransactionReceipt, U256},
types::U256,
};
use zksync_web3_rs::ZKSWallet;
use zksync_web3_rs::{zks_wallet::TransferRequest, ZKSWallet};

// It is set so that the transaction is replay-protected (EIP-155)
// https://era.zksync.io/docs/api/hardhat/testing.html#connect-wallet-to-local-nodes
Expand Down Expand Up @@ -53,6 +53,11 @@ async fn main() {

let zk_wallet = ZKSWallet::new(signer, None, Some(provider.clone()), None).unwrap();

/* Payment transaction building */
let payment_request = TransferRequest::new(amount).to(args.to).from(args.from);

log::debug!("{:?}", payment_request);

/* Sending the payment transaction */

log::debug!(
Expand All @@ -64,11 +69,14 @@ async fn main() {
provider.get_balance(args.to, None).await.unwrap()
);

let pending_payment_transaction = zk_wallet.transfer(args.to, amount, None).await.unwrap();
let payment_transaction_id = zk_wallet.transfer(&payment_request, None).await.unwrap();
let payment_transaction_receipt = provider
.get_transaction_receipt(payment_transaction_id)
.await
.unwrap()
.unwrap();

/* Waiting for the payment transaction */
let payment_response: TransactionReceipt = pending_payment_transaction.await.unwrap().unwrap();
log::info!("{:?}", payment_response);
log::info!("{:?}", payment_transaction_receipt);

log::debug!(
"Sender's balance after paying: {:?}",
Expand Down
107 changes: 104 additions & 3 deletions src/eip712/transaction_request.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
use super::{rlp_append_option, Eip712Meta};
use std::{fs::File, io::BufReader, path::PathBuf, str::FromStr};

use super::{hash_bytecode, rlp_append_option, Eip712Meta};
use crate::{
zks_utils::{EIP712_TX_TYPE, ERA_CHAIN_ID, MAX_PRIORITY_FEE_PER_GAS},
zks_wallet::Overrides,
zks_utils::{
self, CONTRACT_DEPLOYER_ADDR, EIP712_TX_TYPE, ERA_CHAIN_ID, MAX_PRIORITY_FEE_PER_GAS,
},
zks_wallet::{DeployRequest, Overrides, TransferRequest, WithdrawRequest, ZKRequestError},
};
use ethers::{
abi::{Abi, HumanReadableParser, ParseError},
types::{
transaction::{eip2930::AccessList, eip712::Eip712Error},
Address, Bytes, Signature, U256,
},
utils::rlp::{Encodable, RlpStream},
};
use ethers_contract::encode_function_data;
use serde::{Deserialize, Serialize};

// TODO: Not all the fields are optional. This was copied from the JS implementation.
Expand Down Expand Up @@ -233,3 +239,98 @@ impl Default for Eip712TransactionRequest {
}
}
}

impl TryFrom<WithdrawRequest> for Eip712TransactionRequest {
type Error = ZKRequestError;

fn try_from(request: WithdrawRequest) -> Result<Self, Self::Error> {
let contract_address =
Address::from_str(zks_utils::CONTRACTS_L2_ETH_TOKEN_ADDR).map_err(|e| {
ZKRequestError::CustomError(format!("Error getting L2 ETH token address {e:?}"))
})?;
let function_signature = "function withdraw(address _l1Receiver) external payable override";
let function = HumanReadableParser::parse_function(function_signature)
.map_err(ParseError::LexerError)?;
let function_args = function.decode_input(&zks_utils::encode_args(
&function,
&[format!("{:?}", request.to)],
)?)?;
let data: Bytes = function.encode_input(&function_args)?.into();

Ok(Eip712TransactionRequest::new()
.r#type(EIP712_TX_TYPE)
.to(contract_address)
.value(request.amount)
.from(request.from)
.data(data))
}
}

impl From<TransferRequest> for Eip712TransactionRequest {
fn from(request: TransferRequest) -> Self {
Eip712TransactionRequest::new()
.r#type(EIP712_TX_TYPE)
.to(request.to)
.value(request.amount)
.from(request.from)
}
}

impl TryFrom<DeployRequest> for Eip712TransactionRequest {
type Error = ZKRequestError;

fn try_from(request: DeployRequest) -> Result<Self, Self::Error> {
let mut contract_deployer_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
contract_deployer_path.push("src/abi/ContractDeployer.json");

let custom_data = Eip712Meta::new().factory_deps({
let mut factory_deps = Vec::new();
if let Some(factory_dependencies) = request.factory_deps {
factory_deps.extend(factory_dependencies);
}
factory_deps.push(request.contract_bytecode.clone());
factory_deps
});

let contract_deployer = Abi::load(BufReader::new(
File::open(contract_deployer_path).map_err(|e| {
ZKRequestError::CustomError(format!(
"Error opening contract deployer abi file {e:?}"
))
})?,
))?;
let create = contract_deployer.function("create")?;

// TODO: User could provide this instead of defaulting.
let salt = [0_u8; 32];
let bytecode_hash = hash_bytecode(&request.contract_bytecode).map_err(|e| {
ZKRequestError::CustomError(format!("Error hashing contract bytecode {e:?}"))
})?;
let call_data: Bytes = match (
request.contract_abi.constructor(),
request.constructor_parameters.is_empty(),
) {
(None, false) => {
return Err(ZKRequestError::CustomError(
"Constructor not present".to_owned(),
))
}
(None, true) | (Some(_), true) => Bytes::default(),
(Some(constructor), false) => {
zks_utils::encode_constructor_args(constructor, &request.constructor_parameters)?
.into()
}
};

let data = encode_function_data(create, (salt, bytecode_hash, call_data))?;

let contract_deployer_address = Address::from_str(CONTRACT_DEPLOYER_ADDR).map_err(|e| {
ZKRequestError::CustomError(format!("Error getting contract deployer address {e:?}"))
})?;
Ok(Eip712TransactionRequest::new()
.r#type(EIP712_TX_TYPE)
.to(contract_deployer_address)
.custom_data(custom_data)
.data(data))
}
}
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ pub mod contracts;
pub mod eip712;
pub mod zks_provider;
pub mod zks_utils;

pub mod zks_wallet;
pub use zks_wallet::{DepositRequest, ZKSWallet, ZKSWalletError};

pub use zks_wallet::{ZKSWallet, ZKSWalletError};

// For macro expansions only, not public API.
#[allow(unused_extern_crates)]
Expand Down
Loading

0 comments on commit e0ef89e

Please sign in to comment.