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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ value-encoding rules, and the no-real-user-data guarantee.
- [RWA Protocol Threat Model](docs/threat-model.md) — protected assets, trust boundaries, threat catalog (compliance bypass, admin/role misuse, minting and transfer risks, pause misuse, event reliability), and explicit off-chain/legal out-of-scope items
- [Emergency Pause Policy](docs/emergency-pause.md) — global pause mechanism, authorization, and trust model
- [Admin Roles & Permissions](docs/admin-roles.md) — role-based access control (RBAC) design
- [Issuer Role Separation](docs/issuer-role-separation.md) — separation-of-duties controls for issuance: the duty map per role, the opt-in policy (dual-duty, self-issuance, independent-approver), the `check_issuance_authority` pre-flight read, and what the controls do and do not defend against

- [Admin Misuse Risks](docs/admin-misuse-risks.md) — threat model and mitigations
- [Supply Cap Amendment Governance](docs/supply-cap-governance.md) — 2-step cap amendment workflow and enforcement
- [Protocol Configuration Governance](docs/protocol-configuration.md) — global configuration module (`ProtocolConfig`) 2-step governance workflow and RWA guardrails
Expand Down
23 changes: 22 additions & 1 deletion docs/admin-roles.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This document describes the role-based access control (RBAC) system for the Aegi
| `Admin` | Supreme authority. Can perform all operations and manage roles. | All operations + role management + admin transfer |
| `ComplianceOfficer` | Manages the compliance whitelist and lifecycle. | `whitelist_user`, `revoke_whitelist`, `set_compliance_status`, `batch_set_compliance_status` |
| `AssetManager` | Manages asset minting and yield distribution. | `mint_asset`, `distribute_yield` |
| `EmergencyOfficer` | Combined compliance + asset privileges for operational flexibility. | `whitelist_user`, `revoke_whitelist`, `set_compliance_status`, `batch_set_compliance_status`, `mint_asset`, `distribute_yield` |
| `EmergencyOfficer` | Compliance privileges plus the emergency pause. **Not** an issuer: `mint_asset` and `distribute_yield` call `require_role(AssetManager)`, which admits only an `AssetManager` or the admin. | `whitelist_user`, `revoke_whitelist`, `set_compliance_status`, `batch_set_compliance_status`, `pause` |
| `None` | No role assigned. Cannot perform any privileged operation. | None (except `transfer` which requires self-auth) |

### Admin Bypass
Expand Down Expand Up @@ -78,6 +78,7 @@ All role changes emit Soroban events for off-chain indexing and audit trails:
| Admin renounced | `("admin_renounced",)` | `{ previous_admin, new_admin }` |
| Contract paused | `("contract_paused",)` | `{ admin }` |
| Contract unpaused | `("contract_unpaused",)` | `{ admin }` |
| Issuer separation policy updated | `("issuer_separation_policy_updated",)` | `{ admin, previous_policy, new_policy }` |

See [`events.md`](events.md) for the full event schema reference and
SDK/dashboard compatibility notes.
Expand Down Expand Up @@ -120,6 +121,7 @@ If the candidate does not accept, the transfer can be superseded by a new `trans
| `revoke_whitelist` | `ComplianceOfficer` | Yes |
| `set_compliance_status` | `ComplianceOfficer`* | Yes |
| `batch_set_compliance_status` | `ComplianceOfficer`* | Yes |
| `set_issuer_separation_policy` | `Admin` | N/A (admin-only) |
| `set_role` | `Admin` | N/A (admin-only) |
| `remove_role` | `Admin` | N/A (admin-only) |
| `transfer` | Self-auth | N/A |
Expand All @@ -129,13 +131,32 @@ If the candidate does not accept, the transfer can be superseded by a new `trans
`*` Moving an address out of `Blocked` is admin-only, including inside
`batch_set_compliance_status`.

## Separation of Duties

Roles say *which privileges* an address holds; duties say *which classes of
decision* it can make. Only the admin currently carries both the compliance and
the issuance duty, which means one key can clear an investor and then fund
them. [`issuer-role-separation.md`](issuer-role-separation.md) specifies the
duty map, the opt-in policy that forbids that combination (plus self-issuance
and same-approver issuance), the `check_issuance_authority` pre-flight read,
and the assumptions the controls rest on.

| Role | Compliance | Issuance | Emergency | Governance |
| --- | :---: | :---: | :---: | :---: |
| `ComplianceOfficer` | ✓ | | | |
| `AssetManager` | | ✓ | | |
| `EmergencyOfficer` | ✓ | | ✓ | |
| `Admin` | ✓ | ✓ | ✓ | ✓ |

## Storage Layout

