IPcl.globalPeriodicVolume

globalPeriodicVolume(
  address user,
  string calldata asset,
  uint64 resetPeriodSeconds,
  bool resolveAgentOwners
) external view returns (PeriodicVolume[] memory statuses)

Returns every global periodic-volume counter that shares the requested (asset, resetPeriodSeconds) pair for user. The array is empty when no global PERIODIC_VOLUME_POLICY matches the (asset, reset-period) tuple; otherwise it contains one PeriodicVolume entry per registered limit — a single reset period can back several distinct maxAmount variants, and each is returned so callers can pick the tightest applicable cap. Set resolveAgentOwners to true to read an agent wallet's owners' counters instead — the largest of them is returned.

Parameters

Name Type Required Description
user address ✓ The account whose accumulated volume should be read.
asset string ✓ Asset identifier as declared in the PeriodicVolumePolicy.tokens array (typically the base denom, e.g. "atokrw"). Normalized server-side.
resetPeriodSeconds uint64 ✓ The period length in seconds identifying which counter to read (for example 86400 for a 24-hour window). Only entries whose registered period equals this value are returned.
resolveAgentOwners bool ✓ When true, user is read as an agent wallet: the counters of its agents' owners are read (user's own when it has none) and the largest is returned. The owners' counters hold an agent wallet's transfers only when the cap is wrapped in FOR_EACH_POLICY over agent owners. maroo-testnet-1's global daily cap is wrapped that way, so on the testnet true returns the owner nearest the cap. When false, only user's own counter is read.

Returns

Type: PeriodicVolume[]

Array of PeriodicVolume { uint256 amount; uint256 maxAmount; uint64 resetPeriodSeconds; uint64 resetAt; } — one entry per registered limit matching the (asset, reset-period) tuple. Empty array means no policy is currently registered for that tuple.

Examples

Read every global daily OKRW cap that applies to a user

Because multiple limits can share the same (asset, reset-period) tuple, clients must fold across the returned array — usually by taking the minimum remaining headroom — instead of assuming a single record.

import { createPublicClient, http } from "viem";

const PCL = "0x1000000000000000000000000000000000000005" as const;
const pclAbi = [{
  name: "globalPeriodicVolume", type: "function", stateMutability: "view",
  inputs: [
    { name: "user",                type: "address" },
    { name: "asset",               type: "string" },
    { name: "resetPeriodSeconds",  type: "uint64" },
    { name: "resolveAgentOwners",  type: "bool" },
  ],
  outputs: [{
    type: "tuple[]",
    components: [
      { name: "amount",             type: "uint256" },
      { name: "maxAmount",          type: "uint256" },
      { name: "resetPeriodSeconds", type: "uint64" },
      { name: "resetAt",            type: "uint64" },
    ],
  }],
}] as const;

const client = createPublicClient({ transport: http("https://rpc-testnet.maroo.io") });

// TODO: replace with the real end-user address before production.
const statuses = await client.readContract({
  address: PCL,
  abi: pclAbi,
  functionName: "globalPeriodicVolume",
  args: ["0x8F3ac2B1d9E74c05A6B18FE27Dc4913e5A0F7b62", "atokrw", 86_400n, false],
});

if (statuses.length === 0) {
  console.log("no periodic-volume policy is currently registered for this (asset, period)");
} else {
  // Pick the tightest applicable cap — remaining headroom is min(maxAmount - amount) across entries.
  // `amount` can pass `maxAmount`: the testnet records volume for attested senders too, who are not held to the cap.
  const left = (s: (typeof statuses)[number]) => (s.amount >= s.maxAmount ? 0n : s.maxAmount - s.amount);
  const headroom = statuses.reduce<bigint>((min, s) => (left(s) < min ? left(s) : min), left(statuses[0]));
  console.log(`tightest remaining headroom: ${headroom} atokrw`);
}

Reading the same counter from Solidity

The Solidity view returns an in-memory array, so contracts can iterate and pick the tightest cap without any additional RPC calls.

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.28;

import "@maroo-chain/contracts/precompiles/pcl/IPcl.sol";

contract HeadroomView {
    IPcl constant pcl = IPcl(0x1000000000000000000000000000000000000005);

    /// @return tightest Minimum remaining headroom across every registered
    ///         global periodic-volume limit for the (atokrw, 24h) counter.
    function remainingDailyHeadroom(address user) external view returns (uint256 tightest) {
        PeriodicVolume[] memory rows =
            pcl.globalPeriodicVolume(user, "atokrw", 86_400, false);
        if (rows.length == 0) {
            return type(uint256).max; // no policy → unbounded
        }
        tightest = type(uint256).max;
        for (uint256 i = 0; i < rows.length; ++i) {
            // amount can pass maxAmount (attested senders are recorded but not held to the cap)
            uint256 left = rows[i].amount >= rows[i].maxAmount ? 0 : rows[i].maxAmount - rows[i].amount;
            if (left < tightest) tightest = left;
        }
    }
}
ESC
Type to search