Skip to content
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

Improve API consistency #85

Merged
merged 30 commits into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b824eb2
Add withdraw request builder
IAvecilla Jul 11, 2023
710c4b7
New module for specific transaction requests
IAvecilla Jul 11, 2023
ec28030
Improve return type on send functions
IAvecilla Jul 12, 2023
8199608
Delete duplicated code to get providers
IAvecilla Jul 12, 2023
2ef7d7a
Add function to provider to send eip712 transactions
IAvecilla Jul 12, 2023
981cfe3
Add from trait for eip712 transactions with every request type
IAvecilla Jul 13, 2023
c669388
Revert returning transaction hash from send functions
IAvecilla Jul 13, 2023
bd40b5b
Add transfer request type
IAvecilla Jul 13, 2023
01789f8
Add defaults for withdraw request
IAvecilla Jul 13, 2023
cb2f4f1
Update wallet to use every transaction type
IAvecilla Jul 13, 2023
9e9d92f
Add deploy request implementation
IAvecilla Jul 14, 2023
3cbd7b0
Use deploy request in wallet deploy function
IAvecilla Jul 14, 2023
5ab4ec7
Add call request implementation
IAvecilla Jul 14, 2023
b29a21a
Fix unused imports
IAvecilla Jul 14, 2023
01d399f
Fix deploy test
IAvecilla Jul 14, 2023
85818be
Fix clippy lints
IAvecilla Jul 18, 2023
6d3a8b3
Add request conversion error
IAvecilla Jul 18, 2023
aa399f4
Fix unused import
IAvecilla Jul 18, 2023
760cd5d
Update README and payment example
IAvecilla Jul 18, 2023
f33c543
Return contract address in deploy function
IAvecilla Jul 20, 2023
cda5391
Remove unnecesary sleeps in tests
IAvecilla Jul 20, 2023
eef156c
Merge branch 'main' into improve_api
IAvecilla Jul 20, 2023
be1b6b8
Merge branch 'main' into improve_api
IAvecilla Jul 24, 2023
d510941
Update request builder methods
IAvecilla Jul 25, 2023
75ec65d
Change return type to pending transaction
IAvecilla Jul 25, 2023
97b5125
Return transaction ID in every wallet operation
IAvecilla Jul 25, 2023
995813a
Update README and payment example
IAvecilla Jul 25, 2023
2e30826
Empty commit
IAvecilla Aug 28, 2023
238066b
Merge branch 'main' into improve_api
IAvecilla Aug 28, 2023
79e727a
Merge branch 'main' into improve_api
IAvecilla Aug 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since a WithdrawRequest is a subset of Eip712TransactionRequests, shouldn't we implement a TryInto in that module? (following the principle that Eip712TransactionRequest shouldn't know about its children structures.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TryFrom function already implements the functionality of TryInto, but the reverse isn't necessarily true. By implementing TryFrom or From, you automatically gain the corresponding TryInto or Into trait respectively. This behavior is outlined in the Rust documentation on this trait.


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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as my comment for WithdrawRequests

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

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as my comment for WithdrawRequests


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
Loading