@@ -22,11 +22,26 @@ contract OpenMatrixPaymaster is ReentrancyGuard {
2222 uint256 public totalSponsored;
2323 uint256 public totalTransactions;
2424
25+ // ── Value-call policy (mirrors the server paymaster policy.allowed_actions) ──
26+ // A compromised/authorized AGENT key must not be able to drain funds or hit
27+ // an arbitrary target via sponsoredCallWithValue. The owner is trusted and
28+ // exempt; agents are constrained by a per-agent daily value cap and an
29+ // optional target allowlist. Both default to the tightest setting
30+ // (cap = 0 → agents cannot move value until the owner grants an allowance).
31+ uint256 public agentDailyCap;
32+ bool public targetAllowlistEnabled;
33+ mapping (address => bool ) public allowedTargets;
34+ mapping (address => uint256 ) public agentSpentToday;
35+ mapping (address => uint256 ) public agentDayStart;
36+
2537 event GasSponsored (address indexed user , uint256 amount , string action );
2638 event AgentAuthorized (address indexed agent );
2739 event AgentRevoked (address indexed agent );
2840 event FundsDeposited (address indexed from , uint256 amount );
2941 event FundsWithdrawn (address indexed to , uint256 amount );
42+ event AgentDailyCapSet (uint256 cap );
43+ event TargetAllowed (address indexed target , bool allowed );
44+ event TargetAllowlistEnabledSet (bool enabled );
3045
3146 modifier onlyOwner () {
3247 require (msg .sender == owner, "Only owner " );
@@ -69,15 +84,51 @@ contract OpenMatrixPaymaster is ReentrancyGuard {
6984 }
7085
7186 /// @notice Execute a sponsored call with ETH value
87+ /// @dev The owner is unrestricted. A non-owner AGENT is bound by the target
88+ /// allowlist (when enabled) and a per-agent daily value cap, so a
89+ /// compromised agent key cannot drain funds or reach an arbitrary
90+ /// target. The cap resets on a rolling 1-day bucket.
7291 function sponsoredCallWithValue (address target , bytes calldata data , uint256 value ) external onlyAuthorized nonReentrant returns (bytes memory ) {
7392 require (address (this ).balance >= value, "Insufficient balance " );
93+ if (msg .sender != owner && value > 0 ) {
94+ if (targetAllowlistEnabled) {
95+ require (allowedTargets[target], "Target not allowlisted " );
96+ }
97+ uint256 dayStart = block .timestamp - (block .timestamp % 1 days);
98+ if (agentDayStart[msg .sender ] != dayStart) {
99+ agentDayStart[msg .sender ] = dayStart;
100+ agentSpentToday[msg .sender ] = 0 ;
101+ }
102+ require (agentSpentToday[msg .sender ] + value <= agentDailyCap, "Agent daily cap exceeded " );
103+ agentSpentToday[msg .sender ] += value;
104+ }
74105 totalTransactions++ ;
75106 totalSponsored += tx .gasprice * gasleft ();
76107 (bool success , bytes memory result ) = target.call {value: value}(data);
77108 require (success, "Sponsored call failed " );
78109 return result;
79110 }
80111
112+ /// @notice Set the per-agent daily value cap (wei). Default 0 blocks all
113+ /// agent value-calls until the owner grants an allowance.
114+ function setAgentDailyCap (uint256 cap ) external onlyOwner {
115+ agentDailyCap = cap;
116+ emit AgentDailyCapSet (cap);
117+ }
118+
119+ /// @notice Allow/deny a target for agent value-calls (used when the
120+ /// allowlist is enabled).
121+ function setTargetAllowed (address target , bool allowed ) external onlyOwner {
122+ allowedTargets[target] = allowed;
123+ emit TargetAllowed (target, allowed);
124+ }
125+
126+ /// @notice Enable/disable the target allowlist for agent value-calls.
127+ function setTargetAllowlistEnabled (bool enabled ) external onlyOwner {
128+ targetAllowlistEnabled = enabled;
129+ emit TargetAllowlistEnabledSet (enabled);
130+ }
131+
81132 /// @notice Authorize an agent address to sponsor gas
82133 function authorizeAgent (address agent ) external onlyOwner {
83134 authorizedAgents[agent] = true ;
0 commit comments