> For the complete documentation index, see [llms.txt](https://docs.greenowl.money/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.greenowl.money/smart-contracts/contracts/whitelistpaymaster.md).

# WhitelistPaymaster

**Author:** Lendefi Team

Sponsors gas for ERC-4337 UserOperations that target whitelisted contracts. Inherits from `BasePaymaster` (which provides `Ownable` and `EntryPoint` integration).

***

## Overview

The `WhitelistPaymaster` is a permissioned gas sponsor for the Owl Smart Wallet system. It validates that a UserOperation's target contract is on the whitelist before agreeing to pay gas fees. This enables controlled gas sponsorship for AI agents interacting with approved protocols (e.g., DeFi lending, token swaps).

**Key Features:**

* Whitelist-based target validation for `execute()` and `executeBatch()` calls
* Optional wallet registry enforcement to ensure only valid wallets receive sponsorship
* Gas tracking metrics for monitoring and analytics
* Batch whitelist management for operational efficiency

**Security Model:**

* Only owner can modify whitelist and registry settings
* **Critical:** Only first-level call targets are validated. Do NOT whitelist contracts with arbitrary forwarding capabilities (multicall routers, generic proxies) as they would bypass target validation.

**Agentic Context:**\
AI agents submit UserOperations via the bundler relay. If the target contract (e.g., Morpho, Uniswap) is whitelisted, the paymaster sponsors gas, enabling gasless autonomous operation.

***

## State Variables

### whitelistedContracts

Mapping of approved target contracts that can receive sponsored transactions.

```solidity
mapping(address contractAddress => bool isWhitelisted) public whitelistedContracts;
```

### walletRegistry

Optional registry/factory contract used to validate that `userOp.sender` is a legitimate wallet.

```solidity
address public walletRegistry;
```

### enforceWalletRegistry

If `true`, requires `userOp.sender` to be validated against `walletRegistry` before sponsoring.

```solidity
bool public enforceWalletRegistry;
```

### totalSponsoredTransactions

Counter of all sponsored transactions (for analytics).

```solidity
uint256 public totalSponsoredTransactions;
```

### totalGasSponsored

Cumulative gas cost sponsored (in wei).

```solidity
uint256 public totalGasSponsored;
```

### EXECUTE\_SELECTOR

Function selector for `SmartWallet.execute(address,uint256,bytes)`.

```solidity
bytes4 private constant EXECUTE_SELECTOR = 0xb61d27f6;
```

### EXECUTE\_BATCH\_SELECTOR

Function selector for `SmartWallet.executeBatch((address,uint256,bytes)[])`.

```solidity
bytes4 private constant EXECUTE_BATCH_SELECTOR = bytes4(keccak256("executeBatch((address,uint256,bytes)[])"));
```

***

## Functions

### Constructor

Initializes the paymaster with the ERC-4337 EntryPoint and owner address.

```solidity
constructor(IEntryPoint _entryPoint, address _owner) BasePaymaster(_entryPoint, _owner);
```

| Parameter     | Type          | Description                               |
| ------------- | ------------- | ----------------------------------------- |
| `_entryPoint` | `IEntryPoint` | ERC-4337 EntryPoint contract              |
| `_owner`      | `address`     | Owner address (receives admin privileges) |

***

## Admin Functions

### setWhitelistedContract

Add or remove a contract from the whitelist.

**Access Control:** `onlyOwner`

**Security Warning:** Only whitelist contracts you trust as direct call targets. Do NOT whitelist generic forwarding contracts (multicall, proxies, routers), as they enable attackers to bypass target validation.

```solidity
function setWhitelistedContract(address contractAddress, bool whitelisted) external onlyOwner;
```

| Parameter         | Type      | Description                               |
| ----------------- | --------- | ----------------------------------------- |
| `contractAddress` | `address` | Contract address to whitelist/unwhitelist |
| `whitelisted`     | `bool`    | `true` to whitelist, `false` to remove    |

**Emits:** `ContractWhitelisted`

***

### setWhitelistedContractsBatch

Batch whitelist multiple contracts in a single transaction.

**Access Control:** `onlyOwner`

```solidity
function setWhitelistedContractsBatch(address[] calldata contractAddresses, bool whitelisted) external onlyOwner;
```

| Parameter           | Type        | Description                                    |
| ------------------- | ----------- | ---------------------------------------------- |
| `contractAddresses` | `address[]` | Array of contract addresses                    |
| `whitelisted`       | `bool`      | `true` to whitelist all, `false` to remove all |

**Emits:** `ContractWhitelisted` (once per address)

***

### setWalletRegistry

Set the wallet registry/factory contract used to validate `userOp.sender`.

Set to `address(0)` to disable registry validation (also disables enforcement automatically).

**Access Control:** `onlyOwner`

```solidity
function setWalletRegistry(address newRegistry) external onlyOwner;
```

| Parameter     | Type      | Description                                            |
| ------------- | --------- | ------------------------------------------------------ |
| `newRegistry` | `address` | Registry contract address (or `address(0)` to disable) |

**Emits:** `WalletRegistryUpdated`

***

### setEnforceWalletRegistry

Enable or disable wallet registry enforcement.

When enabled, `walletRegistry` must be set and all `userOp.sender` addresses must pass registry validation.

**Access Control:** `onlyOwner`

```solidity
function setEnforceWalletRegistry(bool enforced) external onlyOwner;
```

| Parameter  | Type   | Description                                      |
| ---------- | ------ | ------------------------------------------------ |
| `enforced` | `bool` | `true` to enable enforcement, `false` to disable |

**Emits:** `WalletRegistryEnforcementUpdated`

***

## View Functions

### isWhitelisted

Check if a contract address is whitelisted.

```solidity
function isWhitelisted(address contractAddress) external view returns (bool);
```

| Parameter         | Type      | Description      |
| ----------------- | --------- | ---------------- |
| `contractAddress` | `address` | Address to check |

**Returns:**

| Type   | Description                              |
| ------ | ---------------------------------------- |
| `bool` | `true` if whitelisted, `false` otherwise |

***

## Internal Functions

### \_validatePaymasterUserOp

*Internal validation logic called by EntryPoint.*

Validates the UserOperation before agreeing to sponsor gas. Checks:

1. `userOp.sender` is valid (if registry enforcement enabled)
2. Target contract (extracted from calldata) is whitelisted
3. Function selector is `execute` or `executeBatch`

```solidity
function _validatePaymasterUserOp(
    PackedUserOperation calldata userOp,
    bytes32, /* userOpHash */
    uint256 /* maxCost */
)
    internal
    view
    override
    returns (bytes memory context, uint256 validationData);
```

**Returns:**

| Name             | Type      | Description                                                |
| ---------------- | --------- | ---------------------------------------------------------- |
| `context`        | `bytes`   | ABI-encoded `(address user, address target)` for `_postOp` |
| `validationData` | `uint256` | `0` for valid, non-zero for invalid/expired                |

**Reverts:**

* `InvalidWallet(wallet)` if registry enforcement enabled and wallet not valid
* `WalletRegistryNotSet()` if enforcement enabled but registry is `address(0)`
* `InvalidUserOperation()` if calldata too short or selector invalid
* `TargetNotWhitelisted(target)` if target not whitelisted

***

### \_postOp

*Internal post-operation hook called by EntryPoint.*

Tracks gas usage metrics and emits `TransactionSponsored` event.

```solidity
function _postOp(
    PostOpMode, /* mode */
    bytes calldata context,
    uint256 actualGasCost,
    uint256 /* actualUserOpFeePerGas */
)
    internal
    override;
```

| Parameter       | Type      | Description                                                  |
| --------------- | --------- | ------------------------------------------------------------ |
| `context`       | `bytes`   | ABI-encoded `(address user, address target)` from validation |
| `actualGasCost` | `uint256` | Actual gas cost in wei                                       |

**Emits:** `TransactionSponsored`

***

## Events

### ContractWhitelisted

Emitted when a contract is added to or removed from the whitelist.

```solidity
event ContractWhitelisted(address indexed contractAddress, bool whitelisted);
```

| Parameter         | Type      | Description                                      |
| ----------------- | --------- | ------------------------------------------------ |
| `contractAddress` | `address` | Contract address being whitelisted/unwhitelisted |
| `whitelisted`     | `bool`    | `true` if whitelisted, `false` if removed        |

***

### TransactionSponsored

Emitted when a transaction is successfully sponsored.

```solidity
event TransactionSponsored(address indexed user, address indexed target, uint256 actualGasCost);
```

| Parameter       | Type      | Description                                    |
| --------------- | --------- | ---------------------------------------------- |
| `user`          | `address` | Wallet address whose transaction was sponsored |
| `target`        | `address` | Target contract of the sponsored call          |
| `actualGasCost` | `uint256` | Gas cost in wei                                |

***

### WalletRegistryUpdated

Emitted when the wallet registry address is changed.

```solidity
event WalletRegistryUpdated(address indexed oldRegistry, address indexed newRegistry);
```

| Parameter     | Type      | Description               |
| ------------- | --------- | ------------------------- |
| `oldRegistry` | `address` | Previous registry address |
| `newRegistry` | `address` | New registry address      |

***

### WalletRegistryEnforcementUpdated

Emitted when wallet registry enforcement is enabled or disabled.

```solidity
event WalletRegistryEnforcementUpdated(bool enforced);
```

| Parameter  | Type   | Description                                        |
| ---------- | ------ | -------------------------------------------------- |
| `enforced` | `bool` | `true` if enforcement enabled, `false` if disabled |

***

## Errors

### TargetNotWhitelisted

Thrown when the target contract is not whitelisted.

```solidity
error TargetNotWhitelisted(address target);
```

***

### InvalidUserOperation

Thrown when the UserOperation has invalid calldata or function selector.

```solidity
error InvalidUserOperation();
```

***

### ZeroAddress

Thrown when a zero address is provided where not allowed.

```solidity
error ZeroAddress();
```

***

### InvalidWallet

Thrown when wallet registry enforcement is enabled and the wallet is not valid.

```solidity
error InvalidWallet(address wallet);
```

***

### WalletRegistryNotSet

Thrown when wallet registry enforcement is enabled but `walletRegistry` is `address(0)`.

```solidity
error WalletRegistryNotSet();
```

***

## Integration Guide

**For AI Agent Developers:**

When submitting UserOperations via the bundler relay, ensure:

1. The target contract is whitelisted (check via `isWhitelisted()`)
2. The wallet is valid (if registry enforcement is enabled)
3. The function is `execute()` or `executeBatch()`

**For Protocol Integrators:**

To enable gas sponsorship for your protocol:

1. Request whitelist approval from the paymaster owner
2. Ensure your contract does not forward arbitrary calls
3. Test with a UserOperation before production use

**For Auditors:**

Critical validation points:

* `_validatePaymasterUserOp` extracts target from calldata at fixed offsets
* Only first-level targets are validated (nested calls not checked)
* Registry enforcement is optional but recommended for production


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.greenowl.money/smart-contracts/contracts/whitelistpaymaster.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
