Status: educational / hackathon project, not audited for production use.
| Actor | Trust level | Why |
|---|---|---|
| Deployer / Owner | Fully trusted | Owns GasPaymaster and GasAnalytics; controls sponsor policies, whitelisted contracts, authorized callers. Uses Ownable2Step for ownership transfer safety. |
| Relayer | Semi-trusted | Submits signed transactions on-chain but cannot forge user intent. All actions require valid EIP-712 signatures. Can censor (refuse to relay) but can't steal funds or execute unauthorized ops. |
| End User | Untrusted | Interacts only through signed meta-transactions. Replay, forgery, and nonce manipulation are prevented at the contract level. |
| Target Contracts | Per-whitelist | Only contracts in GasPaymaster.sponsoredContracts receive gas sponsorship. Unknown targets are rejected by preRelayCheck. |
Mode 1 (MetaTxBatchForwarder / TrustedForwarder):
- 2D nonce scheme: TrustedForwarder tracks nonces[from][key]. Each ForwardRequest specifies (from, nonce) and is rejected if nonce doesn't match.
- Deadline: every ForwardRequest carries a deadline timestamp. Expired requests revert with ExpiredRequest.
- Chain-bound EIP-712: domain separator includes chainId and verifyingContract, preventing cross-chain and cross-contract replay.
- Nonce consumption: nonce is incremented before execution, so even reverted executions consume the nonce.
Mode 2 (MiniEntryPoint / SmartAccount):
- Sequential nonce: SmartAccount maintains a nonce storage variable, checked and incremented by MiniEntryPoint.handleOp.
- Deadline: UserOperation.deadline enforced identically to Mode 1.
- Chain-bound EIP-712: separate domain ("GasVote EntryPoint") with chainId + verifyingContract.
- Signature recovery: SmartAccount.validateUserOp recovers the signer via ECDSA and checks against owner. Invalid signatures revert.
All state-changing contracts use TransientReentrancyGuard (EIP-1153):
| Contract | Protected functions |
|---|---|
| BatchExecutor | executeBatch |
| TrustedForwarder | execute |
| MetaTxBatchForwarder | executeWithSponsor |
| MiniEntryPoint | handleOp, handleOps |
| SmartAccount | execute |
| GasPaymaster | withdraw, reimburse |
| DemoVault | deposit, withdraw |
Traditional reentrancy guards use SSTORE (20,000 gas cold / 5,000 warm for write, 2,100 cold / 100 warm for read). EIP-1153 transient storage costs 100 gas for both TSTORE and TLOAD, saving ~5,000 gas per guard entry. Transient slots auto-clear at transaction end.
Requires Cancun-compatible EVM (mainnet since March 2024, Anvil with evm_version = "cancun").
GasPaymaster (Ownable2Step):
| Function | Access |
|---|---|
| setAuthorizedCaller | onlyOwner |
| setSponsoredContract | onlyOwner |
| setPolicy | onlyOwner |
| withdraw | onlyOwner + nonReentrant |
| withdrawTokens | onlyOwner |
| deposit | Public (anyone can fund the pool) |
| reimburse | authorizedCallers only |
Ownable2Step requires a two-step ownership transfer (propose then accept), preventing accidental transfers to wrong addresses.
GasAnalytics: setAuthorizedRecorder is onlyOwner, recordBatch is authorizedRecorders only, getGlobalStats is public view.
SmartAccount: execute, validateUserOp, and transferOwnership are all onlyEntryPoint.
Both modes use EIP-712 typed data for user authorization.
ForwardRequest (Mode 1): from, nonce, deadline, gasPaymentMode, calls (array of BatchCall with target, callData, value, allowFailure, dependsOn, useERC2771).
UserOperation (Mode 2): sender, nonce, deadline, gasPaymentMode, calls (same structure).
Domain separators:
- Mode 1: EIP712Domain("GasVote Forwarder", "1", chainId, trustedForwarder)
- Mode 2: EIP712Domain("GasVote EntryPoint", "1", chainId, miniEntryPoint)
TrustedForwarder appends the original msg.sender (20 bytes) to calldata before forwarding to target contracts. Targets using ERC2771Context (OpenZeppelin) extract the real sender from the last 20 bytes of msg.data.
Trust boundary: only calls routed through the TrustedForwarder (which validates signatures) carry appended sender data. Direct calls use msg.sender as-is. ERC2771Context._msgSender() handles both cases by checking msg.data.length >= 20 && msg.sender == trustedForwarder.
| Risk | Severity | Mitigation |
|---|---|---|
| Owner key compromise | Critical | Ownable2Step prevents instant transfer; multi-sig recommended for production |
| Relayer censorship | Medium | Relayer can refuse to relay; decentralized relay network would mitigate |
| Relayer front-running | Low | Relayer sees signed transactions first; signature-bound operations prevent fund theft |
| Gas price manipulation | Low | GasPaymaster.reimburse uses tx.gasprice for cost; on Anvil tx.gasprice = 0 (known test failure) |
| DemoToken unlimited mint | Info | faucetCap limits per-call mint (10k), but no total supply cap -- by design for demo/testing |
| DemoNFT supply cap | Info | Max supply of 10,000 with 5 per-call anti-griefing cap -- by design for demo |
| Batch gas limit | Medium | Large batches may exceed block gas limit; BatchExecutor enforces max 32 calls |
| Warm storage bias in scripts | Info | DemoMode2.s.sol measurements may differ from GasComparison.t.sol due to warm slots (documented) |
| Dependency | Version | Notes |
|---|---|---|
| OpenZeppelin Contracts | 5.x | ERC-2771, Ownable2Step, EIP712, ECDSA, ERC20Permit |
| Forge Std | Latest | Test framework only, not deployed |
| Solidity | 0.8.24 | Built-in overflow/underflow protection |
| Property | Test file | Result |
|---|---|---|
| Replay protection (nonce) | TrustedForwarder.t.sol, MiniEntryPoint.t.sol | Pass |
| Deadline enforcement | Both unit test suites | Pass |
| Invalid signature rejection | Both unit test suites | Pass |
| Self-dependency DAG rejection | BatchExecutorSymbolic.t.sol | Pass |
| Forward-dependency rejection | BatchExecutorSymbolic.t.sol | Pass (fuzz) |
| Reentrancy guard (7 contracts) | Implicit via nonReentrant | Pass |
| Paymaster pool invariants | GasPaymasterInvariant.t.sol | Pass (2/3) |
| Unauthorized recorder rejection | GasAnalytics.t.sol | Pass |
| Unauthorized reimburse rejection | GasPaymaster.t.sol | Pass |
| Wrong nonce rejection | Both unit test suites | Pass |
| E2E permit/deposit/stake | GasComparison.t.sol | Pass |
This is an educational/hackathon project. If you discover a security issue, please open a GitHub issue or contact the maintainers directly.