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.
113 lines
4.1 KiB
Solidity
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);
|
|
}
|
|
}
|