diff --git a/tests/test_paymaster.py b/tests/test_paymaster.py index 2d6a1ec..8298871 100644 --- a/tests/test_paymaster.py +++ b/tests/test_paymaster.py @@ -4,6 +4,8 @@ from eth_utils import keccak, remove_0x_prefix from web3 import Web3 from web3.middleware import geth_poa_middleware + +import zksync2.transaction.transaction712 from zksync2.provider.eth_provider import EthereumProvider from zksync2.manage_contracts.paymaster_utils import PaymasterFlowEncoder @@ -18,6 +20,14 @@ from zksync2.transaction.transaction712 import TxCreate2Contract, TxFunctionCall +# mint tx hash of Test coins: +# https://goerli.explorer.zksync.io/tx/0xaaafc0e4228336783ad4e996292123c6eebb36b5c1b257e5b628e7e0281cccab + +# https://goerli.explorer.zksync.io/tx/0xea09a90d09aa3644791ef300b5b5bbf397723812542a5040ff50237472e549fa +# https://goerli.explorer.zksync.io/tx/0x86b1b6361cfe54dd54b2968fbfe97429d8876994a0240b27962ad9869acd512f +# https://goerli.explorer.zksync.io/tx/0x78cf6db673a941495c6887badb84b4150bc09c3962a76c0d5c239190151e1ec5 +# SERC20 + class PaymasterTests(TestCase): ETH_TEST_URL = "https://rpc.ankr.com/eth_goerli" ZKSYNC_TEST_URL = "https://zksync2-testnet.zksync.dev" @@ -27,11 +37,12 @@ class PaymasterTests(TestCase): ETH_AMOUNT_BALANCE = 1 ETH_TEST_NET_AMOUNT_BALANCE = Decimal(1) - USDC_TOKEN = Token( - Web3.toChecksumAddress("0xd35cceead182dcee0f148ebac9447da2c4d449c4"), - Web3.toChecksumAddress("0x852a4599217e76aa725f0ada8bf832a1f57a8a91"), - "USDC", - 6) + SERC20_TOKEN = Token( + Web3.toChecksumAddress("0x" + "0" * 40), + Web3.toChecksumAddress("0xFC174650BDEbE4D94736442307D4D7fdBe799EeC"), + "SERC20", + 18) + SALT = keccak(text="TestPaymaster") def setUp(self) -> None: @@ -43,21 +54,6 @@ def setUp(self) -> None: self.custom_paymaster_contract_bin = get_hex_binary("custom_paymaster_binary.hex") - def test_deposit_usdc(self): - amount_usdc = 100000 - eth_web3 = Web3(Web3.HTTPProvider(self.ETH_TEST_URL)) - eth_web3.middleware_onion.inject(geth_poa_middleware, layer=0) - eth_provider = EthereumProvider.build_ethereum_provider(zksync=self.web3, - eth=eth_web3, - account=self.account, - gas_provider=self.gas_provider) - is_approved = eth_provider.approve_deposits(self.USDC_TOKEN, amount_usdc) - self.assertTrue(is_approved) - tx_receipt = eth_provider.deposit(self.USDC_TOKEN, - amount_usdc, - self.account.address) - self.assertEqual(1, tx_receipt["status"]) - def _is_deployed(self): return len(self.web3.zksync.get_code(self.paymaster_address)) > 0 @@ -65,7 +61,7 @@ def _is_deployed(self): def paymaster_address(self) -> HexStr: return self.web3.zksync.zks_get_testnet_paymaster_address() - # @skip("Integration test, paymaster params test not implemented yet") + @skip("Integration test, paymaster params test not implemented yet") def test_deploy_paymaster(self): if not self._is_deployed(): nonce = self.web3.zksync.get_transaction_count(self.account.address, EthBlockParams.PENDING.value) @@ -110,6 +106,7 @@ def get_balance(self, addr: HexStr, token: Token, at: EthBlockParams = EthBlockP gas_provider=self.gas_provider) return erc20.balance_of(addr) + @skip("Integration test, paymaster params test not implemented yet") def test_send_funds_for_fee(self): gas_price = self.web3.zksync.gas_price func_call = TxFunctionCall(self.chain_id, @@ -120,12 +117,12 @@ def test_send_funds_for_fee(self): estimate_gas = self.web3.zksync.eth_estimate_gas(func_call.tx) fee = estimate_gas * gas_price print(f"Fee : {fee}") - usdc_balance = self.get_balance(self.account.address, self.USDC_TOKEN) - print(f"USDC balance: {usdc_balance}") + serc20_balance = self.get_balance(self.account.address, self.SERC20_TOKEN) + print(f"SERC20 balance: {serc20_balance}") - self.assertTrue(usdc_balance >= fee, f"Not enough balance for pay fee {fee} with balance {usdc_balance}") + self.assertTrue(serc20_balance >= fee, f"Not enough balance for pay fee {fee} with balance {serc20_balance}") - nonce = self.web3.zksync.get_transaction_count(self.account.address, ZkBlockParams.COMMITTED) + nonce = self.web3.zksync.get_transaction_count(self.account.address, ZkBlockParams.COMMITTED.value) transfer_for_fee = TxFunctionCall(chain_id=self.chain_id, nonce=nonce, from_=self.account.address, @@ -141,43 +138,59 @@ def test_send_funds_for_fee(self): tx_receipt = self.web3.zksync.wait_for_transaction_receipt(tx_hash, timeout=240, poll_latency=0.5) self.assertEqual(1, tx_receipt["status"]) + def build_paymaster(self, trans: TxFunctionCall, fee: int) -> TxFunctionCall: + paymaster_encoder = PaymasterFlowEncoder(self.web3) + encoded_approval_base = paymaster_encoder.encode_approval_based(self.SERC20_TOKEN.l2_address, + fee, + b'') + encoded_approval_bin = bytes.fromhex(remove_0x_prefix(encoded_approval_base)) + trans.tx["eip712Meta"].paymaster_params = PaymasterParams(paymaster=self.paymaster_address, + paymaster_input=encoded_approval_bin) + return trans + + @skip("Integration test, paymaster params test not implemented yet") def test_send_funds_with_paymaster(self): gas_price = self.web3.zksync.gas_price paymaster_address = self.paymaster_address - nonce = self.web3.zksync.get_transaction_count(self.account.address, EthBlockParams.PENDING.value) transaction = TxFunctionCall(self.chain_id, nonce=nonce, from_=self.account.address, to=self.account.address, gas_price=gas_price) - est_gas = self.web3.zksync.eth_estimate_gas(transaction.tx) - fee = est_gas * gas_price - balance = self.web3.zksync.get_balance(paymaster_address, EthBlockParams.LATEST) - self.assertTrue(balance > fee, "Not enough balance to process fee") - erc20 = ERC20Contract(self.web3, contract_address=self.USDC_TOKEN.l2_address, account=self.account) + # INFO: encode paymaster params with dummy fee to estimate real one + unknown_fee = 0 + transaction = self.build_paymaster(transaction, unknown_fee) + + paymaster_est_gas = self.web3.zksync.eth_estimate_gas(transaction.tx) + preprocessed_fee = gas_price * paymaster_est_gas + + print(f"Paymaster fee: {preprocessed_fee}") + + erc20 = ERC20Contract(self.web3.zksync, contract_address=self.SERC20_TOKEN.l2_address, + account=self.account, + gas_provider=self.gas_provider) allowance = erc20.allowance(self.account.address, paymaster_address) - if allowance < fee: - tx_receipt = erc20.approve_deposit(paymaster_address, fee) - self.assertEqual(1, tx_receipt["status"]) + if allowance < preprocessed_fee: + is_approved = erc20.approve_deposit(paymaster_address, preprocessed_fee) + self.assertTrue(is_approved) - balance_before = self.web3.zksync.get_balance(self.account.address, EthBlockParams.PENDING) - paymaster_encoder = PaymasterFlowEncoder(self.web3) - encoded_approval_base = paymaster_encoder.encode_approval_based(self.USDC_TOKEN.l2_address, - fee, - b'') - encoded_approval_bin = bytes.fromhex(remove_0x_prefix(encoded_approval_base)) - transaction.tx["eip712Meta"].paymaster_params = PaymasterParams(paymaster=paymaster_address, - paymaster_input=encoded_approval_bin) - tx712 = transaction.tx712(est_gas) + # INFO: encode paymaster params with real fee + transaction = self.build_paymaster(transaction, preprocessed_fee) + + balance_before = self.web3.zksync.get_balance(self.account.address, EthBlockParams.PENDING.value) + print(f"balance before : {balance_before}") + + tx712 = transaction.tx712(paymaster_est_gas) singed_message = self.signer.sign_typed_data(tx712.to_eip712_struct()) msg = tx712.encode(singed_message) tx_hash = self.web3.zksync.send_raw_transaction(msg) tx_receipt = self.web3.zksync.wait_for_transaction_receipt(tx_hash, timeout=240, poll_latency=0.5) self.assertEqual(1, tx_receipt["status"]) - balance_after = self.web3.zksync.get_balance(self.account.address, EthBlockParams.PENDING) - self.assertEqual(balance_before, balance_after) + balance_after = self.web3.zksync.get_balance(self.account.address, EthBlockParams.PENDING.value) + print(f"balance after: {balance_after}") + self.assertEqual(balance_before, balance_after) diff --git a/tests/test_zksync_web3.py b/tests/test_zksync_web3.py index 8807039..fef843c 100644 --- a/tests/test_zksync_web3.py +++ b/tests/test_zksync_web3.py @@ -92,6 +92,28 @@ def test_deposit(self): self.account.address) self.assertEqual(1, tx_receipt["status"]) + @skip("Integration test, used for develop purposes only") + def test_deposit_usdc(self): + USDC_TOKEN = Token( + Web3.toChecksumAddress("0xd35cceead182dcee0f148ebac9447da2c4d449c4"), + Web3.toChecksumAddress("0x852a4599217e76aa725f0ada8bf832a1f57a8a91"), + "USDC", + 6) + + amount_usdc = 100000 + eth_web3 = Web3(Web3.HTTPProvider(self.ETH_TEST_URL)) + eth_web3.middleware_onion.inject(geth_poa_middleware, layer=0) + eth_provider = EthereumProvider.build_ethereum_provider(zksync=self.web3, + eth=eth_web3, + account=self.account, + gas_provider=self.gas_provider) + is_approved = eth_provider.approve_deposits(USDC_TOKEN, amount_usdc) + self.assertTrue(is_approved) + tx_receipt = eth_provider.deposit(USDC_TOKEN, + amount_usdc, + self.account.address) + self.assertEqual(1, tx_receipt["status"]) + @skip("Integration test, used for develop purposes only") def test_get_nonce(self): nonce = self.web3.zksync.get_transaction_count(self.account.address, EthBlockParams.LATEST.value) @@ -545,7 +567,7 @@ def test_execute_contract(self): updated_result = int.from_bytes(eth_ret2, "big", signed=True) self.assertEqual(result + 1, updated_result) - @skip("Integration test, used for develop purposes only") + # @skip("Integration test, used for develop purposes only") def test_get_all_account_balances(self): balances = self.web3.zksync.zks_get_all_account_balances(self.account.address) print(f"balances : {balances}") diff --git a/zksync2/manage_contracts/paymaster_utils.py b/zksync2/manage_contracts/paymaster_utils.py index 601c9ef..90b378d 100644 --- a/zksync2/manage_contracts/paymaster_utils.py +++ b/zksync2/manage_contracts/paymaster_utils.py @@ -24,9 +24,6 @@ def __init__(self, zksync: Web3): self.web3 = zksync self.contract = self.web3.zksync.contract(address=None, abi=_paymaster_flow_abi_default()) - # - # def encode_method(self, fn_name, args) -> HexStr: - # return self.contract.encodeABI(fn_name=fn_name, args=args) def encode_approval_based(self, address: HexStr, min_allowance: int, inner_input: bytes) -> HexStr: return self.contract.encodeABI(fn_name="approvalBased", args=[address, min_allowance, inner_input]) diff --git a/zksync2/module/zksync_module.py b/zksync2/module/zksync_module.py index ebc3981..113734e 100644 --- a/zksync2/module/zksync_module.py +++ b/zksync2/module/zksync_module.py @@ -64,9 +64,10 @@ def meta_formatter(eip712: EIP712Meta) -> dict: for dep in eip712.factory_deps] pp_params = eip712.paymaster_params if pp_params is not None: + paymaster_input = factory_formatter(pp_params.paymaster_input) ret["paymasterParams"] = { "paymaster": pp_params.paymaster, - "paymasterInput": to_ascii_if_bytes(pp_params.paymaster_input) + "paymasterInput": paymaster_input } return ret diff --git a/zksync2/transaction/transaction712.py b/zksync2/transaction/transaction712.py index be22dbf..0eb8333 100644 --- a/zksync2/transaction/transaction712.py +++ b/zksync2/transaction/transaction712.py @@ -4,6 +4,7 @@ import rlp from eth_account.datastructures import SignedMessage from eth_typing import ChecksumAddress, HexStr +from eth_utils import remove_0x_prefix from rlp.sedes import big_endian_int, binary from rlp.sedes import List as rlpList from web3 import Web3 @@ -50,7 +51,7 @@ def encode(self, signature: Optional[SignedMessage] = None) -> bytes: paymaster_params.paymaster is not None and \ paymaster_params.paymaster_input is not None: paymaster_params_data = [ - bytes.fromhex(paymaster_params.paymaster), + bytes.fromhex(remove_0x_prefix(paymaster_params.paymaster)), paymaster_params.paymaster_input ] paymaster_params_elements = [binary, binary]