aere-contracts/contracts/AereAppPaymasterFactory.sol
Aere Network a13a649b77
Some checks are pending
contracts-ci / Install (lockfile) → compile → full test suite (push) Waiting to run
contracts-ci / PQC known-answer tests (NIST vectors) (push) Waiting to run
contracts-ci / Coverage (scoped, with artifacts) (push) Waiting to run
Initial public release
Aere Network public source. Everything here can be checked against the live
chain (chain id 2800, https://rpc.aere.network).

Scope note, stated up front rather than buried: consensus on chain 2800 is
classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at
the signature, precompile, account and transport layers. Nothing here makes the
consensus post-quantum, and no document in it should be read as claiming so.
2026-07-20 01:02:37 +03:00

113 lines
4.1 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import "./paymaster/PaymasterBase.sol";
/**
* @title AereAppPaymaster
* @notice Generic dApp-funded paymaster. Each dApp on AERE deploys one of these
* and funds it from their own treasury. The paymaster sponsors gas only
* for UserOps calling contracts on the dApp's whitelist, and (optionally)
* only for senders on the dApp's allowlist.
*
* Use case: Bank28 deploys their own AereAppPaymaster, funds it with X AERE
* per month from their CAC budget, whitelists Bank28's contracts as sponsorship
* targets, and optionally allowlists the senders they've authenticated through
* the Bank28 app. Foundation contributes zero.
*
* Same pattern works for any dApp: NFT marketplaces, games, DeFi apps.
*/
contract AereAppPaymaster is PaymasterBase {
/// Optional sender allowlist. If enabled, only allowlisted senders can be sponsored.
bool public senderAllowlistEnabled;
mapping(address => bool) public senderAllowlist;
/// Required target whitelist. UserOps must invoke an allowed target contract.
mapping(address => bool) public targetWhitelist;
/// Per-sender lifetime sponsorship cap. 0 = unlimited.
uint256 public maxOpsPerSender;
mapping(address => uint256) public opsCountBySender;
event SenderAllowlistChanged(bool enabled);
event SenderAllowlist(address indexed sender, bool allowed);
event TargetWhitelist(address indexed target, bool allowed);
event ConfigChanged(uint256 maxOpsPerSender);
event Sponsored(address indexed sender, address indexed target, uint256 newSenderCount);
constructor(IEntryPoint _ep, address _owner) PaymasterBase(_ep) {
_transferOwnership(_owner);
}
function setSenderAllowlist(bool enabled) external onlyOwner {
senderAllowlistEnabled = enabled;
emit SenderAllowlistChanged(enabled);
}
function setAllowedSender(address sender, bool allowed) external onlyOwner {
senderAllowlist[sender] = allowed;
emit SenderAllowlist(sender, allowed);
}
function setTargetWhitelist(address target, bool allowed) external onlyOwner {
targetWhitelist[target] = allowed;
emit TargetWhitelist(target, allowed);
}
function setMaxOpsPerSender(uint256 max) external onlyOwner {
maxOpsPerSender = max;
emit ConfigChanged(max);
}
function _validatePaymasterUserOp(
PackedUserOperation calldata userOp,
bytes32 /*userOpHash*/,
uint256 /*maxCost*/
) internal override returns (bytes memory context, uint256 validationData) {
address sender = userOp.sender;
if (senderAllowlistEnabled) {
require(senderAllowlist[sender], "AppPM: sender not allowed");
}
if (maxOpsPerSender > 0) {
require(opsCountBySender[sender] < maxOpsPerSender, "AppPM: sender quota exhausted");
}
// Decode target from SimpleAccount-compatible execute(target, value, data) callData layout.
bytes calldata cd = userOp.callData;
require(cd.length >= 36, "AppPM: bad callData");
address target;
assembly {
target := shr(96, calldataload(add(cd.offset, 16)))
}
require(targetWhitelist[target], "AppPM: target not allowed");
opsCountBySender[sender] += 1;
emit Sponsored(sender, target, opsCountBySender[sender]);
return ("", 0);
}
}
/**
* @title AereAppPaymasterFactory
* @notice One-call factory for any dApp to spin up its own AereAppPaymaster.
* The dApp's deployer ends up as owner of the new paymaster.
* No registration fee. Anyone can deploy.
*/
contract AereAppPaymasterFactory {
IEntryPoint public immutable entryPoint;
event PaymasterCreated(address indexed deployer, address indexed paymaster);
constructor(IEntryPoint _ep) {
entryPoint = _ep;
}
function createPaymaster() external returns (address paymaster) {
AereAppPaymaster p = new AereAppPaymaster(entryPoint, msg.sender);
paymaster = address(p);
emit PaymasterCreated(msg.sender, paymaster);
}
}