From 268b1d1fd3d86e29c49e236c839f57e07360c8d3 Mon Sep 17 00:00:00 2001 From: Ajibose Date: Sun, 28 Jun 2026 14:41:00 +0300 Subject: [PATCH] feat(vault): add capabilities() bitmap for client feature detection Exposes a pure-view `capabilities() -> u64` function on CalloraVault that returns a stable bitmask of every feature the contract supports. Each of the 18 assigned bits maps to a specific capability (deposit, withdraw, deduct, batch_deduct, pause, authorized_caller, offering_metadata, price_registry, request_idempotency, two_step_ownership, two_step_admin, settlement, revenue_pool, rate_limit, admin_broadcast, depositor_allowlist, slippage_guard, upgrade). Bits 18-63 are reserved and always zero. - contracts/vault/src/capabilities.rs: bit constants and capabilities() - contracts/vault/src/lib.rs: expose CalloraVault::capabilities(), wire module - contracts/vault/src/test_capabilities.rs: 25 tests covering every bit, stability, reserved-bits-zero, idempotency, and no-init availability - docs/CAPABILITIES.md: full bit registry with integration examples --- contracts/vault/src/capabilities.rs | 142 ++++++++++++ contracts/vault/src/lib.rs | 21 ++ contracts/vault/src/test_capabilities.rs | 281 +++++++++++++++++++++++ docs/CAPABILITIES.md | 84 +++++++ 4 files changed, 528 insertions(+) create mode 100644 contracts/vault/src/capabilities.rs create mode 100644 contracts/vault/src/test_capabilities.rs create mode 100644 docs/CAPABILITIES.md diff --git a/contracts/vault/src/capabilities.rs b/contracts/vault/src/capabilities.rs new file mode 100644 index 00000000..9830ba91 --- /dev/null +++ b/contracts/vault/src/capabilities.rs @@ -0,0 +1,142 @@ +//! Capability bitmap for the Callora Vault contract. +//! +//! Each bit in the `u64` returned by [`capabilities`] represents a distinct, +//! stable feature supported by this contract version. Bits are assigned once +//! and never reassigned to a different feature, so clients can rely on them +//! across upgrades. +//! +//! # Quick-start +//! ```ignore +//! let caps = client.capabilities(); +//! if caps & CAP_BATCH_DEDUCT != 0 { +//! // safe to call batch_deduct +//! } +//! ``` +//! +//! See `docs/CAPABILITIES.md` for the full bit registry and integration guide. + +use soroban_sdk::Env; + +/// Bit 0 — Deposit: `deposit()` accepts USDC from allowlisted callers. +/// Introduced: v1.0.0 +pub const CAP_DEPOSIT: u64 = 1 << 0; + +/// Bit 1 — Withdraw: owner can withdraw USDC via `withdraw()` / `withdraw_to()`. +/// Introduced: v1.0.0 +pub const CAP_WITHDRAW: u64 = 1 << 1; + +/// Bit 2 — Deduct: authorized caller can deduct funds via `deduct()`. +/// Introduced: v1.0.0 +pub const CAP_DEDUCT: u64 = 1 << 2; + +/// Bit 3 — Batch deduct: authorized caller deducts multiple items atomically via `batch_deduct()`. +/// Introduced: v1.0.0 +pub const CAP_BATCH_DEDUCT: u64 = 1 << 3; + +/// Bit 4 — Pause circuit-breaker: admin or owner can pause/unpause the vault via +/// `pause()` / `unpause()`. Deposits and deducts are blocked while paused. +/// Introduced: v1.0.0 +pub const CAP_PAUSE: u64 = 1 << 4; + +/// Bit 5 — Authorized caller: a designated address is permitted to invoke deduct +/// operations. Configured via `set_authorized_caller()`. +/// Introduced: v1.0.0 +pub const CAP_AUTHORIZED_CALLER: u64 = 1 << 5; + +/// Bit 6 — Offering metadata: per-offering metadata stored and queried on-chain. +/// Managed via `set_metadata()`, `update_metadata()`, `remove_metadata()`. +/// Introduced: v1.0.0 +pub const CAP_OFFERING_METADATA: u64 = 1 << 6; + +/// Bit 7 — Price registry: per-offering prices stored on-chain. +/// Managed via `set_price()`, `get_price()`, `remove_price()`, `list_prices()`. +/// Introduced: v1.0.0 +pub const CAP_PRICE_REGISTRY: u64 = 1 << 7; + +/// Bit 8 — Request idempotency: `deduct` and `batch_deduct` accept an optional +/// `request_id` that prevents duplicate execution (at-least-once retry semantics). +/// Introduced: v1.0.0 +pub const CAP_REQUEST_IDEMPOTENCY: u64 = 1 << 8; + +/// Bit 9 — Two-step ownership transfer: ownership moves via `transfer_ownership()` / +/// `accept_ownership()`, preventing accidental loss. +/// Introduced: v1.0.0 +pub const CAP_TWO_STEP_OWNERSHIP: u64 = 1 << 9; + +/// Bit 10 — Two-step admin transfer: the admin role moves via `set_admin()` / +/// `accept_admin()` / `cancel_admin_transfer()`. +/// Introduced: v1.0.0 +pub const CAP_TWO_STEP_ADMIN: u64 = 1 << 10; + +/// Bit 11 — Settlement integration: deducted funds are forwarded to a settlement +/// contract via `receive_payment()`. Configured via `set_settlement()`. +/// Introduced: v1.0.0 +pub const CAP_SETTLEMENT: u64 = 1 << 11; + +/// Bit 12 — Revenue pool: an optional revenue pool address is configurable via a +/// two-step `propose_revenue_pool()` / `accept_revenue_pool()` pattern. +/// Introduced: v1.0.0 +pub const CAP_REVENUE_POOL: u64 = 1 << 12; + +/// Bit 13 — Developer rate limiting: per-developer token-bucket rate limits are +/// enforced on deduct operations. Configured via `set_developer_rate_limit()`. +/// Introduced: v1.0.0 +pub const CAP_RATE_LIMIT: u64 = 1 << 13; + +/// Bit 14 — Admin broadcast: admin can emit signed on-chain messages with severity +/// levels via `broadcast()`. +/// Introduced: v1.0.0 +pub const CAP_ADMIN_BROADCAST: u64 = 1 << 14; + +/// Bit 15 — Depositor allowlist: owner restricts deposits to approved addresses. +/// Managed via `add_address()`, `set_allowed_depositor()`, `clear_all()`, +/// `get_allowlist()`. +/// Introduced: v1.0.0 +pub const CAP_DEPOSITOR_ALLOWLIST: u64 = 1 << 15; + +/// Bit 16 — Slippage guard: `deduct` enforces a caller-supplied `max_fee_bps` cap +/// expressed as basis points of the current vault balance. +/// Introduced: v1.0.0 +pub const CAP_SLIPPAGE_GUARD: u64 = 1 << 16; + +/// Bit 17 — Contract upgrade: admin can replace the WASM via `upgrade()`. +/// Introduced: v1.0.0 +pub const CAP_UPGRADE: u64 = 1 << 17; + +// Bits 18–63 are reserved for future capabilities and are always zero. + +/// Bitmask of all capabilities exposed by this contract version. +/// +/// Combine individual `CAP_*` constants with `&` to test for a specific feature: +/// ```ignore +/// assert!(caps & CAP_DEPOSIT != 0); +/// ``` +pub const ALL_CAPABILITIES: u64 = CAP_DEPOSIT + | CAP_WITHDRAW + | CAP_DEDUCT + | CAP_BATCH_DEDUCT + | CAP_PAUSE + | CAP_AUTHORIZED_CALLER + | CAP_OFFERING_METADATA + | CAP_PRICE_REGISTRY + | CAP_REQUEST_IDEMPOTENCY + | CAP_TWO_STEP_OWNERSHIP + | CAP_TWO_STEP_ADMIN + | CAP_SETTLEMENT + | CAP_REVENUE_POOL + | CAP_RATE_LIMIT + | CAP_ADMIN_BROADCAST + | CAP_DEPOSITOR_ALLOWLIST + | CAP_SLIPPAGE_GUARD + | CAP_UPGRADE; + +/// Return the capability bitmap for this contract. +/// +/// Each set bit signals a supported feature. Bits are stable across upgrades — +/// once assigned a bit position is never reused for a different feature. +/// Reserved bits (18–63) are always zero. +/// +/// No authentication is required; this is a pure view function. +pub fn capabilities(_env: &Env) -> u64 { + ALL_CAPABILITIES +} diff --git a/contracts/vault/src/lib.rs b/contracts/vault/src/lib.rs index 754a1fa1..2508f917 100644 --- a/contracts/vault/src/lib.rs +++ b/contracts/vault/src/lib.rs @@ -1604,6 +1604,23 @@ impl CalloraVault { .get(&StorageKey::ContractVersion) } + /// Return the capability bitmap for this contract version. + /// + /// Each set bit represents a feature that this contract supports. Bits are + /// stable — a position once assigned is never reused for a different feature. + /// Reserved bits (18–63) are always zero. + /// + /// No authentication required; this is a pure view function. + /// + /// # Example + /// ```ignore + /// let caps = client.capabilities(); + /// let has_batch = caps & capabilities::CAP_BATCH_DEDUCT != 0; + /// ``` + pub fn capabilities(env: Env) -> u64 { + capabilities::capabilities(&env) + } + /// Garbage-collect processed request markers from persistent storage. /// Only the owner can call this. /// Emits a `request_id_pruned` event for each removed ID. @@ -1794,6 +1811,7 @@ impl CalloraVault { } mod events; +pub mod capabilities; pub mod rate_limit; // --------------------------------------------------------------------------- @@ -1829,3 +1847,6 @@ mod test_balance_property; #[cfg(test)] mod test_rate_limit; + +#[cfg(test)] +mod test_capabilities; diff --git a/contracts/vault/src/test_capabilities.rs b/contracts/vault/src/test_capabilities.rs new file mode 100644 index 00000000..2417682b --- /dev/null +++ b/contracts/vault/src/test_capabilities.rs @@ -0,0 +1,281 @@ +extern crate std; + +use soroban_sdk::{testutils::Address as _, Address, Env}; + +use crate::{ + capabilities::{ + ALL_CAPABILITIES, CAP_ADMIN_BROADCAST, CAP_AUTHORIZED_CALLER, CAP_BATCH_DEDUCT, + CAP_DEPOSITOR_ALLOWLIST, CAP_DEDUCT, CAP_DEPOSIT, CAP_OFFERING_METADATA, CAP_PAUSE, + CAP_PRICE_REGISTRY, CAP_RATE_LIMIT, CAP_REQUEST_IDEMPOTENCY, CAP_REVENUE_POOL, + CAP_SETTLEMENT, CAP_SLIPPAGE_GUARD, CAP_TWO_STEP_ADMIN, CAP_TWO_STEP_OWNERSHIP, + CAP_UPGRADE, CAP_WITHDRAW, + }, + CalloraVault, CalloraVaultClient, +}; + +fn create_usdc(env: &Env, admin: &Address) -> Address { + let ca = env.register_stellar_asset_contract_v2(admin.clone()); + ca.address() +} + +fn setup(env: &Env) -> CalloraVaultClient<'_> { + let owner = Address::generate(env); + let vault_addr = env.register(CalloraVault, ()); + let client = CalloraVaultClient::new(env, &vault_addr); + let usdc = create_usdc(env, &owner); + env.mock_all_auths(); + client.init(&owner, &usdc, &None, &None, &None, &None, &None); + client +} + +// --------------------------------------------------------------------------- +// Basic return value +// --------------------------------------------------------------------------- + +#[test] +fn capabilities_returns_nonzero() { + let env = Env::default(); + let client = setup(&env); + assert_ne!(client.capabilities(), 0); +} + +#[test] +fn capabilities_equals_all_capabilities_constant() { + let env = Env::default(); + let client = setup(&env); + assert_eq!(client.capabilities(), ALL_CAPABILITIES); +} + +// --------------------------------------------------------------------------- +// Each individual capability bit is set +// --------------------------------------------------------------------------- + +#[test] +fn cap_deposit_is_set() { + let env = Env::default(); + let client = setup(&env); + assert_ne!(client.capabilities() & CAP_DEPOSIT, 0); +} + +#[test] +fn cap_withdraw_is_set() { + let env = Env::default(); + let client = setup(&env); + assert_ne!(client.capabilities() & CAP_WITHDRAW, 0); +} + +#[test] +fn cap_deduct_is_set() { + let env = Env::default(); + let client = setup(&env); + assert_ne!(client.capabilities() & CAP_DEDUCT, 0); +} + +#[test] +fn cap_batch_deduct_is_set() { + let env = Env::default(); + let client = setup(&env); + assert_ne!(client.capabilities() & CAP_BATCH_DEDUCT, 0); +} + +#[test] +fn cap_pause_is_set() { + let env = Env::default(); + let client = setup(&env); + assert_ne!(client.capabilities() & CAP_PAUSE, 0); +} + +#[test] +fn cap_authorized_caller_is_set() { + let env = Env::default(); + let client = setup(&env); + assert_ne!(client.capabilities() & CAP_AUTHORIZED_CALLER, 0); +} + +#[test] +fn cap_offering_metadata_is_set() { + let env = Env::default(); + let client = setup(&env); + assert_ne!(client.capabilities() & CAP_OFFERING_METADATA, 0); +} + +#[test] +fn cap_price_registry_is_set() { + let env = Env::default(); + let client = setup(&env); + assert_ne!(client.capabilities() & CAP_PRICE_REGISTRY, 0); +} + +#[test] +fn cap_request_idempotency_is_set() { + let env = Env::default(); + let client = setup(&env); + assert_ne!(client.capabilities() & CAP_REQUEST_IDEMPOTENCY, 0); +} + +#[test] +fn cap_two_step_ownership_is_set() { + let env = Env::default(); + let client = setup(&env); + assert_ne!(client.capabilities() & CAP_TWO_STEP_OWNERSHIP, 0); +} + +#[test] +fn cap_two_step_admin_is_set() { + let env = Env::default(); + let client = setup(&env); + assert_ne!(client.capabilities() & CAP_TWO_STEP_ADMIN, 0); +} + +#[test] +fn cap_settlement_is_set() { + let env = Env::default(); + let client = setup(&env); + assert_ne!(client.capabilities() & CAP_SETTLEMENT, 0); +} + +#[test] +fn cap_revenue_pool_is_set() { + let env = Env::default(); + let client = setup(&env); + assert_ne!(client.capabilities() & CAP_REVENUE_POOL, 0); +} + +#[test] +fn cap_rate_limit_is_set() { + let env = Env::default(); + let client = setup(&env); + assert_ne!(client.capabilities() & CAP_RATE_LIMIT, 0); +} + +#[test] +fn cap_admin_broadcast_is_set() { + let env = Env::default(); + let client = setup(&env); + assert_ne!(client.capabilities() & CAP_ADMIN_BROADCAST, 0); +} + +#[test] +fn cap_depositor_allowlist_is_set() { + let env = Env::default(); + let client = setup(&env); + assert_ne!(client.capabilities() & CAP_DEPOSITOR_ALLOWLIST, 0); +} + +#[test] +fn cap_slippage_guard_is_set() { + let env = Env::default(); + let client = setup(&env); + assert_ne!(client.capabilities() & CAP_SLIPPAGE_GUARD, 0); +} + +#[test] +fn cap_upgrade_is_set() { + let env = Env::default(); + let client = setup(&env); + assert_ne!(client.capabilities() & CAP_UPGRADE, 0); +} + +// --------------------------------------------------------------------------- +// Reserved bits are zero +// --------------------------------------------------------------------------- + +#[test] +fn reserved_bits_are_zero() { + let env = Env::default(); + let client = setup(&env); + // Bits 18–63 must always be zero. + let reserved_mask: u64 = !((1u64 << 18) - 1); + assert_eq!(client.capabilities() & reserved_mask, 0); +} + +// --------------------------------------------------------------------------- +// Stability: bit positions match their constant values exactly +// --------------------------------------------------------------------------- + +#[test] +fn bit_positions_are_stable() { + assert_eq!(CAP_DEPOSIT, 0x0000_0000_0000_0001); + assert_eq!(CAP_WITHDRAW, 0x0000_0000_0000_0002); + assert_eq!(CAP_DEDUCT, 0x0000_0000_0000_0004); + assert_eq!(CAP_BATCH_DEDUCT, 0x0000_0000_0000_0008); + assert_eq!(CAP_PAUSE, 0x0000_0000_0000_0010); + assert_eq!(CAP_AUTHORIZED_CALLER, 0x0000_0000_0000_0020); + assert_eq!(CAP_OFFERING_METADATA, 0x0000_0000_0000_0040); + assert_eq!(CAP_PRICE_REGISTRY, 0x0000_0000_0000_0080); + assert_eq!(CAP_REQUEST_IDEMPOTENCY, 0x0000_0000_0000_0100); + assert_eq!(CAP_TWO_STEP_OWNERSHIP, 0x0000_0000_0000_0200); + assert_eq!(CAP_TWO_STEP_ADMIN, 0x0000_0000_0000_0400); + assert_eq!(CAP_SETTLEMENT, 0x0000_0000_0000_0800); + assert_eq!(CAP_REVENUE_POOL, 0x0000_0000_0000_1000); + assert_eq!(CAP_RATE_LIMIT, 0x0000_0000_0000_2000); + assert_eq!(CAP_ADMIN_BROADCAST, 0x0000_0000_0000_4000); + assert_eq!(CAP_DEPOSITOR_ALLOWLIST, 0x0000_0000_0000_8000); + assert_eq!(CAP_SLIPPAGE_GUARD, 0x0000_0000_0001_0000); + assert_eq!(CAP_UPGRADE, 0x0000_0000_0002_0000); +} + +// --------------------------------------------------------------------------- +// Edge cases +// --------------------------------------------------------------------------- + +#[test] +fn capabilities_is_idempotent() { + let env = Env::default(); + let client = setup(&env); + assert_eq!(client.capabilities(), client.capabilities()); +} + +#[test] +fn capabilities_available_before_init() { + // capabilities() is a pure constant view — it does not require the vault to be + // initialized. + let env = Env::default(); + let vault_addr = env.register(CalloraVault, ()); + let client = CalloraVaultClient::new(&env, &vault_addr); + assert_eq!(client.capabilities(), ALL_CAPABILITIES); +} + +#[test] +fn all_capabilities_constant_has_no_gaps() { + // Every bit from 0 through 17 must be present in ALL_CAPABILITIES. + for bit in 0u64..18 { + let mask = 1u64 << bit; + assert_ne!( + ALL_CAPABILITIES & mask, + 0, + "bit {bit} is missing from ALL_CAPABILITIES" + ); + } +} + +#[test] +fn all_capabilities_bits_are_power_of_two_distinct() { + // Verify no two CAP_* constants share a bit position. + let individual: &[u64] = &[ + CAP_DEPOSIT, + CAP_WITHDRAW, + CAP_DEDUCT, + CAP_BATCH_DEDUCT, + CAP_PAUSE, + CAP_AUTHORIZED_CALLER, + CAP_OFFERING_METADATA, + CAP_PRICE_REGISTRY, + CAP_REQUEST_IDEMPOTENCY, + CAP_TWO_STEP_OWNERSHIP, + CAP_TWO_STEP_ADMIN, + CAP_SETTLEMENT, + CAP_REVENUE_POOL, + CAP_RATE_LIMIT, + CAP_ADMIN_BROADCAST, + CAP_DEPOSITOR_ALLOWLIST, + CAP_SLIPPAGE_GUARD, + CAP_UPGRADE, + ]; + let mut seen: u64 = 0; + for &cap in individual { + assert_eq!(cap & (cap - 1), 0, "CAP constant {cap:#x} is not a power of two"); + assert_eq!(seen & cap, 0, "CAP constant {cap:#x} overlaps with a previously seen bit"); + seen |= cap; + } +} diff --git a/docs/CAPABILITIES.md b/docs/CAPABILITIES.md new file mode 100644 index 00000000..5f547f30 --- /dev/null +++ b/docs/CAPABILITIES.md @@ -0,0 +1,84 @@ +# Callora Vault — Capability Bitmap + +The vault exposes a `capabilities()` view function that returns a `u64` bitmask. +Each set bit indicates a feature that the current contract version supports. +Clients can use bit operations to detect available capabilities without complex +version-string parsing. + +## Querying capabilities + +```typescript +// Using @stellar/stellar-sdk +const caps = await client.capabilities(); + +if (caps & CAP_BATCH_DEDUCT) { + // safe to call batch_deduct +} +``` + +```rust +// Inside another Soroban contract +let caps: u64 = VaultClient::new(&env, &vault).capabilities(); +let has_rate_limit = caps & CAP_RATE_LIMIT != 0; +``` + +## Bit registry + +| Bit | Hex value | Constant | Feature | Introduced | +|-----|-----------|----------|---------|-----------| +| 0 | `0x00001` | `CAP_DEPOSIT` | `deposit()` — accept USDC from allowlisted callers | v1.0.0 | +| 1 | `0x00002` | `CAP_WITHDRAW` | `withdraw()` / `withdraw_to()` — owner-initiated withdrawal | v1.0.0 | +| 2 | `0x00004` | `CAP_DEDUCT` | `deduct()` — authorized caller deducts to settlement | v1.0.0 | +| 3 | `0x00008` | `CAP_BATCH_DEDUCT` | `batch_deduct()` — atomic multi-item deduct | v1.0.0 | +| 4 | `0x00010` | `CAP_PAUSE` | `pause()` / `unpause()` — circuit-breaker (admin or owner) | v1.0.0 | +| 5 | `0x00020` | `CAP_AUTHORIZED_CALLER` | `set_authorized_caller()` — delegate deduct permission | v1.0.0 | +| 6 | `0x00040` | `CAP_OFFERING_METADATA` | `set_metadata()` / `update_metadata()` / `remove_metadata()` | v1.0.0 | +| 7 | `0x00080` | `CAP_PRICE_REGISTRY` | `set_price()` / `get_price()` / `list_prices()` / `remove_price()` | v1.0.0 | +| 8 | `0x00100` | `CAP_REQUEST_IDEMPOTENCY` | Optional `request_id` on `deduct` / `batch_deduct` for at-least-once retry | v1.0.0 | +| 9 | `0x00200` | `CAP_TWO_STEP_OWNERSHIP` | `transfer_ownership()` / `accept_ownership()` | v1.0.0 | +| 10 | `0x00400` | `CAP_TWO_STEP_ADMIN` | `set_admin()` / `accept_admin()` / `cancel_admin_transfer()` | v1.0.0 | +| 11 | `0x00800` | `CAP_SETTLEMENT` | Settlement contract integration via `set_settlement()` | v1.0.0 | +| 12 | `0x01000` | `CAP_REVENUE_POOL` | `propose_revenue_pool()` / `accept_revenue_pool()` / `cancel_revenue_pool()` | v1.0.0 | +| 13 | `0x02000` | `CAP_RATE_LIMIT` | Per-developer token-bucket rate limits via `set_developer_rate_limit()` | v1.0.0 | +| 14 | `0x04000` | `CAP_ADMIN_BROADCAST` | `broadcast()` — admin-signed on-chain messages with severity | v1.0.0 | +| 15 | `0x08000` | `CAP_DEPOSITOR_ALLOWLIST` | `add_address()` / `set_allowed_depositor()` / `clear_all()` | v1.0.0 | +| 16 | `0x10000` | `CAP_SLIPPAGE_GUARD` | `max_fee_bps` parameter on `deduct()` | v1.0.0 | +| 17 | `0x20000` | `CAP_UPGRADE` | `upgrade()` — admin-gated WASM replacement | v1.0.0 | +| 18–63 | — | *(reserved)* | Always zero; reserved for future capabilities | — | + +## Stability guarantee + +- A bit position is assigned once and never reused for a different feature. +- Removed features keep their bit **cleared** in future versions; the position stays reserved. +- New features always occupy the lowest available bit index. +- Reserved bits (18–63) are always `0` in the current version. + +## Integration checklist + +1. Call `capabilities()` once at startup or after detecting a contract upgrade. +2. Cache the result — it is immutable for a given WASM deployment. +3. Use `caps & CAP_ != 0` to gate feature-specific code paths. +4. Treat an unknown bit as a forward-compatible signal: ignore it, do not error. + +## Constant values (for off-chain SDKs) + +```typescript +export const CAP_DEPOSIT = 0x00001n; +export const CAP_WITHDRAW = 0x00002n; +export const CAP_DEDUCT = 0x00004n; +export const CAP_BATCH_DEDUCT = 0x00008n; +export const CAP_PAUSE = 0x00010n; +export const CAP_AUTHORIZED_CALLER = 0x00020n; +export const CAP_OFFERING_METADATA = 0x00040n; +export const CAP_PRICE_REGISTRY = 0x00080n; +export const CAP_REQUEST_IDEMPOTENCY= 0x00100n; +export const CAP_TWO_STEP_OWNERSHIP = 0x00200n; +export const CAP_TWO_STEP_ADMIN = 0x00400n; +export const CAP_SETTLEMENT = 0x00800n; +export const CAP_REVENUE_POOL = 0x01000n; +export const CAP_RATE_LIMIT = 0x02000n; +export const CAP_ADMIN_BROADCAST = 0x04000n; +export const CAP_DEPOSITOR_ALLOWLIST= 0x08000n; +export const CAP_SLIPPAGE_GUARD = 0x10000n; +export const CAP_UPGRADE = 0x20000n; +```