From 1142bc3ec26e419b62ba3bf82cc54e8599c56100 Mon Sep 17 00:00:00 2001 From: Jrigada Date: Thu, 10 Oct 2024 17:48:26 -0300 Subject: [PATCH] Add approval based paymaster usage example --- src/SUMMARY.md | 3 +- src/zksync-specifics/examples/README.md | 3 + .../examples/paymaster-approval-based.md | 135 ++++++++++++++++++ 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 src/zksync-specifics/examples/README.md create mode 100644 src/zksync-specifics/examples/paymaster-approval-based.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index f351d98d7..25a8d8e2c 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -36,7 +36,8 @@ - [zkVmSkip](./zksync-specifics/cheatcodes/zk-vm-skip.md) - [zkUsePaymaster](./zksync-specifics/cheatcodes/zk-use-paymaster.md) - [Gas Overview](./zksync-specifics/gas.md) - +- [Examples](./zksync-specifics/examples/README.md) + - [Paymaster Approval Based](./zksync-specifics/examples/paymaster-approval-based.md) # Supported Commands - [Command List](./supported-commands/README.md) diff --git a/src/zksync-specifics/examples/README.md b/src/zksync-specifics/examples/README.md new file mode 100644 index 000000000..a275c95e0 --- /dev/null +++ b/src/zksync-specifics/examples/README.md @@ -0,0 +1,3 @@ +# ZKsync specific examples + +- [Paymaster Approval Based](paymaster-approval-based.md) diff --git a/src/zksync-specifics/examples/paymaster-approval-based.md b/src/zksync-specifics/examples/paymaster-approval-based.md new file mode 100644 index 000000000..f23b7e4cc --- /dev/null +++ b/src/zksync-specifics/examples/paymaster-approval-based.md @@ -0,0 +1,135 @@ +## Using the zkUsePaymaster Cheatcode in Approval-Based Paymaster Contracts + +This example covers the use of an approval-based paymaster contract. The paymaster contract used is the testnet paymaster of zkSync documented [here](https://docs.zksync.io/build/start-coding/quick-start/paymasters-introduction). + +### Steps Overview + +1. Setup and Initialization + - Create a custom ERC20 token contract. + - Deploy the ERC20 contract. + - Mint tokens to the address using the paymaster. +2. Approval and Paymaster Preparation + - Create a paymaster contract. + - Encode the paymaster call with required parameters. + - Use the zkUsePaymaster cheatcode. + +### Step-by-Step + +Let's start by deploying the ERC20 contract and minting some tokens to the account that is using the paymaster. The idea behind the approval-based paymaster is that users transfer ERC20 tokens to the paymaster, which in turn pays for the transaction. + +This is the code for the ERC20 contract: +```solidity +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract MyERC20 is ERC20 { + constructor() ERC20("SPITTE", "SPT") {} + + function mint(address to, uint256 amount) public { + _mint(to, amount); + } +} +``` + +Now, in the script we are going to run, we deploy the contract and mint some tokens to the account that is using the paymaster: + +```solidity +import {Script} from "forge-std/Script.sol"; +import {console2} from "../lib/forge-std/src/console2.sol"; + +contract PaymasterApprovalScript is Script { + function run() external { + vm.startBroadcast(); + + MyERC20 erc20 = new MyERC20(); + erc20.mint(address(tx.origin), 10); + + vm.stopBroadcast(); + } +} +``` + +Next, we prepare the encoded input for the paymaster: + +```solidity +// Encode the paymaster input +bytes memory paymaster_encoded_input = abi.encodeWithSelector( + bytes4(keccak256("approvalBased(address,uint256,bytes)")), + address(erc20), // ERC20 token address + uint256(1 ether), // Approval amount + bytes("0x") // Additional data (empty in this case) +); +``` +Here, we are encoding the paymaster input with the approvalBased method signature and the required parameters. The second parameter is the address of the recently deployed ERC20 contract, the third parameter is the amount of tokens that the paymaster consumes from the user to pay for the transaction, and the last one is empty bytes in this case. + +With the encoded input prepared, we can now use the zkUsePaymaster cheatcode to prepare the next call to be executed using the paymaster: + +```solidity +// Using zkUsePaymaster with the encoded input +vm.zkUsePaymaster(address(0x3cB2b87D10Ac01736A65688F3e0Fb1b070B3eeA3), paymaster_encoded_input); + +Counter counter = new Counter(); +counter.increment(); +``` + +The `counter.increment()` call will be executed using the paymaster that we set up in the encoded input. + +### Complete code +Below is the full code for the PaymasterTestScript demonstrating all steps: + +```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"; + +contract PaymasterTestScript is Script { + function run() external { + vm.startBroadcast(); + + // Deploy the ERC20 contract + MyERC20 erc20 = new MyERC20(); + + // Mint some tokens + erc20.mint(address(tx.origin), 10); + + // Encode the paymaster input + bytes memory paymaster_encoded_input = abi.encodeWithSelector( + bytes4(keccak256("approvalBased(address,uint256,bytes)")), // Function selector + address(erc20), // ERC20 address + uint256(1 ether), // The uint256 value + bytes("0x") // Empty bytes "0x" + ); + + // Create a new Counter contract + Counter counter = new Counter(); + + // Use the zkUsePaymaster cheatcode to prepare the next call to be executed using the paymaster + vm.zkUsePaymaster(address(0x3cB2b87D10Ac01736A65688F3e0Fb1b070B3eeA3), paymaster_encoded_input); + + // Increment the counter + counter.increment(); + + vm.stopBroadcast(); + } +} + +contract Counter { + uint256 public count = 0; + + function increment() public { + count++; + } + + function getCount() public view returns (uint256) { + return count; + } +} + +contract MyERC20 is ERC20 { + constructor() ERC20("SPITTE", "SPT") {} + + function mint(address to, uint256 amount) public { + _mint(to, amount); + } +}