AGENT_OKRW_TRANSFER_LIMIT_POLICY

component compliance

Per-tx OKRW transfer cap for agent wallets, with the limit read from on-chain agent metadata (TransferLimit) — no policy parameters.

Caps a single OKRW transfer made by an agent wallet using a limit value stored as on-chain metadata on the ERC-8004 IdentityRegistry — specifically getMetadata(agentId, "TransferLimit"). The policy struct itself has no configurable fields; the cap is whatever the agent's owner has put in metadata. Skipped for non-agent senders. Used to bound how much value an agent (an automated key) can move per transaction without holding it back from human-controlled accounts.

Solidity struct & ABI tuple

From IPcl.sol:
// Limit is enforced from agent "TransferLimit" metadata only. Solidity disallows
// empty structs; `reserved` is ignored by the chain (callers may pass 0).
struct AgentOkrwTransferLimitPolicy {
    uint256 reserved;
}
ABI tuple shorthand: (uint256 reserved).

Encode for PolicySet.policy:
AgentOkrwTransferLimitPolicy memory p = AgentOkrwTransferLimitPolicy({
    reserved: 0   // ignored — present only because Solidity forbids empty structs
});
bytes memory policyBytes = abi.encode(p);
// PolicySet.templateId = "AGENT_OKRW_TRANSFER_LIMIT_POLICY"
The per-agent limit is not in the policy. It's set by writing a 32-byte uint256 into IdentityRegistry metadata under key "TransferLimit" for that agent ID.

How the policy is evaluated

For each native OKRW transfer:

1. Look up the agent IDs registered to the sender's wallet (via the Agent precompile / x/agent index).
2. If the sender owns no agent IDs → skip (this template is a no-op for non-agent wallets).
3. For each owned agent ID, read IdentityRegistry.getMetadata(agentId, "TransferLimit") and parse it as a 32-byte uint256.
4. The effective cap is the minimum across all agent IDs the wallet owns (most restrictive wins).
5. If value > cap → reject with ExceededAgentTransferLimit(maxLimit, value).

Non-OKRW assets are skipped — this template only meters the native denom.

Rejection reason codes

  • ExceededAgentTransferLimit(uint256 maxLimit, uint256 value) — agent's TransferLimit metadata enforced. Wallet UX: "this agent's per-transaction cap is X; raise the cap from the owner's account, or use a different signer".
  • AgentTransferLimitMetadataInvalid(string reason) — the metadata exists but isn't a parseable 32-byte uint256, or is empty when the policy expected a value. Almost always a setup bug; surface to the operator, not the end user.
  • AgentKeeperRequired() — internal: PCL was invoked without the agent keeper wired in (chain misconfiguration).

Setting the limit

The cap is configured per-agent on the IdentityRegistry, not per-policy:
// from the agent owner's wallet
IIdentityRegistry registry = IIdentityRegistry(0x8004000000000000000000000000000000000001);

// 1,000,000 OKRW cap, encoded as 32-byte big-endian uint256
bytes32 limit = bytes32(uint256(1_000_000 ether));
registry.setMetadata(agentId, "TransferLimit", abi.encodePacked(limit));
Clearing the metadata (or setting it to zero) means the agent has no transfer limit enforced by this template — but other policies (OKRW_EAS_TRANSFER_LIMIT_POLICY, VOLUME_POLICY) still apply if registered.

Typical usage

  • Agent budget caps: agent wallet for an automated trading or treasury bot, capped at e.g. 10,000,000 OKRW per transfer so a compromised key can't drain the principal in one tx.
  • Owner-controlled tuning: the cap moves with metadata, so the human owner can dial it up or down without going through PCL governance.
  • Combine with OKRW_EAS_PERIODIC_VOLUME_LIMIT_POLICY for a per-tx + cumulative-period two-layer guard on agent wallets.
Source: maroo
ESC
Type to search