Skip to content

Instantly share code, notes, and snippets.

@Lytes
Last active July 13, 2024 00:48
Show Gist options
  • Save Lytes/5db5314dfb7cce238bddbc92fd8ca074 to your computer and use it in GitHub Desktop.
Save Lytes/5db5314dfb7cce238bddbc92fd8ca074 to your computer and use it in GitHub Desktop.
Zk Paymaster contract for unique wallet
// Quick way to spin up paymaster for specific wallets
// Effective for whitehat recoveries on zk
//
// Replace Addresses on line 54 - 56
//
// what are paymasters? https://docs.zksync.io/build/zksync-101/paymaster
// @kuro_lytes
//
pragma solidity ^0.8.0;
import {IPaymaster, ExecutionResult, PAYMASTER_VALIDATION_SUCCESS_MAGIC} from "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/IPaymaster.sol";
import {IPaymasterFlow} from "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/IPaymasterFlow.sol";
import {TransactionHelper, Transaction} from "@matterlabs/zksync-contracts/l2/system-contracts/libraries/TransactionHelper.sol";
import "@matterlabs/zksync-contracts/l2/system-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 {
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]
);
address userAddress = address(uint160(_transaction.from));
require(
userAddress == 0x0000000000000000000000000000000000000000 ||
userAddress == 0x0000000000000000000000000000000000000000 ||
userAddress == 0x0000000000000000000000000000000000000000 ,
"User address not authorized."
);
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 {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment