From b9994592fe1531334e24418d4436d1039d723385 Mon Sep 17 00:00:00 2001 From: Jrigada Date: Mon, 25 Nov 2024 21:08:41 -0300 Subject: [PATCH 1/4] Add general paymaster example --- src/zksync-specifics/examples/README.md | 1 + .../examples/general-paymaster.md | 151 ++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 src/zksync-specifics/examples/general-paymaster.md diff --git a/src/zksync-specifics/examples/README.md b/src/zksync-specifics/examples/README.md index a275c95e0..6413bfcc5 100644 --- a/src/zksync-specifics/examples/README.md +++ b/src/zksync-specifics/examples/README.md @@ -1,3 +1,4 @@ # ZKsync specific examples - [Paymaster Approval Based](paymaster-approval-based.md) +- [General Flow Paymaster](general-paymaster.md) diff --git a/src/zksync-specifics/examples/general-paymaster.md b/src/zksync-specifics/examples/general-paymaster.md new file mode 100644 index 000000000..e1c6a5bfb --- /dev/null +++ b/src/zksync-specifics/examples/general-paymaster.md @@ -0,0 +1,151 @@ +## Using the zkUsePaymaster Cheatcode in General Flow Paymaster Contracts + +This example covers the use of a general flow paymaster contract. +For this example we will use a contract from the paymaster example repository [here](https://github.com/matter-labs/paymaster-examples). + +The contract we are going to use is the `GaslessPaymaster` contract. + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@matterlabs/era-contracts/interfaces/IPaymaster.sol"; +import "@matterlabs/era-contracts/interfaces/IPaymasterFlow.sol"; +import "@matterlabs/era-contracts/Constants.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +/// @author Matter Labs +/// @notice This contract does not include any validations other than using the paymaster general flow. +contract GaslessPaymaster is IPaymaster, Ownable { + constructor() Ownable(msg.sender) {} + + modifier onlyBootloader() { + require( + msg.sender == BOOTLOADER_FORMAL_ADDRESS, + "Only bootloader can call this method" + ); + // Continue execution if called from the bootloader. + _; + } + + function validateAndPayForPaymasterTransaction( + bytes32, + bytes32, + Transaction calldata _transaction + ) + external + payable + onlyBootloader + returns (bytes4 magic, bytes memory context) + { + // By default we consider the transaction as accepted. + magic = PAYMASTER_VALIDATION_SUCCESS_MAGIC; + require( + _transaction.paymasterInput.length >= 4, + "The standard paymaster input must be at least 4 bytes long" + ); + + bytes4 paymasterInputSelector = bytes4( + _transaction.paymasterInput[0:4] + ); + if (paymasterInputSelector == IPaymasterFlow.general.selector) { + // Note, that while the minimal amount of ETH needed is tx.gasPrice * tx.gasLimit, + // neither paymaster nor account are allowed to access this context variable. + uint256 requiredETH = _transaction.gasLimit * + _transaction.maxFeePerGas; + // The bootloader never returns any data, so it can safely be ignored here. + (bool success, ) = payable(BOOTLOADER_FORMAL_ADDRESS).call{ + value: requiredETH + }(""); + require( + success, + "Failed to transfer tx fee to the Bootloader. Paymaster balance might not be enough." + ); + } else { + revert("Unsupported paymaster flow in paymasterParams."); + } + } + + function postTransaction( + bytes calldata _context, + Transaction calldata _transaction, + bytes32, + bytes32, + ExecutionResult _txResult, + uint256 _maxRefundedGas + ) external payable override onlyBootloader { + // Refunds are not supported yet. + } + + function withdraw(address payable _to) external onlyOwner { + // send paymaster funds to the owner + uint256 balance = address(this).balance; + (bool success, ) = _to.call{value: balance}(""); + require(success, "Failed to withdraw funds from paymaster."); + } + + receive() external payable {} +} +``` + +This contract is a general flow paymaster, which means that it can pay for any account. To be able to use this paymaster we need to first deploy it in the intended network. + +For this example we will deploy it in the era-test-node and then use the `zkUsePaymaster` cheatcode to pay for a transaction using a script. + +```solidity +pragma solidity ^0.8.0; + +import {Script} from "forge-std/Script.sol"; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "../src/GeneralPaymaster.sol"; +// We need to import the TestExt to use the zkUsePaymaster cheatcode +// as this is a ZKsync specific cheatcode +import "../src/Counter.sol"; +import {TestExt} from "forge-zksync-std/TestExt.sol"; + +contract PaymasterUsageScript is Script, TestExt { + Counter public counter; + + function run() public { + vm.startBroadcast(); + + GaslessPaymaster paymaster = new GaslessPaymaster(); + + // fund the paymaster + address(paymaster).call{value: 0.05 ether}(""); + + bytes memory paymaster_encoded_input = abi.encodeWithSelector( + bytes4(keccak256("general(bytes)")), + bytes("0x") + ); + vmExt.zkUsePaymaster( + address(paymaster), + paymaster_encoded_input + ); + + counter = new Counter(); + + vm.stopBroadcast(); + } +} +``` + +The key part of this script is encoding the paymaster call with the `general(bytes)` selector and then using the `zkUsePaymaster` cheatcode to pay for the transaction. This will vary depending on the paymaster contract that you are using. + +```solidity + // This is the encoding for the GaslessPaymaster + bytes memory paymaster_encoded_input = abi.encodeWithSelector( + bytes4(keccak256("general(bytes)")), + bytes("0x") + ); + + // Using the encoded parameters to call the zkUsePaymaster cheatcode + vmExt.zkUsePaymaster( + address(paymaster), + paymaster_encoded_input + ); +``` +After calling the `zkUsePaymaster` cheatcode, the paymaster will pay for the following transaction. + + From ca0a935ceb5b3bc93d05ed34a313c827566f5590 Mon Sep 17 00:00:00 2001 From: Jrigada Date: Mon, 25 Nov 2024 21:12:02 -0300 Subject: [PATCH 2/4] Add general example to Summary --- src/SUMMARY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 256e4ef94..a0ae5fe48 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -39,6 +39,7 @@ - [Gas Overview](./zksync-specifics/gas.md) - [Paymaster Overview](./zksync-specifics/paymaster-overview.md) - [Examples](./zksync-specifics/examples/README.md) + - [General Flow Paymaster](./zksync-specifics/examples/general-paymaster.md) - [Paymaster Approval Based](./zksync-specifics/examples/paymaster-approval-based.md) - [Ledger](./zksync-specifics/examples/ledger.md) - [Multisig Smart Account](./zksync-specifics/examples/smart-account.md) From 512ef090744aeb4429e7d92a5892b511875239a4 Mon Sep 17 00:00:00 2001 From: Juan Rigada <62958725+Jrigada@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:10:43 -0300 Subject: [PATCH 3/4] Update src/zksync-specifics/examples/general-paymaster.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Federico Rodríguez --- src/zksync-specifics/examples/general-paymaster.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/zksync-specifics/examples/general-paymaster.md b/src/zksync-specifics/examples/general-paymaster.md index e1c6a5bfb..e0c1aa5a2 100644 --- a/src/zksync-specifics/examples/general-paymaster.md +++ b/src/zksync-specifics/examples/general-paymaster.md @@ -3,7 +3,6 @@ This example covers the use of a general flow paymaster contract. For this example we will use a contract from the paymaster example repository [here](https://github.com/matter-labs/paymaster-examples). -The contract we are going to use is the `GaslessPaymaster` contract. ```solidity // SPDX-License-Identifier: MIT From e3f07108ca463edfdca9453557b49894b01b883b Mon Sep 17 00:00:00 2001 From: Juan Rigada <62958725+Jrigada@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:11:14 -0300 Subject: [PATCH 4/4] Update src/zksync-specifics/examples/general-paymaster.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Federico Rodríguez --- src/zksync-specifics/examples/general-paymaster.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zksync-specifics/examples/general-paymaster.md b/src/zksync-specifics/examples/general-paymaster.md index e0c1aa5a2..fe35bc5d5 100644 --- a/src/zksync-specifics/examples/general-paymaster.md +++ b/src/zksync-specifics/examples/general-paymaster.md @@ -1,7 +1,7 @@ ## Using the zkUsePaymaster Cheatcode in General Flow Paymaster Contracts This example covers the use of a general flow paymaster contract. -For this example we will use a contract from the paymaster example repository [here](https://github.com/matter-labs/paymaster-examples). +For this example we will use the `GaslessPaymaster` contract from the paymaster example repository [here](https://github.com/matter-labs/paymaster-examples). ```solidity