| Key | Storage Type | Description |
|---|---|---|
| `DataKey::Admin` | Instance | The supreme admin address |
| `DataKey::AdminCandidate` | Instance | Pending admin during 2-step transfer |
| `DataKey::Role(Address)` | Persistent | The role assigned to an address |
| `DataKey::IssuerSeparationPolicy` | Instance | Issuer separation-of-duties policy (absent = permissive default) |
| `DataKey::ComplianceApprover(Address)` | Persistent | Address that last approved an investor's compliance |
| `DataKey::Whitelist(Address)` | Persistent | Whitelist flag (legacy, kept for compatibility) |
| `DataKey::Balance(Address)` | Persistent | Token balance |
| `DataKey::TotalSupply` | Instance | Global total supply |
10 changes: 6 additions & 4 deletions docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ pub struct ContractCapabilities {
| `supply_cap` | status | `Supported` | Global cap with 2-step governance. |
| `supply_cap_enforced` | `bool` **(runtime)** | `false` | A cap is currently active (`> 0`). |
| `yield_distribution` | status | `Planned` | `distribute_yield` emits an event only; it settles no value on-chain. |
| `issuer_separation` | status | `Supported` | Issuer separation-of-duties controls. See [`issuer-role-separation.md`](issuer-role-separation.md). |
| `issuer_separation_enforced` | `bool` | `false` | **Runtime**: whether the separation policy is currently enforced. |

#### `transfers`

Expand Down Expand Up @@ -222,6 +224,7 @@ Registry (also returned by `get_capability_keys()`):
| `burning` | `minting.burning` |
| `supply_cap` | `minting.supply_cap` |
| `yield_distribution` | `minting.yield_distribution` |
| `issuer_separation` | `minting.issuer_separation` |
| `transfers` | `transfers.transfers` |
| `holding_cap` | `transfers.holding_cap` |
| `allowances` | `transfers.allowances` |
Expand Down Expand Up @@ -257,10 +260,9 @@ for the full field reference and usage guidance.
## Versioning

`capability_version` is the schema version of the response
(`CAPABILITY_SCHEMA_VERSION`, currently `4` — last bumped when
`compliance.transition_guards` and the `compliance_transition_guards`
registry key were added); `contract_version` is the deployed crate's
semantic version.
(`CAPABILITY_SCHEMA_VERSION`, currently `5` — last bumped when the
`minting.issuer_separation` fields and the `issuer_separation` registry key
were added); `contract_version` is the deployed crate's semantic version.

Bump `capability_version` whenever a field is **added** to any capability
struct or a key is added to the registry, so an SDK pinned to an older schema
Expand Down
7 changes: 7 additions & 0 deletions docs/error-codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ variants can be added to a category without renumbering any other category.
| 3004 | `ContractPaused` | Admin/Auth | The operation is blocked because the contract is paused. |
| 3005 | `AlreadyPaused` | Admin/Auth | `pause` was called while already paused. |
| 3006 | `NotPaused` | Admin/Auth | `unpause` was called while not paused. |
| 3007 | `IssuanceDutyConflict` | Admin/Auth | Issuer separation is enforced and the caller holds both the compliance and issuance duties. See [`issuer-role-separation.md`](issuer-role-separation.md). |
| 3008 | `SelfIssuanceForbidden` | Admin/Auth | Issuer separation is enforced and the caller is the recipient of its own issuance. |
| 3009 | `IssuanceApproverConflict` | Admin/Auth | Issuer separation is enforced and the caller approved the recipient's compliance. |
| 4000 | `SenderNotWhitelisted` | Compliance | The sending address has no current clearance (`Unknown` or `Revoked`). |
| 4001 | `ReceiverNotWhitelisted` | Compliance | The receiving address has no current clearance (`Unknown` or `Revoked`). |
| 4002 | `SenderBlocked` | Compliance | The sending address is `Blocked` — sanctioned or frozen. |
Expand Down Expand Up @@ -91,6 +94,10 @@ has recommended user-facing copy in
perform this action." (Do not expose role internals to end users.)
- `3004`–`3006` (pause-related) → "This contract is currently paused for
maintenance. Please try again later."
- `3007`–`3009` (issuer separation) → "This issuance requires a different
authorized key." The action is not retryable by the same caller; route
the operator to a segregated issuance key rather than inviting a retry.
See [`issuer-role-separation.md`](issuer-role-separation.md).
- `4000`/`4001` → "This address has not completed compliance
verification." Prompt the user toward the whitelist/KYC flow rather
than showing a raw error.
Expand Down
1 change: 1 addition & 0 deletions docs/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ name, never on struct declaration order or Rust type layout.**
| `asset_minted` | `AssetMintedEvent` | `asset.rs` | `mint_asset` | `caller: Address`, `to: Address`, `amount: i128`, `total_supply: i128` |
| `transfer` | `TransferEvent` | `asset.rs` | `transfer` | `from: Address`, `to: Address`, `amount: i128` |
| `yield_distributed` | `YieldDistributedEvent` | `asset.rs` | `distribute_yield` | `caller: Address`, `amount: i128` |
| `issuer_separation_policy_updated` | `IssuerSeparationPolicyUpdatedEvent` | `issuer.rs` | `set_issuer_separation_policy` | `admin: Address`, `previous_policy: IssuerSeparationPolicy`, `new_policy: IssuerSeparationPolicy` |


> **Compliance transitions:** the authorisation, blocked (paused), and
Expand Down
Loading