Skip to content

Commit

Permalink
Add multiwithdraw token
Browse files Browse the repository at this point in the history
Signed-off-by: deniallugo <[email protected]>
  • Loading branch information
Deniallugo committed May 13, 2022
1 parent e7d689e commit bf5ee9c
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 50 deletions.
44 changes: 44 additions & 0 deletions contracts/contracts/dev-contracts/PendingBalanceWithdrawer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.7.0;

pragma experimental ABIEncoderV2;

import "../ZkSync.sol";

contract PendingBalanceWithdrawer {
ZkSync constant zkSync = ZkSync($(ZKSYNC_ADDRESS));

struct RequestWithdrawFT {
address payable owner;
address token;
uint256 gas;
}

struct RequestWithdrawNFT {
uint32 tokenId;
uint256 gas;
}

function withdrawPendingBalances(RequestWithdrawFT[] calldata _FTRequests, RequestWithdrawNFT[] calldata _NFTRequests)
external
{
for (uint256 i = 0; i < _FTRequests.length; ++i) {
try
zkSync.withdrawPendingBalance{gas: _FTRequests[i].gas}(
_FTRequests[i].owner,
_FTRequests[i].token,
type(uint128).max
)
{} catch {}
}

for (uint256 i = 0; i < _NFTRequests.length; ++i) {
try
zkSync.withdrawPendingNFTBalance{gas: _NFTRequests[i].gas}(
_NFTRequests[i].tokenId
)
{} catch {}
}
}
}
7 changes: 7 additions & 0 deletions contracts/scripts/deploy-testkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ async function main() {
const testWallet = Wallet.fromMnemonic(ethTestConfig.test_mnemonic, "m/44'/60'/0'/0/" + i).connect(provider);
await (await erc20.mint(testWallet.address, '0x4B3B4CA85A86C47A098A224000000000')).wait();
}
const pendingWithdrawer = await deployContract(
deployWallet,
readContractCode('dev-contracts/PendingBalanceWithdrawer'),
[],
{ gasLimit: 5000000 }
);
console.log(`CONTRACTS_PENDING_BALANCE_WITHDRAWER=${pendingWithdrawer.address}`);
}

main()
Expand Down
94 changes: 48 additions & 46 deletions core/tests/testkit/src/eth_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::external_commands::js_revert_reason;
use std::collections::HashMap;

