Skip to content

Commit

Permalink
feat: l1 to l2 erc20 deposit (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
vasyl-ivanchuk authored Dec 20, 2024
1 parent 4554f91 commit 21edd3a
Show file tree
Hide file tree
Showing 17 changed files with 827 additions and 225 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
uses: mozilla-actions/[email protected]

- name: Install anvil-zksync
uses: dutterbutter/anvil-zksync-action@v1.0.0
uses: dutterbutter/anvil-zksync-action@v1.1.0

- name: Format
run: cargo fmt --all -- --check
Expand Down
69 changes: 69 additions & 0 deletions examples/deposit_erc20.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use alloy::{
network::EthereumWallet,
primitives::{address, U256},
providers::ProviderBuilder,
signers::local::PrivateKeySigner,
};
use alloy_zksync::{
provider::{zksync_provider, DepositRequest, ZksyncProviderWithWallet},
wallet::ZksyncWallet,
};
use anyhow::Result;

#[tokio::main]
async fn main() -> Result<()> {
// standard RPC urls for the L1 and L2 local nodes spun up by ZKSync CLI:
// More general info on the local setup can be found here:
// https://docs.zksync.io/zksync-era/tooling/local-setup/dockerized-l1-l2-nodes
// and how to spin it up locally:
// https://docs.zksync.io/zksync-era/tooling/zksync-cli/running-a-node
let l1_rpc_url = "http://127.0.0.1:8545".parse()?;
let l2_rpc_url = "http://127.0.0.1:3050".parse()?;
// one of the test rich wallets created by the local setup
// https://github.com/matter-labs/local-setup/blob/main/rich-wallets.json
let signer: PrivateKeySigner =
"0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110"
.parse()
.expect("should parse private key");
let wallet = EthereumWallet::from(signer.clone());

let l1_provider = ProviderBuilder::new()
.with_recommended_fillers()
.wallet(wallet)
.on_http(l1_rpc_url);

let zksync_wallet: ZksyncWallet = ZksyncWallet::from(signer.clone());
let zksync_provider = zksync_provider()
.with_recommended_fillers()
.wallet(zksync_wallet)
.on_http(l2_rpc_url);

// use another test rich wallet as a receiver
// https://github.com/matter-labs/local-setup/blob/main/rich-wallets.json
let receiver = address!("a61464658AfeAf65CccaaFD3a512b69A83B77618");
// 0.00007 tokens
let deposit_amount = U256::from(70000000000000_u64);
let deposit_l1_receipt = zksync_provider
.deposit(
&DepositRequest::new(deposit_amount)
.with_receiver(receiver)
.with_token(address!("f10A110E59a22b444c669C83b02f0E6d945b2b69")),
// use with_bridge_address to specify custom bridge address for the deposit
//.with_bridge_address(address!("785185bbac3a09d447c679cf3420b206ea90be88")),
// disable tokens auto approval if you plan to manage tokens allowance manually
//.with_auto_approval(false),
&l1_provider,
)
.await
.unwrap();

let deposit_l2_receipt = deposit_l1_receipt
.get_l2_tx()?
.with_required_confirmations(1)
.with_timeout(Some(std::time::Duration::from_secs(60 * 5)))
.get_receipt()
.await?;

println!("L2 deposit transaction receipt: {:#?}", deposit_l2_receipt);
Ok(())
}
8 changes: 5 additions & 3 deletions examples/deposit.rs → examples/deposit_eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use alloy::{
signers::local::PrivateKeySigner,
};
use alloy_zksync::{
provider::{zksync_provider, ZksyncProviderWithWallet, ETHER_L1_ADDRESS},
provider::{zksync_provider, DepositRequest, ZksyncProviderWithWallet},
wallet::ZksyncWallet,
};
use anyhow::Result;
Expand Down Expand Up @@ -43,9 +43,11 @@ async fn main() -> Result<()> {
let receiver = address!("a61464658AfeAf65CccaaFD3a512b69A83B77618");
// 0.00007 ETH
let deposit_amount = U256::from(70000000000000_u64);
let l1_token_address = ETHER_L1_ADDRESS;
let deposit_l1_receipt = zksync_provider
.deposit(l1_token_address, receiver, deposit_amount, &l1_provider)
.deposit(
&DepositRequest::new(deposit_amount).with_receiver(receiver),
&l1_provider,
)
.await
.unwrap();

Expand Down
44 changes: 44 additions & 0 deletions src/contracts/common/erc20.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use alloy::{
contract::Error,
dyn_abi::DynSolValue,
network::Network,
primitives::{Bytes, U256},
transports::Transport,
};
use ERC20::ERC20Instance;

alloy::sol! {
#[sol(rpc)]
contract ERC20 {
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 value) external returns (bool);

function name() public view virtual returns (string memory);
function symbol() public view virtual returns (string memory);
function decimals() public view virtual returns (uint8);
}
}

pub(crate) async fn encode_token_data_for_bridge<T, P, N>(
erc20_contract: &ERC20Instance<T, P, N>,
) -> Result<Bytes, Error>
where
T: Transport + Clone,
P: alloy::providers::Provider<T, N>,
N: Network,
{
let erc20_name = erc20_contract.name().call().await?._0;
let erc20_symbol = erc20_contract.symbol().call().await?._0;
let erc20_decimals = erc20_contract.decimals().call().await?._0;

let token_data = Bytes::from(
DynSolValue::Tuple(vec![
DynSolValue::Bytes(DynSolValue::String(erc20_name).abi_encode()),
DynSolValue::Bytes(DynSolValue::String(erc20_symbol).abi_encode()),
DynSolValue::Bytes(DynSolValue::Uint(U256::from(erc20_decimals), 256).abi_encode()),
])
.abi_encode_params(),
);

Ok(token_data)
}
1 change: 1 addition & 0 deletions src/contracts/common/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod erc20;
17 changes: 17 additions & 0 deletions src/contracts/l1/bridge_hub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,19 @@ alloy::sol! {
address refundRecipient;
}

#[allow(missing_docs)]
struct L2TransactionRequestTwoBridges {
uint256 chainId;
uint256 mintValue;
uint256 l2Value;
uint256 l2GasLimit;
uint256 l2GasPerPubdataByteLimit;
address refundRecipient;
address secondBridgeAddress;
uint256 secondBridgeValue;
bytes secondBridgeCalldata;
}

#[allow(missing_docs)]
struct L2CanonicalTransaction {
uint256 txType;
Expand Down Expand Up @@ -39,6 +52,10 @@ alloy::sol! {
L2TransactionRequestDirect memory request
) external payable returns (bytes32 canonicalTxHash);

function requestL2TransactionTwoBridges(
L2TransactionRequestTwoBridges calldata _request
) external payable returns (bytes32 canonicalTxHash);

function l2TransactionBaseCost(
uint256 _chainId,
uint256 _gasPrice,
Expand Down
25 changes: 25 additions & 0 deletions src/contracts/l1/l1_bridge.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use alloy::{
dyn_abi::DynSolValue,
primitives::{Address, Bytes, U256},
};
alloy::sol! {
#[sol(rpc)]
contract L1Bridge {
function l2BridgeAddress(uint256 _chainId) external view returns (address);
}
}

pub(crate) fn encode_deposit_token_calldata(
token: Address,
amount: U256,
receiver: Address,
) -> Bytes {
Bytes::from(
DynSolValue::Tuple(vec![
DynSolValue::Address(token),
DynSolValue::Uint(amount, 256),
DynSolValue::Address(receiver),
])
.abi_encode_params(),
)
}
1 change: 1 addition & 0 deletions src/contracts/l1/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod bridge_hub;
pub mod l1_bridge;
36 changes: 36 additions & 0 deletions src/contracts/l2/l2_bridge.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use alloy::{
primitives::{Address, Bytes, U256},
sol_types::SolCall,
};
use L2Bridge::finalizeDepositCall;

alloy::sol! {
#[sol(rpc)]
contract L2Bridge {
function finalizeDeposit(
address _l1Sender,
address _l2Receiver,
address _l1Token,
uint256 _amount,
bytes calldata _data
);
}
}

pub(crate) fn encode_finalize_deposit_calldata(
sender: Address,
receiver: Address,
l1_token_address: Address,
amount: U256,
token_data: Bytes,
) -> Bytes {
let call = finalizeDepositCall {
_l1Sender: sender,
_l2Receiver: receiver,
_l1Token: l1_token_address,
_amount: amount,
_data: token_data,
};

call.abi_encode().into()
}
1 change: 1 addition & 0 deletions src/contracts/l2/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod contract_deployer;
pub mod l2_bridge;
1 change: 1 addition & 0 deletions src/contracts/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod common;
pub mod l1;
pub mod l2;
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ pub mod network;
pub mod node_bindings;
pub mod provider;
pub mod types;
pub mod utils;
pub mod wallet;
Loading

0 comments on commit 21edd3a

Please sign in to comment.