> ## Documentation Index
> Fetch the complete documentation index at: https://base-a060aa97-docs-sync-code-change-91427ab.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# IPolicyRegistry.createCompositePolicy

> Creates a new composite policy that combines existing simple policies under a UNION or INTERSECT logic gate.

## Signature

```solidity IPolicyRegistry.sol theme={null}
function createCompositePolicy(address admin, PolicyType policyType, uint64[] calldata childPolicyIds) external returns (uint64 newPolicyId);
```

| Field               | Value                                           |
| ------------------- | ----------------------------------------------- |
| Selector            | `0x6fdd1491`                                    |
| Canonical signature | `createCompositePolicy(address,uint8,uint64[])` |

## Description

Creates a composite policy that combines two to four existing simple policies (`ALLOWLIST` or `BLOCKLIST`) under a single logic gate:

| `policyType` | Authorized when                    |
| ------------ | ---------------------------------- |
| `UNION`      | Any child authorizes the account   |
| `INTERSECT`  | Every child authorizes the account |

The registry stores references to the children, not a snapshot of their members. Every call to `isAuthorized` reads each child's current member set, so updates to a child policy are immediately visible through the composite.

A child policy ID may carry the invert flag (bit 63, set via `invertedPolicyId`). The registry validates and stores the base policy ID while preserving the invert flag. At authorization time, the inverted child returns the opposite of its base's result. An inverted composite child is not valid and reverts `InvalidChildPolicy`. Across the whole child set, `PolicyNotFound` takes precedence over `InvalidChildPolicy`.

Creation is permissionless. The `admin` you supply is the only address that can later call `updateComposite`, `stageUpdateAdmin`, or `renounceAdmin` on this policy.

On success, emits `PolicyCreated(newPolicyId, creator, policyType)`, `PolicyAdminUpdated(newPolicyId, address(0), admin)`, and `CompositePolicyUpdated(newPolicyId, creator, childPolicyIds)`.

## Parameters

| Name             | Type                   | Description                                                                                                                                                                                                                                                |
| ---------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `admin`          | `address`              | Initial admin authorized to update child policies and transfer or renounce administration. Cannot be `address(0)`.                                                                                                                                         |
| `policyType`     | `PolicyType` (`uint8`) | Must be `UNION` or `INTERSECT`.                                                                                                                                                                                                                            |
| `childPolicyIds` | `uint64[]`             | IDs of existing simple policies to combine. Count must be in `[MIN_COMPOSITE_CHILD_POLICIES, MAX_COMPOSITE_CHILD_POLICIES]` (2–4). A child ID may have its invert flag (bit 63) set; the registry validates and stores the base while preserving the flag. |

## Returns

| Name          | Type     | Description                             |
| ------------- | -------- | --------------------------------------- |
| `newPolicyId` | `uint64` | The newly assigned composite policy ID. |

## Reverts

| Error                               | Condition                                                                                                                |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `ZeroAddress()`                     | `admin` is `address(0)`                                                                                                  |
| `IncompatiblePolicyType()`          | `policyType` is not `UNION` or `INTERSECT`                                                                               |
| `ChildPoliciesOutsideOfRange()`     | `childPolicyIds.length` is outside `[MIN_COMPOSITE_CHILD_POLICIES, MAX_COMPOSITE_CHILD_POLICIES]` (2–4)                  |
| `PolicyNotFound()`                  | Any child policy ID's base does not exist in the registry (checked first, across the whole set)                          |
| `InvalidChildPolicy(childPolicyId)` | Any child's base is not a simple policy (composites, inverted composites, and built-in sentinels are not valid children) |
| Panic `0x11`                        | The policy ID counter has reached its maximum value                                                                      |

## Access Control

Permissionless, any caller may create a composite policy.

## Example

```solidity Create a KYC-and-sanctions composite lines wrap expandable highlight={10} theme={null}
// 1. Create a KYC allowlist and a sanctions blocklist first.
uint64 kycId       = registry.createPolicy(admin, PolicyType.ALLOWLIST);
uint64 sanctionsId = registry.createPolicy(admin, PolicyType.BLOCKLIST);

// 2. Combine them: an account must be KYC'd AND not sanctioned.
uint64[] memory children = new uint64[](2);
children[0] = kycId;
children[1] = sanctionsId;

uint64 gateId = registry.createCompositePolicy(admin, PolicyType.INTERSECT, children);

// 3. Bind the composite to the token's transfer and mint scopes.
token.updatePolicy(B20Constants.TRANSFER_SENDER_POLICY, gateId);
token.updatePolicy(B20Constants.TRANSFER_RECEIVER_POLICY, gateId);
token.updatePolicy(B20Constants.MINT_RECEIVER_POLICY, gateId);
```

```solidity KYC and not on an exclusion list lines wrap expandable highlight={9,11} theme={null}
// 1. Create a KYC allowlist and an exclusion allowlist.
uint64 kycId       = registry.createPolicy(admin, PolicyType.ALLOWLIST);
uint64 exclusionId = registry.createPolicy(admin, PolicyType.ALLOWLIST);

// 2. Invert the exclusion ID so the child evaluates as "not on the list".
uint64 notExcluded = registry.invertedPolicyId(exclusionId);

// 3. INTERSECT: KYC'd AND not on the exclusion list.
uint64[] memory children = new uint64[](2);
children[0] = kycId;
children[1] = notExcluded;

uint64 gateId = registry.createCompositePolicy(admin, PolicyType.INTERSECT, children);
token.updatePolicy(B20Constants.TRANSFER_RECEIVER_POLICY, gateId);
```

<Note>
  Updating a child policy's membership (via `updateAllowlist` or `updateBlocklist`) takes effect on the next `isAuthorized` call through any composite that references it. No second `updateComposite` is needed on the token. This applies to inverted children too — updating the base updates the inverse.
</Note>
