Skip to content

Commit

Permalink
Add approval based paymaster usage example
Browse files Browse the repository at this point in the history
  • Loading branch information
Jrigada committed Oct 10, 2024
1 parent f61f755 commit 1142bc3
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
3 changes: 3 additions & 0 deletions src/zksync-specifics/examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# ZKsync specific examples

- [Paymaster Approval Based](paymaster-approval-based.md)
135 changes: 135 additions & 0 deletions src/zksync-specifics/examples/paymaster-approval-based.md
Original file line number Diff line number Diff line change
@@ -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);
}
}

0 comments on commit 1142bc3

Please sign in to comment.