PERIODIC_VOLUME_POLICY

component compliance

Cumulative transaction-volume limits per denom over a reset period (24h, 30d, etc.).

Tracks cumulative transaction volume per sender per denom over a configured reset period and rejects transactions that would push the running total above the limit. Distinct from VOLUME_POLICY, which checks each transaction independently. Used for daily / monthly transfer caps and Travel-Rule–style thresholds.

Solidity struct + ABI

From IPcl.sol:
struct UnitPeriodicVolumePolicy {
    uint256 maxAmount;           // maximum total amount per reset period
    uint64  resetPeriodSeconds;  // period length in seconds (e.g. 86400 for 24h)
}

struct PeriodicVolumePolicy {
    string[] tokens;                       // denom names, e.g. "aokrw"
    UnitPeriodicVolumePolicy[] limits;     // parallel array — limits[i] applies to tokens[i]
}
ABI tuple shorthand: (string[] tokens, (uint256 maxAmount, uint64 resetPeriodSeconds)[] limits).

Encode for PolicySet.policy:
UnitPeriodicVolumePolicy[] memory limits = new UnitPeriodicVolumePolicy[](1);
limits[0] = UnitPeriodicVolumePolicy({
    maxAmount: 1_000_000 ether,   // 1,000,000 OKRW per period (in aokrw)
    resetPeriodSeconds: 86_400    // 24h
});
string[] memory toks = new string[](1);
toks[0] = "aokrw";

PeriodicVolumePolicy memory pvp = PeriodicVolumePolicy({
    tokens: toks,
    limits: limits
});
bytes memory policyBytes = abi.encode(pvp);

Evaluation

For each token transferred, PCL maintains a PeriodicVolume accumulator per (scope, sender, denom):
struct PeriodicVolume {
    uint256 amount;              // accumulated amount in the current period
    uint256 maxAmount;           // policy limit
    uint64  resetPeriodSeconds;  // period length in seconds
    uint64  resetAt;             // unix timestamp at which the current period ends
}
Flow:
1. If block.timestamp >= resetAt → reset amount = 0, advance resetAt.
2. If amount + value > maxAmount → reject (ExceededPeriodicVolume).
3. Otherwise admit and add value to amount.

Accumulators are per-sender — sending to many different recipients does not split the counter. Read accumulators with globalPeriodicVolume(user, asset) or contractPeriodicVolume(contract, user, selector, asset).

ReasonCode on rejection

ExceededPeriodicVolume(uint256 maxLimit, uint256 value, uint256 resetAt) — wallet UX should show "daily/monthly limit reached; resets at <resetAt>". The resetAt field is a unix timestamp, render it in the user's locale.

Typical usage

  • Travel-Rule-style thresholds (e.g. maxAmount: 1,000,000 OKRW per 24h for un-attested users).
  • Anti-fraud / pace limits (e.g. cap a hot wallet at 10 M OKRW per 30d to catch compromise faster).
  • Combined with EAS_POLICY: the EAS-conditional variant OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY lets you have different limits for attested vs un-attested users — the more common production setup.
Source: maroo
ESC
Type to search