use anyhow::{bail, ensure, format_err};
use ethabi::{Token, Uint};
use ethabi::{Contract, Token, Uint};
use num::{BigUint, ToPrimitive};
use std::convert::TryFrom;
use std::str::FromStr;
Expand Down Expand Up @@ -435,57 +435,59 @@ impl EthereumAccount {
&self,
execute_operation: &BlocksExecuteOperation,
tokens: &HashMap<TokenId, Address>,
pending_withdrawer_contract: &(Contract, Address),
) -> Result<Option<ETHExecResult>, anyhow::Error> {
if let Some(ex_op) = execute_operation
let ex_ops: Vec<_> = execute_operation
.blocks
.first()
.unwrap()
.block_transactions
.iter()
.find(|a| a.is_successful() && (a.variance_name() == "Withdraw"))
{
// let address = self.main_contract_eth_client.call_contract_function().await;
.flat_map(|b| {
b.block_transactions
.iter()
.filter(|a| a.is_successful() && (a.variance_name() == "Withdraw"))
})
.collect();
let mut ft_balances = vec![];
let mut nft_balances = vec![];
for ex_op in ex_ops {
let ex_op = ex_op.get_executed_tx().unwrap().signed_tx.clone().tx;
let (func_name, params) = match ex_op {
ZkSyncTx::Withdraw(tx) => (
"withdrawPendingBalance",
vec![
Token::Address(tx.to),
Token::Address(tokens.get(&tx.token).unwrap().clone()),
Token::Uint(Uint::from_str(&tx.amount.to_string()).unwrap()),
],
),
ZkSyncTx::WithdrawNFT(tx) => (
"withdrawPendingNFTBalance",
vec![Token::Uint(
Uint::from_str(&tx.token.0.to_string()).unwrap(),
)],
),
match ex_op {
ZkSyncTx::Withdraw(tx) => ft_balances.push(Token::Tuple(vec![
Token::Address(tx.to),
Token::Address(tokens.get(&tx.token).unwrap().clone()),
Token::Uint(Uint::from(200_000)),
])),
ZkSyncTx::WithdrawNFT(tx) => nft_balances.push(Token::Tuple(vec![
Token::Uint(Uint::from_str(&tx.token.0.to_string()).unwrap()),
Token::Uint(Uint::from(300_000)),
])),
_ => unreachable!(),
};
dbg!(&params);
let data = self
.main_contract_eth_client
.encode_tx_data(func_name, params.as_slice());

let signed_tx = self
.main_contract_eth_client
.sign_prepared_tx(
data,
Options::with(|f| f.gas = Some(U256::from(9 * 10u64.pow(6)))),
)
.await
.map_err(|e| format_err!("Complete withdrawals send err: {}", e))?;
let receipt =
send_raw_tx_wait_confirmation(&self.main_contract_eth_client, signed_tx.raw_tx)
.await?;

Ok(Some(
ETHExecResult::new(receipt, &self.main_contract_eth_client).await,
))
} else {
Ok(None)
}

if ft_balances.is_empty() && nft_balances.is_empty() {
return Ok(None);
}

let f = pending_withdrawer_contract
.0
.function("withdrawPendingBalances")
.expect("failed to get function parameters");

let tokens = vec![Token::Array(ft_balances), Token::Array(nft_balances)];
let data = f
.encode_input(&tokens)
.expect("failed to encode parameters");
let signed_tx = self
.main_contract_eth_client
.sign_prepared_tx_for_addr(data, pending_withdrawer_contract.1, Options::default())
.await
.map_err(|e| format_err!("Complete withdrawals send err: {}", e))?;

let receipt =
send_raw_tx_wait_confirmation(&self.main_contract_eth_client, signed_tx.raw_tx).await?;
Ok(Some(
ETHExecResult::new(receipt, &self.main_contract_eth_client).await,
))
}

pub async fn revert_blocks(&self, blocks: &[Block]) -> Result<ETHExecResult, anyhow::Error> {
Expand Down Expand Up @@ -548,7 +550,7 @@ impl EthereumAccount {
#[derive(Debug, Clone)]
pub struct ETHExecResult {
success: bool,
receipt: TransactionReceipt,
pub receipt: TransactionReceipt,
revert_reason: String,
}

Expand Down
19 changes: 19 additions & 0 deletions core/tests/testkit/src/external_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
//! `zk` script should be in path.
//!
use std::collections::HashMap;
use std::fs::read;
use std::process::Command;
use std::str::FromStr;
use web3::types::{Address, H256};

use serde::{Deserialize, Serialize};
use serde_json::Value;
use zksync_crypto::convert::FeConvert;
use zksync_crypto::Fr;

Expand All @@ -17,6 +19,7 @@ pub struct Contracts {
pub contract: Address,
pub upgrade_gatekeeper: Address,
pub test_erc20_address: Address,
pub pending_withdrawer: (ethabi::Contract, Address),
}

fn get_contract_address(deploy_script_out: &str) -> Option<(String, Address)> {
Expand All @@ -42,6 +45,13 @@ fn get_contract_address(deploy_script_out: &str) -> Option<(String, Address)> {
String::from("CONTRACTS_UPGRADE_GATEKEEPER_ADDR"),
Address::from_str(output).expect("can't parse contract address"),
))
} else if let Some(output) =
deploy_script_out.strip_prefix("CONTRACTS_PENDING_BALANCE_WITHDRAWER=0x")
{
Some((
String::from("CONTRACTS_PENDING_BALANCE_WITHDRAWER"),
Address::from_str(output).expect("can't parse contract address"),
))
} else {
deploy_script_out
.strip_prefix("CONTRACTS_TEST_ERC20=0x")
Expand Down Expand Up @@ -103,6 +113,9 @@ pub fn deploy_contracts(use_prod_contracts: bool, genesis_root: Fr) -> Contracts
contracts.insert(name, address);
}
}
let pending_withdrawer_abi: Value = serde_json::from_slice( read("contracts/artifacts/cache/solpp-generated-contracts/dev-contracts/PendingBalanceWithdrawer.sol/PendingBalanceWithdrawer.json").unwrap().as_slice()).unwrap();
let contract =
serde_json::from_value(pending_withdrawer_abi.get("abi").unwrap().clone()).unwrap();

Contracts {
governance: contracts
Expand All @@ -120,6 +133,12 @@ pub fn deploy_contracts(use_prod_contracts: bool, genesis_root: Fr) -> Contracts
test_erc20_address: contracts
.remove("CONTRACTS_TEST_ERC20")
.expect("TEST_ERC20 missing"),
pending_withdrawer: (
contract,
contracts
.remove("CONTRACTS_PENDING_BALANCE_WITHDRAWER")
.expect("CONTRACTS_PENDING_BALANCE_WITHDRAWER missing"),
),
}
}

Expand Down
2 changes: 2 additions & 0 deletions core/tests/testkit/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
extern crate core;

use zksync_types::{Account, AccountId, Address};

pub use self::{
Expand Down
30 changes: 26 additions & 4 deletions core/tests/testkit/src/test_setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pub struct TestSetup {

pub accounts: AccountSet,
pub tokens: HashMap<TokenId, Address>,
pub deployed_contracts: Contracts,

pub expected_changes_for_current_block: ExpectedAccountState,

Expand Down Expand Up @@ -94,6 +95,7 @@ impl TestSetup {
processed_tx_events_receiver: sk_channels.queued_txs_events,
accounts,
tokens,
deployed_contracts: deployed_contracts.clone(),
expected_changes_for_current_block: ExpectedAccountState::default(),
commit_account,
current_state_root: Some(initial_root),
Expand Down Expand Up @@ -996,6 +998,7 @@ impl TestSetup {
.await
.expect("block commit send tx")
.expect_success();

self.last_committed_block = new_block.clone();

new_block
Expand Down Expand Up @@ -1081,7 +1084,6 @@ impl TestSetup {
.send(StateKeeperTestkitRequest::SealBlock)
.await
.expect("sk receiver dropped");

let new_block = self.await_for_block_commit().await;
self.current_state_root = Some(new_block.new_root_hash);

Expand Down Expand Up @@ -1120,12 +1122,32 @@ impl TestSetup {
.expect("execute block tx")
.expect_success();

let pending_withdrawals_result = self
let pending_withdrawals_result = if let Some(a) = self
.commit_account
.execute_pending_withdrawals(&block_execute_op, &self.tokens)
.execute_pending_withdrawals(
&block_execute_op,
&self.tokens,
&self.deployed_contracts.pending_withdrawer,
)
.await
.expect("execute block tx")
.map(|a| a.expect_success());
{
dbg!(&a);
let tx_hash = a.receipt.transaction_hash;
let res = if let Ok(rec) = a.success_result() {
rec
} else {
let failure_reason = self
.commit_account
.main_contract_eth_client
.failure_reason(tx_hash)
.await;
panic!("Failure {:?}", failure_reason);
};
Some(res)
} else {
None
};

self.last_committed_block = new_block.clone();

Expand Down

0 comments on commit bf5ee9c

Please sign in to comment.