Skip to content

PoC: IPolicyRegistry with AND/OR composition + stateful primitives forward-compat#5

Draft
amiecorso wants to merge 1 commit into
mainfrom
amie/policy-registry-poc
Draft

PoC: IPolicyRegistry with AND/OR composition + stateful primitives forward-compat#5
amiecorso wants to merge 1 commit into
mainfrom
amie/policy-registry-poc

Conversation

@amiecorso
Copy link
Copy Markdown
Collaborator

Summary

PoC for the future-extensible IPolicyRegistry design discussed in chat. Goal: structure the registry interface so we can ship a minimal v1 (just adds the redeemer slot to compound) without locking ourselves out of the obvious next year of evolution (rate limits, time locks, amount caps, AND/OR composition).

Not for merge. Posting so the team can react to the design before we commit to anything.

What ships in v1 (the only structural change)

What's drafted as forward-compat (signatures present, implementations stub until future hardforks)

  • AND / OR combinator policy types. Variable-length constituents, bounded by MAX_COMBINATOR_CONSTITUENTS and MAX_COMPOSITION_DEPTH. Cycles structurally impossible (every constituent must already exist when the combinator is created, so the policy graph is a DAG with edges always pointing from higher IDs to lower).
  • RATE_LIMIT, TIME_LOCK, AMOUNT_CAP stateful primitives. Per-address state, per-policy admin, classic linear-replenishment / unlock-timestamp / per-tx-cap semantics.
  • Per-operation mutating authorize functions (`authorizeTransfer`, `authorizeMint`, `authorizeRedeem`) that take the full operation context (`from`, `to`, `amount`) and revert on denial with typed errors. Replaces TIP-403/1015's view-only `isAuthorized*`.
  • Preview view counterparts (`previewAuthorize*`) for off-chain simulation. Return bool, never revert, never mutate.

Key forward-compat property

The hot-path authorize signatures take `amount` from day one even though no v1 policy uses it. This means tokens written today work with policies invented in 2027 without any code or ABI changes. New stateful policy types just plug into the existing graph composition.

Composition example: "KYC AND rate-limit on sender, KYC on recipient"

```
kycPolicyId = createPolicy(adminAddr, WHITELIST) // ID 17
rateLimitPolicyId = createRateLimitPolicy(adminAddr, 1000e6, 86400) // ID 23
senderAndPolicyId = createAndPolicy([17, 23]) // ID 31
compoundPolicyId = createCompoundPolicy(
senderPolicyId = 31, // AND[KYC, rate-limit]
recipientPolicyId = 17, // KYC only
mintRecipientPolicyId = 1, // always-allow
redeemerPolicyId = 0 // no redemption
) // ID 47
token.changeTransferPolicyId(47)
```

Token only ever stores ONE policy ID. The composition tree lives in the registry. AND/OR can nest arbitrarily up to `MAX_COMPOSITION_DEPTH` (sketched as 8 levels).

Notable design decisions

  • Authorize REVERTS on denial, returns `true` on success. Atomic state mutations (rate-limit consumption gets reverted naturally if a later check fails); typed error reasons (`RateLimitExceeded(remaining, requested)` etc.) for integrator debug.
  • AND/OR cannot contain COMPOUND. Combinators evaluate on a single `(user, amount)` context; COMPOUND needs multiple contexts. The two compose: a COMPOUND slot can reference an AND.
  • OR with stateful constituents: evaluate non-mutatingly until one authorizes, then re-run that one in mutating mode to commit. Avoids consuming rate-limit capacity on constituents that get denied.
  • Future-hardfork function signatures live in the v1 ABI. Implementations stub-revert with `InvalidPolicyType` until enabled. This way no token or admin-tooling ABI break when new policies actually ship.

Open questions

  1. Spender slot. Worth a 5th `spenderPolicyId` slot for explicit asymmetric handling, or keep using `senderPolicyId` for both `from` and `msg.sender`?
  2. Admin power level for stateful primitives. Should the policy admin be able to reset per-address rate-limit state? It's effectively "give this user free capacity."
  3. OR preview semantics. Should `previewAuthorize` simulate the consume-on-first-success behavior, or just check whether ANY constituent could authorize?
  4. `MAX_COMPOSITION_DEPTH` value. Sketched as 8. Right number?

Status

Draft. Looking for team reaction on the overall shape before committing to any of this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant