Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ function invertedPolicyId(uint64 policyId) external view returns (uint64);
| Symbol | Selector | Status | Behavior |
| --- | --- | --- | --- |
| `invertedPolicyId(uint64)` | `0x6b468933` | New view | Toggles bit 63 (`policyId ^ INVERTED_POLICY_BIT`). Never reverts, reads no state, and is involutive. |
| `isAuthorized(uint64,address)` | Unchanged | Extended | An inverted ID resolves the base and returns the negated result. Fail-closed on an unknown or malformed base. |
| `isAuthorized(uint64,address)` | Unchanged | Extended | An inverted ID resolves the base and returns the negated result when the base exists. An inverted unknown or malformed base returns `false`. |
| `policyExists(uint64)` | Unchanged | Extended | Strips to base: `policyExists(invertedPolicyId(id)) == policyExists(id)`. |
| `policyAdmin(uint64)` | Unchanged | Extended | Strips to base. |
| `pendingPolicyAdmin(uint64)` | Unchanged | Extended | Strips to base. |
Expand All @@ -69,7 +69,8 @@ function invertedPolicyId(uint64 policyId) external view returns (uint64);

#### Authorization

`isAuthorized` gains a leading invert branch. All non-inverted paths are unchanged.
`isAuthorized` gains a leading invert branch. All non-inverted paths are unchanged. `isAuthorized`
never reverts.

```text Authorization Evaluation lines expandable wrap highlight={2-6}
isAuthorized(policyId, account):
Expand All @@ -83,7 +84,16 @@ isAuthorized(policyId, account):
```

Inverting a composite negates the composite's combined result. An inverted ID over a never-created
base returns `false`; it never becomes allow-everyone.
or malformed base returns `false`; it never becomes allow-everyone.

The following table shows how `isAuthorized` resolves each kind of ID.

| ID | Result |
| --- | --- |
| Malformed | `false`. The invert flag (bit 63) is cleared before the type check, so an ID is malformed when the remaining top byte is outside `PolicyType`. |
| Well-formed but unknown | Treated as an empty set: `ALLOWLIST` and `UNION` return `false`; `BLOCKLIST` and `INTERSECT` return `true`. |
| Inverted, base exists | The negated result of the base. |
| Inverted, base unknown or malformed | `false`. |

#### Getters Strip to Base

Expand Down Expand Up @@ -162,6 +172,8 @@ composites are unaffected. To adopt:
it treats the ID as an opaque `uint64`.
3. Consumers that store policy IDs must still validate `policyExists(policyId)` at write time. This
works for inverted IDs because existence resolves to the base.
4. Do not rely on an inverted ID to allow an unknown or malformed base. `isAuthorized` returns
`false` for it, and returns `false` for any malformed ID.

For the current interface, see the
[IPolicyRegistry reference](/specifications/b20/reference/interfaces/i-policy-registry/index).
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ The B20 contract provides ERC-8056 helper functions for common calculations, whe

To comply with any regulatory requirements that apply to a particular asset, [policies](/specifications/b20/reference/interfaces/i-policy-registry) that manage allowlists and blocklists may be implemented. Policies determine whether a transfer is allowed or rejected.

The B20 contract provides the [`isAuthorized(policyID, account)`](/specifications/b20/reference/interfaces/i-policy-registry/is-authorized) function, which you can use to determine whether a specific account is allowed to transfer funds.
The B20 contract provides the [`isAuthorized(policyID, account)`](/specifications/b20/reference/interfaces/i-policy-registry/is-authorized) function, which you can use to determine whether a specific account is allowed to transfer funds. It never reverts, so a policy ID that doesn't exist still returns a result; check `policyExists(policyId)` before you rely on it.

<Note>
The standard [`approve()`](/specifications/b20/reference/interfaces/ib20/approve) function is not policy gated. Checking whether a quantity of funds is approved to be transferred does not guarantee that the funds aren't blocked by a policy.
Expand Down
2 changes: 2 additions & 0 deletions docs/build-on-base/issue-rwa/seize-and-cancel-units.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ How the seize scope works:

Because the holder check is inverted, attach a **blocklist** to `SEIZE_EXEMPT_POLICY`. Accounts on the list are unauthorized and therefore seizable; every other holder stays exempt. Never attach an allowlist or `ALWAYS_BLOCK` to this scope: an empty allowlist authorizes nobody, so every holder would be seizable.

`isAuthorized` never reverts: a malformed ID, or an inverted ID whose base doesn't exist, returns `false`. On `SEIZE_EXEMPT_POLICY`, `false` makes the holder seizable, so confirm `policyExists(policyId)` before you attach an ID. See [`isAuthorized`](/specifications/b20/reference/interfaces/i-policy-registry/is-authorized) for the full rules.

## Seize, Cancel, and Verify

Grant the roles once per token:
Expand Down
2 changes: 1 addition & 1 deletion docs/specifications/b20/concepts/policies.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ An issuer may want the opposite of an existing policy without a second member se

Call `invertedPolicyId(policyId)` to set or clear that bit. You can also set bit 63 yourself. Bind the inverted ID to a token scope, or pass it as a composite child ("A AND NOT X"). The flag applies to every type: `ALLOWLIST`, `BLOCKLIST`, `UNION`, and `INTERSECT`.

`isAuthorized` on an inverted ID returns the opposite of the base. If the base does not exist, the result is `false`. That fail-closed guard prevents a mistyped inverted ID from becoming allow-everyone.
`isAuthorized` on an inverted ID returns the opposite of the base when that base exists. If the base is unknown or malformed, the result is `false`. That fail-closed guard prevents a mistyped inverted ID from becoming allow-everyone.

Read views strip bit 63 and load the base. `policyExists` and `policyAdmin` on an inverted ID match the base. An inverted ID has no record of its own.

Expand Down
2 changes: 2 additions & 0 deletions docs/specifications/b20/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ Those lists live in the Policy Registry, a global singleton precompile, not on t

A token admin binds a policy ID to a policy scope with `updatePolicy`. A scope sits in a similar place to a hook: it runs on a specific function. When that function runs, the token asks the registry `isAuthorized(policyId, account)` and reverts with `PolicyForbids` if the check fails. Which scope runs on which function is in [Policies](/specifications/b20/concepts/policies).

`isAuthorized` never reverts, even for a policy ID that doesn't exist: a malformed ID returns `false`, and an unknown ID behaves as an empty set. Validate `policyExists(policyId)` when you store a policy ID. See [`isAuthorized`](/specifications/b20/reference/interfaces/i-policy-registry/is-authorized) for every case.

A policy-gated transfer looks like this:

```mermaid Integrating Compliance Checks Diagram lines wrap expandable highlight={1}
Expand Down
2 changes: 1 addition & 1 deletion docs/specifications/b20/reference/constants.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ description: "B20 precompile addresses, role identifiers, policy scopes, and val
| Name | Value | Purpose |
|---|---|---|
| `B20_FACTORY_ADDRESS` | `0xB20f000000000000000000000000000000000000` | Deploys and looks up B-20 tokens; every asset and stablecoin instance is created through the [`IB20Factory`](/specifications/b20/reference/interfaces#ib20factory) at this address. |
| `POLICY_REGISTRY_ADDRESS` | `0x8453000000000000000000000000000000000002` | Stores allowlist/blocklist/composite policies and answers `isAuthorized` checks consulted by every policy scope (see [Policies](/specifications/b20/concepts/policies)). |
| `POLICY_REGISTRY_ADDRESS` | `0x8453000000000000000000000000000000000002` | Stores allowlist/blocklist/composite policies and answers `isAuthorized` checks consulted by every policy scope (see [Policies](/specifications/b20/concepts/policies)). `isAuthorized` never reverts and returns false for a malformed policy ID. |
| `ACTIVATION_REGISTRY_ADDRESS` | `0x8453000000000000000000000000000000000001` | Gates whether a B-20 variant or feature is live on a given chain; checked by the factory before it will create that variant. |

## Roles
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ description: "Generated B20 reference for IPolicyRegistry functions, events, and
| [`updateAllowlist`](/specifications/b20/reference/interfaces/i-policy-registry/update-allowlist) | `0x3388fb5b` | Sets `accounts` membership in an ALLOWLIST policy to `allowed` in one batch. |
| [`updateBlocklist`](/specifications/b20/reference/interfaces/i-policy-registry/update-blocklist) | `0x5c4e51b8` | Sets `accounts` membership in a BLOCKLIST policy to `blocked` in one batch. |
| [`updateComposite`](/specifications/b20/reference/interfaces/i-policy-registry/update-composite) | `0xbfe142c0` | Replaces a composite policy's child-policy set in full with `childPolicyIds`. |
| [`isAuthorized`](/specifications/b20/reference/interfaces/i-policy-registry/is-authorized) | `0x55a1179e` | Returns whether `account` is authorized under `policyId`. Never reverts; unknown |
| [`isAuthorized`](/specifications/b20/reference/interfaces/i-policy-registry/is-authorized) | `0x55a1179e` | Returns whether `account` is authorized under `policyId`. Never reverts. A malformed ID returns false; a well-formed unknown ID is an empty set (ALLOWLIST and UNION return false, BLOCKLIST and INTERSECT return true). An inverted ID negates the base result only when the base exists; an inverted unknown or malformed base returns false. |
| [`MIN_COMPOSITE_CHILD_POLICIES`](/specifications/b20/reference/interfaces/i-policy-registry/min-composite-child-policies) | `0xb3ae29f7` | Minimum number of child policies a composite must reference, inclusive. Never reverts. |
| [`MAX_COMPOSITE_CHILD_POLICIES`](/specifications/b20/reference/interfaces/i-policy-registry/max-composite-child-policies) | `0x54309870` | Maximum number of child policies a composite may reference, inclusive. Never reverts. |
| [`policyExists`](/specifications/b20/reference/interfaces/i-policy-registry/policy-exists) | `0x330f5637` | Returns whether `policyId` is a built-in sentinel or a previously-assigned custom ID. Never reverts. |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "IPolicyRegistry.isAuthorized"
description: "Generated B20 reference for isAuthorized(uint64,address)."
description: "Returns whether an account is authorized under a policy ID, including results for malformed, unknown, and inverted IDs."
---


Expand All @@ -18,17 +18,38 @@ function isAuthorized(uint64 policyId, address account) external view returns (b

## Description

Returns whether `account` is authorized under `policyId`. Never reverts; unknown
or malformed IDs collapse to empty-member-set semantics (ALLOWLIST -&gt; false,
BLOCKLIST -&gt; true).
Dev: Callers that store policy IDs MUST validate `policyExists(policyId)` at write time.
Param: policyId Policy to query.
Param: account Account to check.
Return: Whether `account` is authorized.
Returns whether `account` is authorized under `policyId`. Never reverts.

## Result by policy ID

| Policy ID | Result |
|---|---|
| Malformed | `false`. The invert flag (bit 63) is cleared before the type check, so an ID is malformed when the remaining top byte is outside `PolicyType`. |
| Well-formed but unknown, ALLOWLIST or UNION | `false`. An unknown ID is treated as an empty set. |
| Well-formed but unknown, BLOCKLIST or INTERSECT | `true`. An unknown ID is treated as an empty set. |
| Inverted, base exists | The negated result of the base. Applies to every policy type. |
| Inverted, base unknown or malformed | `false` |

<Warning>
Callers that store policy IDs MUST validate `policyExists(policyId)` at write time. An unknown `BLOCKLIST` or `INTERSECT` ID returns `true`, and an inverted unknown or malformed base returns `false` rather than the negation of the empty-set result.
</Warning>

## Parameters

| Name | Type | Description |
|---|---|---|
| `policyId` | `uint64` | Policy to query. |
| `account` | `address` | Account to check. |

## Returns

| Type | Description |
|---|---|
| `bool` | Whether `account` is authorized under `policyId`. |

## Access Control

Read-only or ERC-20-standard access rules unless the NatSpec states otherwise.
Read-only. No role required.

## Policy Interaction

Expand Down
Loading