forked from foundry-rs/book
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add approval based paymaster usage example
- Loading branch information
Showing
3 changed files
with
140 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
135
src/zksync-specifics/examples/paymaster-approval-based.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |