# DALPAsset Source: https://docs.settlemint.com/docs/architecture/components/asset-contracts/dalp-asset Recommended configurable asset contract. Attach token-features and compliance modules per instrument; evolve configuration post-deploy under governance controls. ## At a glance [#at-a-glance] * Configurable post-deployment via `SMARTConfigurable` - attach/remove token features and compliance modules without redeploying * All configuration changes are governance-gated (require `GOVERNANCE_ROLE`) * Deployable as upgradeable (UUPS proxy) or immutable - see [Deployment Architecture](/docs/architecture/components/asset-contracts/deployment-architecture) * Feature attachment is composable - not limited to predefined presets * External systems (wallets, indexers, dashboards) interact via standard ERC-20 and ERC-3643 interfaces *** ## What DALPAsset is [#what-dalpasset-is] DALPAsset extends SMART Protocol (ERC-3643) with the `SMARTConfigurable` extension, which allows any combination of [token features](/docs/architecture/components/token-features) to be attached and reconfigured at runtime - after the token is deployed. This eliminates the need to commit to a specialized contract type at deployment time. A DALPAsset token can evolve: start as a simple bearer instrument, then have fee features added, governance enabled, or maturity redemption configured - all without redeploying. *** ## How configuration works [#how-configuration-works] ### Token features [#token-features] * Runtime-pluggable via the `ISMARTFeature` interface - see the [Token Features](/docs/architecture/components/token-features) catalog for all available features * Integrate through six lifecycle hooks: mint, burn, transfer, redeem, update, and attach * Feature ordering is the caller's responsibility - see the [Token Features ordering guide](/docs/architecture/components/token-features) * Some features rewrite transfer amounts (e.g., fees); ordering matters for downstream analytics ### Compliance modules [#compliance-modules] * Transfer and supply rules enforced by the ERC-3643 compliance engine - see [Compliance Modules](/docs/architecture/security/compliance) * Modules can be added, removed, or reconfigured at runtime * Common patterns: capped supply, collateral enforcement, jurisdiction-based investor restrictions ### Configuration is composable [#configuration-is-composable] DALPAsset is not limited to the seven [Legacy-Equivalent Presets](/docs/architecture/components/asset-contracts/instrument-profiles). Any combination of token features and compliance modules is valid. The presets exist as proven starting points for teams migrating from legacy types - they are not exhaustive. *** ## What changes mean [#what-changes-mean] * **Adding/removing features** - governance-gated, no redeployment required. Understand what the feature's hooks do before attaching. * **Changing compliance rules** - transfer rules change immediately for all subsequent transactions. Existing balances are not retroactively affected. * **Parameter immutability** - some parameters (e.g., face value, maturity date) are immutable by design. Others (fee rates, schedules) are governance-reconfigurable. Check per-feature documentation. * **Governance expectations** - all changes require `GOVERNANCE_ROLE`. Multi-sig or timelock recommended for production. *** ## Relationship to legacy [#relationship-to-legacy] * Seven specialized types (DALPBond, DALPEquity, DALPFund, DALPStableCoin, DALPDeposit, DALPRealEstate, DALPPreciousMetal) predate `SMARTConfigurable` - see [Legacy Types](/docs/architecture/components/asset-contracts/legacy-types) * Legacy types are fully supported, not deprecated - no migration required * [Legacy-Equivalent Presets](/docs/architecture/components/asset-contracts/instrument-profiles) recreate legacy behavior on DALPAsset * Choose legacy when compile-time immutability is required by regulation or legal frameworks * Choose DALPAsset for all new projects and when requirements may evolve *** ## See also [#see-also] * [Token Features](/docs/architecture/components/token-features) - runtime-pluggable feature catalog * [Compliance Modules](/docs/architecture/security/compliance) - transfer and supply rule engine * [Legacy-Equivalent Presets](/docs/architecture/components/asset-contracts/instrument-profiles) - pre-built configurations per instrument type * [Legacy Types](/docs/architecture/components/asset-contracts/legacy-types) - compile-time types, decision checklists, coexistence guidance * [RBAC](/docs/architecture/components/asset-contracts/rbac) - per-asset role model and separation-of-duties invariants * [Deployment Architecture](/docs/architecture/components/asset-contracts/deployment-architecture) - factory pattern, upgradeability, and invariants # Deployment Architecture Source: https://docs.settlemint.com/docs/architecture/components/asset-contracts/deployment-architecture Factory deployment pattern for all asset types. Covers CREATE2 deterministic addressing, initialization invariants, deployment failure modes, and custodian administrative controls. ## Factory deployment pattern [#factory-deployment-pattern] All asset types follow the same factory deployment pattern: 1. Factory receives `createAsset(params)` and deploys a proxy via CREATE2 (deterministic address) 2. Factory registers an OnchainID identity contract for the token 3. Proxy is initialized with the identity and delegates to the implementation contract 4. Required system roles are assigned; `TokenDeployed` event emitted for indexing The factory transaction is atomic. If any step fails, the entire deployment reverts - no partially-deployed tokens can exist on-chain. *** ## Key invariants [#key-invariants] These invariants must hold for every deployment. Violating any of them reverts the factory transaction. * **CREATE2 determinism** - token addresses are predictable from deployment parameters; the same parameters always produce the same address * **Initialization order** - identity must be set before compliance, compliance before transfers are enabled * **Identity before compliance** - the OnchainID identity contract must be registered before compliance modules can verify identity claims * **Compliance before transfers** - the compliance engine must be configured before any transfer operations are permitted *** ## Deployment-specific failure modes [#deployment-specific-failure-modes] These failures are scoped to the initialization sequence. Runtime operational failures are covered in [Failure Modes](/docs/architecture/operability/failure-modes). | Failure | System behavior | | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | **CREATE2 address collision** | Deployment reverts; same-parameter deployment to the same factory produces an identical address. Redeployment with different salt or parameters required. | | **Initialization failure mid-sequence** | Proxy is deployed but not fully initialized. Factory transaction is atomic - partial initialization reverts the entire deployment. | | **OnchainID registration failure** | Identity registry rejects the registration (e.g., identity already exists for this address). Deployment reverts. | | **Incomplete role assignment** | Factory assigns all required roles atomically. If any role assignment fails, the entire deployment reverts. No partially-permissioned tokens can exist. | *** Asset review and deployment confirmation before on-chain activation ## Administrative controls [#administrative-controls] The Custodian extension provides administrative controls across all asset types. These controls exist to handle regulatory, legal, and recovery scenarios that cannot be enforced through normal transfer rules. * **Forced transfers** - court orders, inheritance, regulatory seizures. Custodian can transfer tokens between any addresses regardless of compliance module checks. * **Account freezing** - full or partial, pending investigation. Frozen tokens cannot be transferred by the holder but can still be force-transferred by the custodian. * **Token recovery** - two-step identity recovery. When a holder's identity is recovered (key compromise, lost access), tokens are transferred to the new address associated with the recovered identity. * **Batch operations** - bulk compliance actions for operational efficiency (e.g., freeze multiple addresses, batch forced transfers). All custodian actions emit events for auditability. See [RBAC](/docs/architecture/components/asset-contracts/rbac) for role separation between custodian and other administrative functions. *** ## Step-by-step deployment [#step-by-step-deployment] [Asset Issuance Flow](/docs/architecture/flows/asset-issuance) covers the deployment sequence, including pre-conditions, state transitions, indexer hooks, and UI flow. *** ## Change impact [#change-impact] * **New asset type:** Adding a new preset does not change the factory pattern. The factory deploys any DALPAsset configuration through the same CREATE2 proxy flow. * **Upgradeability changes:** Switching between upgradeable (UUPS) and immutable deployment affects proxy behavior but not the factory deployment sequence. * **Compliance module changes:** Modifying compliance rules post-deployment does not require redeployment. The compliance engine is reconfigurable at runtime. *** ## See also [#see-also] * [Asset Contracts](/docs/architecture/components/asset-contracts) - contract types, legacy-equivalent presets, common foundation * [Asset Issuance Flow](/docs/architecture/flows/asset-issuance) - step-by-step deployment sequence * [RBAC](/docs/architecture/components/asset-contracts/rbac) - per-asset role model and separation-of-duties invariants * [Failure Modes](/docs/architecture/operability/failure-modes) - runtime operational failure modes # Asset Contracts Source: https://docs.settlemint.com/docs/architecture/components/asset-contracts DALPAsset is the recommended contract type for all new tokenization projects. Configure token features and compliance modules per instrument. Specialized types remain supported for existing deployments. > **In development:** DALPAsset and token features are behind a feature flag and under active development. Interfaces and behavior may change before general availability. ## DALPAsset: recommended for all new projects [#dalpasset-recommended-for-all-new-projects] **DALPAsset** is the recommended contract type for all new tokenization projects. It extends SMART Protocol (ERC-3643) with runtime-configurable token features and compliance modules - no redeployment needed to evolve. See [DALPAsset](/docs/architecture/components/asset-contracts/dalp-asset) for the full architecture: how configuration works, change impact, and relationship to legacy types. *** ## Legacy-equivalent presets at a glance [#legacy-equivalent-presets-at-a-glance] | Preset | Token features to attach | Compliance modules | | ----------------- | -------------------------------------------------------------- | ------------------ | | **Bond** | Fixed Treasury Yield, Maturity Redemption, Historical Balances | Capped | | **Equity** | Voting Power, Historical Balances | Per jurisdiction | | **Fund** | AUM Fee, Voting Power, Historical Balances | Per jurisdiction | | **StableCoin** | Historical Balances | Collateral | | **Deposit** | Historical Balances | Per jurisdiction | | **RealEstate** | Historical Balances | Capped | | **PreciousMetal** | Historical Balances | Per jurisdiction | See [Legacy-Equivalent Presets](/docs/architecture/components/asset-contracts/instrument-profiles) for the full configuration spec - token features, compliance modules, parameters, and legacy equivalents per instrument. *** Asset contract details showing on-chain parameters ## When to choose [#when-to-choose] * **DALPAsset** - runtime flexibility: attach and reconfigure token features post-deployment. Recommended for all new projects. See [DALPAsset](/docs/architecture/components/asset-contracts/dalp-asset) for details and [Legacy-Equivalent Presets](/docs/architecture/components/asset-contracts/instrument-profiles) for configuration guidance. * **Legacy specialized type** - compile-time immutability: features embedded at deployment cannot change. Choose when compliance or legal frameworks require post-issuance immutability guarantees. See [Legacy Types](/docs/architecture/components/asset-contracts/legacy-types) for the full decision checklist. *** ## Common foundation [#common-foundation] Every asset type - DALPAsset and specialized - shares the same SMART Protocol foundation: * **ERC-3643** compliance - on-chain transfer enforcement via compliance modules * **ERC-20** compatibility - standard wallet, DEX, and tooling support * **OnchainID** integration - identity registry for KYC/AML claims * **ERC-2771** meta-transactions - gasless operations via trusted forwarders * **ERC-165** introspection - external systems query supported capabilities * **Unified RBAC** - seven per-asset roles with separation of duties (see [RBAC](/docs/architecture/components/asset-contracts/rbac)) *** ## Further reading [#further-reading] * [DALPAsset](/docs/architecture/components/asset-contracts/dalp-asset) - recommended configurable contract type * [Legacy-Equivalent Presets](/docs/architecture/components/asset-contracts/instrument-profiles) - pre-built configurations per instrument type * [Legacy Types](/docs/architecture/components/asset-contracts/legacy-types) - compile-time types, decision checklists, coexistence guidance * [RBAC](/docs/architecture/components/asset-contracts/rbac) - per-asset role model and separation-of-duties invariants * [Deployment Architecture](/docs/architecture/components/asset-contracts/deployment-architecture) - factory pattern, invariants, failure modes * [Token Features](/docs/architecture/components/token-features) - runtime-pluggable feature catalog * [Compliance Modules](/docs/architecture/security/compliance) - transfer and supply rule engine # Legacy-Equivalent Presets Source: https://docs.settlemint.com/docs/architecture/components/asset-contracts/instrument-profiles Pre-built DALPAsset configurations that mirror legacy type behavior - starting points, not limits. Covers Bond, Equity, Fund, StableCoin, Deposit, RealEstate, and PreciousMetal. ## Beyond the presets [#beyond-the-presets] DALPAsset supports any combination of token features and compliance modules. The seven profiles below are common configurations, not a fixed limit. * These presets recreate [legacy specialized contract](/docs/architecture/components/asset-contracts/legacy-types) behavior on DALPAsset - they are **starting points**, not limits * Add features beyond the baseline: fees, governance, redemption, yield, permits, conversion * Add compliance modules beyond the baseline: jurisdiction templates, investor count caps, collateral enforcement * Regulation or legal constraints may require compile-time immutability - see [Legacy Types](/docs/architecture/components/asset-contracts/legacy-types) * Choose features and modules based on requirements; presets are not exhaustive *** Custom instrument metadata schema for asset-specific fields ## Presets [#presets] Each preset mirrors a legacy type's embedded behavior using DALPAsset's configurable features and modules. ### Bond [#bond] * **Use case:** Fixed-income instruments with face value, coupon payments, and maturity date. * **Token features:** * [Fixed Treasury Yield](/docs/architecture/components/token-features/fixed-treasury-yield) - periodic yield schedule paid in denomination asset; pull-based (holders claim) * [Maturity Redemption](/docs/architecture/components/token-features/maturity-redemption) - maturity date, face value redemption in denomination asset * [Historical Balances](/docs/architecture/components/token-features/historical-balances) - balance snapshots for coupon accrual calculations * **Compliance modules:** * [CappedComplianceModule](/docs/architecture/security/compliance/supply-cap-collateral#cappedcompliancemodule) - enforces maximum issuance cap * Additional modules per jurisdiction (see [Regulatory templates](/docs/architecture/security/compliance)) * **Key parameters at deployment:** face value, maturity date, denomination asset (e.g. USDC), coupon rate/schedule, supply cap * **Legacy equivalent:** `DALPBond` - embeds `SMARTRedeemableUpgradeable`, `SMARTYieldUpgradeable` (legacy yield mechanism, deprecated in favor of Fixed Treasury Yield), `SMARTCappedUpgradeable`, `SMARTHistoricalBalancesUpgradeable` at compile time. Blocks transfers after maturity via `isMatured` state. *** ### Equity [#equity] * **Use case:** Shares with governance voting rights and balance snapshots for shareholder records. * **Token features:** * [Voting Power](/docs/architecture/components/token-features/voting-power) - ERC20Votes delegation, voting weight equals token balance at snapshot * [Historical Balances](/docs/architecture/components/token-features/historical-balances) - balance checkpoints for snapshot-based governance * **Compliance modules:** * Per jurisdiction - see [Regulatory templates](/docs/architecture/security/compliance). Common: Identity Verification + Country Allow List + Investor Count. * **Key parameters at deployment:** none specific beyond standard token parameters * **Legacy equivalent:** `DALPEquity` - embeds `ERC20VotesUpgradeable` and `SMARTHistoricalBalances` at compile time. *** ### Fund [#fund] * **Use case:** Managed investment vehicles with AUM-based management fees and governance. * **Token features:** * [AUM Fee](/docs/architecture/components/token-features/aum-fee) - time-weighted management fee calculated as AUM x feeBps x elapsed / year, minted to configured `feeRecipient` * [Voting Power](/docs/architecture/components/token-features/voting-power) - investor governance * [Historical Balances](/docs/architecture/components/token-features/historical-balances) - NAV and fee calculation snapshots * **Compliance modules:** * Per jurisdiction - see [Regulatory templates](/docs/architecture/security/compliance). Common: Identity Verification + Country Allow List. * **Key parameters at deployment:** fee rate in basis points * **Legacy equivalent:** `DALPFund` - embeds management fee logic, `ERC20VotesUpgradeable`, `SMARTHistoricalBalancesUpgradeable` at compile time. See [Parity Notes](/docs/architecture/components/asset-contracts/legacy-types#parity-notes) for behavioral differences. *** ### StableCoin [#stablecoin] * **Use case:** Fiat-pegged or asset-backed stable value tokens with on-chain collateral enforcement. * **Token features:** * [Historical Balances](/docs/architecture/components/token-features/historical-balances) - balance snapshots for reporting and audit * **Compliance modules:** * [CollateralComplianceModule](/docs/architecture/security/compliance/supply-cap-collateral#collateralcompliancemodule) - enforces that minting cannot exceed the on-chain collateral ratio (configured in basis points: 10,000 = 100%). Collateral proof verified via ERC-735 identity claims with expiry - claims must be renewed proactively or minting halts. * Additional modules per jurisdiction * **Key parameters at deployment:** collateral proof topic (ERC-735 claim topic), collateral ratio (basis points), trusted issuers * **Legacy equivalent:** `DALPStableCoin` - embeds `SMARTCollateralUpgradeable` (a separate compile-time collateral mechanism, distinct from the plug-in CollateralComplianceModule) and `SMARTHistoricalBalancesUpgradeable` at compile time. The collateral ratio is a required deployment parameter; 10,000 bps (100%) is the conventional starting point. *** ### Deposit [#deposit] * **Use case:** General-purpose deposit tokens (e.g. tokenized bank deposits) with minimal on-chain constraints. * **Token features:** * [Historical Balances](/docs/architecture/components/token-features/historical-balances) - balance snapshots. Additional features as needed. * **Compliance modules:** * Per jurisdiction - see [Regulatory templates](/docs/architecture/security/compliance). For collateral-backed deposits, add [CollateralComplianceModule](/docs/architecture/security/compliance/supply-cap-collateral#collateralcompliancemodule). * **Key parameters at deployment:** none specific beyond standard token parameters * **Legacy equivalent:** `DALPDeposit` - embeds only `SMARTHistoricalBalances` at compile time. Minimal specialized logic. *** ### RealEstate [#realestate] * **Use case:** Fractional ownership of real estate with fixed supply representing the property valuation. * **Token features:** * [Historical Balances](/docs/architecture/components/token-features/historical-balances) - ownership snapshots for dividend/rent distribution * **Compliance modules:** * [CappedComplianceModule](/docs/architecture/security/compliance/supply-cap-collateral#cappedcompliancemodule) - enforces fixed supply cap representing property valuation * Additional modules per jurisdiction * **Key parameters at deployment:** supply cap (matching property valuation/fractionalization), property metadata * **Legacy equivalent:** `DALPRealEstate` - embeds `SMARTCapped` and `SMARTHistoricalBalances` at compile time. Has a premint mechanism: factory mints the full supply to a recipient then pauses, locking the cap to the preminted amount. *** ### PreciousMetal [#preciousmetal] * **Use case:** Tokenized precious metals (gold, silver) with pooled custody model - supply grows as vault deposits increase. * **Token features:** * [Historical Balances](/docs/architecture/components/token-features/historical-balances) - balance snapshots for custody reporting * **Compliance modules:** * Per jurisdiction - see [Regulatory templates](/docs/architecture/security/compliance). No cap constraint (supply is dynamic based on vault backing). * **Key parameters at deployment:** none specific beyond standard token parameters. No supply cap. * **Legacy equivalent:** `DALPPreciousMetal` - embeds only `SMARTHistoricalBalances` at compile time. PAXG-style pooled model with no supply cap. *** ## See also [#see-also] * [DALPAsset](/docs/architecture/components/asset-contracts/dalp-asset) - recommended configurable contract type * [Asset Contracts](/docs/architecture/components/asset-contracts) - decision framework and common foundation * [Token Features](/docs/architecture/components/token-features) - full runtime-pluggable feature catalog * [Compliance Modules](/docs/architecture/security/compliance) - transfer and supply rule engine * [Legacy Types](/docs/architecture/components/asset-contracts/legacy-types) - compile-time types, decision checklists, coexistence guidance * [RBAC](/docs/architecture/components/asset-contracts/rbac) - per-asset role model and separation-of-duties invariants # Legacy Types Source: https://docs.settlemint.com/docs/architecture/components/asset-contracts/legacy-types Specialized contract types (DALPBond, DALPEquity, DALPFund, DALPStableCoin, DALPDeposit, DALPRealEstate, DALPPreciousMetal) are fully supported with compile-time immutability. DALPAsset is recommended for all new projects. ## Status and support posture [#status-and-support-posture] All seven specialized contract types are **fully supported, not deprecated, and require no migration**. There is no planned deprecation timeline. They continue to receive security patches and compatibility updates alongside DALPAsset. **DALPAsset is recommended for all new projects.** Specialized types exist because they predate the `SMARTConfigurable` extension. Existing deployments that use them and should not be replaced unless new capability requirements demand runtime reconfiguration. *** ## Legacy type catalog [#legacy-type-catalog] | Type | Primary use case | Compiled-in capabilities | Immutability guarantees | Equivalent DALPAsset preset | | --------------------- | ------------------------ | -------------------------------------------------------------------------- | ------------------------------------------------ | --------------------------- | | **DALPBond** | Fixed-income instruments | SMARTRedeemable, SMARTYield (legacy), SMARTCapped, SMARTHistoricalBalances | Fee structure and maturity terms fixed at deploy | Bond | | **DALPEquity** | Shares with voting | ERC20Votes, SMARTHistoricalBalances | Voting mechanism fixed at deploy | Equity | | **DALPFund** | Investment vehicles | Management fee, ERC20Votes, SMARTHistoricalBalances | Fee rate fixed at deploy | Fund | | **DALPStableCoin** | Fiat-pegged tokens | SMARTCollateral, SMARTHistoricalBalances | Collateral mechanism fixed at deploy | StableCoin | | **DALPDeposit** | General deposits | SMARTHistoricalBalances | Minimal constraints | Deposit | | **DALPRealEstate** | Fractional property | SMARTCapped, SMARTHistoricalBalances, premint mechanism | Supply cap fixed at deploy | RealEstate | | **DALPPreciousMetal** | Tokenized metals | SMARTHistoricalBalances | No supply cap, PAXG-style model | PreciousMetal | All types share the same SMART Protocol (ERC-3643) foundation, RBAC model, and factory deployment pattern as DALPAsset. *** ## Compile-time vs runtime-configurable [#compile-time-vs-runtime-configurable] The core architectural difference is **when capabilities are bound to the token**: | | Legacy (compile-time) | DALPAsset (runtime via SMARTConfigurable) | | ----------------- | ----------------------------------------------------------- | -------------------------------------------------------------------------- | | **Binding** | Features embedded in bytecode at compilation; cannot change | Features attached post-deployment; can be added, upgraded, or reconfigured | | **Governance** | No configuration drift - contract is the complete spec | Configuration changes require role-gated transactions, multi-sig | | **Audit surface** | Simpler - no reconfiguration paths or configuration state | Broader - must review feature attachment, composition, and permissions | *** ## Choose legacy when [#choose-legacy-when] Use this checklist to determine whether a specialized type is the right choice: * Compliance or legal framework requires that features (fee structure, maturity terms, collateral ratio) **cannot change post-issuance** * A stronger immutability guarantee is needed for **investor trust** - the on-chain contract is the complete, immutable specification * A simpler audit surface is preferred - **no runtime reconfiguration paths** to review * Existing deployments that work correctly and **do not need new features** *** ## Choose DALPAsset when [#choose-dalpasset-when] * Requirements may **evolve post-deployment** (add fees, governance, yield after launch) * Post-deploy feature attachment is needed for phased rollouts * A modular approach is preferred for **multi-instrument deployments** using a single contract type * **All new projects** (default recommendation) *** ## Parity notes [#parity-notes] Where a legacy compiled-in feature differs from the modern token feature equivalent: | Legacy feature | Legacy type | Modern equivalent | Difference | | ---------------------------- | ------------------------ | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | `SMARTYieldUpgradeable` | DALPBond | [Fixed Treasury Yield](/docs/architecture/components/token-features/fixed-treasury-yield) | Legacy uses a different yield distribution mechanism. The token feature uses pull-based claiming from a treasury with scheduled distributions. | | `SMARTCollateralUpgradeable` | DALPStableCoin | CollateralComplianceModule | Legacy embeds collateral logic in the token contract. The module approach uses the compliance engine with ERC-735 identity claims for collateral proof. | | `SMARTCappedUpgradeable` | DALPBond, DALPRealEstate | CappedComplianceModule | Same cap enforcement logic, different attachment mechanism (compiled-in vs compliance module). | | `collectManagementFee()` | DALPFund | [AUM Fee](/docs/architecture/components/token-features/aum-fee) | Legacy mints fee tokens to the caller (governance role). The token feature mints to a configured `feeRecipient` address. | These differences are behavioral. The compiled-in implementations and the modern equivalents achieve the same business outcome through different mechanisms. When evaluating a migration, review whether the behavioral difference affects downstream integrations (e.g., fee accounting systems that expect minting to a specific address). *** ## Coexistence guidance [#coexistence-guidance] Mixed-fleet deployments are fully supported. DALPAsset and legacy types can coexist in the same deployment because they share the same foundation: * **Same SMART Protocol (ERC-3643)** - identical compliance enforcement, transfer rules, and identity registry integration * **Same RBAC model** - identical seven-role structure with the same role identifiers (see [RBAC](/docs/architecture/components/asset-contracts/rbac)) * **Same factory pattern** - deployed through the same factory infrastructure with the same deployment invariants * **Same upgradeability model** - both support UUPS proxy or immutable deployment (see [Deployment Architecture](/docs/architecture/components/asset-contracts/deployment-architecture)) No migration is required. An organization can operate DALPBond tokens alongside DALPAsset tokens configured with the Bond preset. External systems (wallets, indexers, compliance dashboards) interact with both through the same ERC-20 and ERC-3643 interfaces. *** ## See also [#see-also] * [DALPAsset](/docs/architecture/components/asset-contracts/dalp-asset) - recommended configurable contract type * [Asset Contracts](/docs/architecture/components/asset-contracts) - return to the asset contracts hub * [Legacy-Equivalent Presets](/docs/architecture/components/asset-contracts/instrument-profiles) - per-instrument DALPAsset configuration spec * [Token Features](/docs/architecture/components/token-features) - runtime-pluggable feature catalog * [RBAC](/docs/architecture/components/asset-contracts/rbac) - per-asset role model shared by all contract types # Per-Asset RBAC Source: https://docs.settlemint.com/docs/architecture/components/asset-contracts/rbac Seven per-asset roles defined in DALPAssetRoles.sol enforce separation of duties across every token contract. Each role is scoped to a single asset and grants a narrow set of operational powers. ## Per-asset role model [#per-asset-role-model] Every token contract - DALPAsset and specialized types - uses the same seven roles defined in `DALPAssetRoles.sol`. Roles are scoped per asset: holding `GOVERNANCE_ROLE` on Token A grants no power over Token B. | Role | Scope | Key actions | | --------------------- | ---------------------------- | ------------------------------------------------------------------------------------- | | **Default Admin** | Role management | Grant and revoke all other per-asset roles; no operational powers | | **Governance** | Configuration and compliance | Set identity contracts, compliance modules, token features (DALPAsset only), metadata | | **Supply Management** | Minting and burning | Mint, burn, batch operations, set supply cap | | **Custodian** | Asset protection | Freeze addresses or partial amounts, forced transfers, wallet recovery | | **Emergency** | Incident response | Pause and unpause operations, recover stuck ERC-20 tokens | | **Sale Admin** | Token sale (addon only) | Manage token sale configuration and lifecycle | | **Funds Manager** | Sale funds (addon only) | Withdraw funds from token sales | Sale Admin and Funds Manager are available only on assets with the token sale addon attached. They have no effect on tokens without a sale. *** Role-based access control configuration for asset operations ## Separation-of-duties invariants [#separation-of-duties-invariants] The role model enforces the following invariants. No single role can act unilaterally across operational boundaries: * **Admin grants roles but has no operational powers** - cannot mint, burn, freeze, pause, or configure. This prevents the role-granting authority from also executing privileged operations. * **Supply Management cannot freeze; Custodian cannot mint** - operational segregation ensures that the entity issuing tokens is not the same entity that can freeze or recover them. * **Emergency is limited to pause and recovery** - cannot mint, configure compliance, or execute forced transfers. The incident-response role can halt the system but cannot alter its state. * **Governance configures policy; Supply Management executes issuance** - the entity that sets compliance rules and identity contracts does not control token supply. * **Sale Admin configures sales; Funds Manager withdraws proceeds** - separating sale lifecycle management from fund withdrawal prevents a single operator from both launching a sale and extracting its funds. These invariants hold for all asset types. The contract enforces them via OpenZeppelin `AccessControl` modifiers - violations revert on-chain. *** ## Token holder permissions [#token-holder-permissions] Any address holding a token balance can perform the following actions, subject to compliance module checks: * **Transfer tokens** - standard ERC-20 transfers, validated by attached compliance modules before execution * **Redeem at maturity** - if [Maturity Redemption](/docs/architecture/components/token-features/maturity-redemption) is attached, holders can redeem tokens after the maturity date * **Vote and delegate** - if [Voting Power](/docs/architecture/components/token-features/voting-power) is attached, holders can delegate voting weight and participate in governance * **Claim yield** - if [Fixed Treasury Yield](/docs/architecture/components/token-features/fixed-treasury-yield) is attached, holders can claim accrued yield distributions Token holder actions require no on-chain role assignment. The holder's address must pass the identity and compliance checks configured by the Governance role. *** ## Full authorization taxonomy [#full-authorization-taxonomy] The seven per-asset roles are **Layer 3** of the four-layer DALP authorization model: | Layer | Scope | Where defined | | ----------------- | --------------------------------- | ------------------------ | | 1. Platform | Off-chain API and console access | Better Auth | | 2. System People | On-chain system-wide operations | `DALPPeopleRoles.sol` | | **3. Per-Asset** | **On-chain per-token operations** | **`DALPAssetRoles.sol`** | | 4. System Modules | On-chain contract-to-contract | `DALPSystemRoles.sol` | For the complete taxonomy - including all 26 roles across all four layers, the dual-layer permission model, and multi-tenant architecture - see [Authorization](/docs/architecture/security/authorization). *** ## See also [#see-also] * [Asset Contracts](/docs/architecture/components/asset-contracts) - contract types and common foundation * [Authorization](/docs/architecture/security/authorization) - full 4-layer role taxonomy with 26 roles * [Token Features](/docs/architecture/components/token-features) - runtime-pluggable feature catalog * [Compliance Modules](/docs/architecture/security/compliance) - transfer and supply rule engine # SMART Protocol integration (ERC-3643) Source: https://docs.settlemint.com/docs/architecture/components/asset-contracts/smart-protocol-integration How DALP integrates the SMART Protocol (ERC-3643) as its on-chain compliance foundation - responsibility boundaries, multi-asset model, and where enforcement details are documented. ## What is SMART Protocol? [#what-is-smart-protocol] **SMART** (SettleMint Adaptable Regulated Token) Protocol is an ERC-3643 implementation that DALP uses as its on-chain compliance foundation. ERC-3643 defines a specification for security tokens where transfers are only permitted when a compliance engine - consisting of one or more modular rules - approves them. SMART provides three layers that DALP builds on: | Layer | What it provides | | -------------- | -------------------------------------------------------------------------------- | | **Token** | ERC-20 compatible contracts with compliance hooks and modular extensions | | **Compliance** | Orchestration engine that evaluates configurable rule sets before each transfer | | **Identity** | On-chain identity management via OnchainID (ERC-734/735), storing KYC/AML claims | DALP extends these layers with access control, proxy architecture, infrastructure integration, runtime token features, and system-seeded compliance templates. *** ## DALP implementation model [#dalp-implementation-model] DALP uses ERC-3643 as the enforcement model for regulated asset movement: the token contract owns balances and transfer execution, while the identity registry, compliance engine, and token-feature hooks all participate in deciding whether standard mints and transfers may proceed before token state changes. Any of those gates can revert with its own error instead of creating a partial lifecycle state. The model has five operator-visible parts: 1. **Token contract** - one asset contract represents one instrument or asset class, with its own supply, holders, roles, and lifecycle settings. 2. **Identity registry** - the asset resolves recipient wallet addresses to OnchainID identities before regulated transfer-path operations. 3. **Trusted issuers and claim topics** - approved claim issuers define which identity attestations count for the asset's compliance policy. 4. **Compliance engine** - the asset calls the compliance engine during the transfer path, and the engine evaluates the configured rule set. 5. **Compliance modules and feature hooks** - reusable rule contracts, such as identity, country, supply, investor-count, time-lock, and approval rules, are configured per asset; runtime token features may add their own validation hooks. Standard transfers and mints follow this path: recipient-side identity resolution, including OnchainID lookup for the `to` address; compliance engine rule evaluation; and feature `canUpdate` hooks, such as `_smart_callFeatureCanUpdateHooks`. The default identity verification path is recipient-side only. Sender-side constraints on the `from` address apply only when an additional compliance module is installed and explicitly enforces them. Burns and redemptions use lifecycle-specific hooks: burns destroy token balances and notify compliance after the supply change, while bond redemption first enforces maturity, non-zero amount, and available denomination-asset funding before burning the redeemed balance and paying the holder. DALP treats forced transfers as separate custodian operations. Forced transfers are explicit administrative actions, not the normal investor transfer path. *** ## Responsibility boundaries [#responsibility-boundaries] | Concern | DALP responsibility | SMART responsibility | Where documented | | ---------------------------- | ------------------------------------------------------------ | ---------------------------------------------- | ------------------------------------------------------------------------------------------------ | | **Token lifecycle** | Factory deployment, proxy upgrades, role assignment | ERC-20 token logic, compliance hook invocation | [Deployment Architecture](/docs/architecture/components/asset-contracts/deployment-architecture) | | **Compliance rules** | System-seeded templates, module configuration per instrument | Rule evaluation engine, module interface | [Compliance Modules](/docs/architecture/security/compliance) | | **Identity verification** | Claim issuance workflow, trusted issuer management | Identity registry, claim storage (OnchainID) | [Identity & Compliance](/docs/architecture/security/identity-compliance) | | **Token features** | Runtime-pluggable features (fees, yield, governance) | Extension hook points (`SMARTConfigurable`) | [Token Features](/docs/architecture/components/token-features) | | **Access control** | Seven per-asset roles, multi-tenant authority model | Role-checking hooks | [RBAC](/docs/architecture/components/asset-contracts/rbac) | | **Transfer enforcement** | Policy design (which modules, which parameters) | On-chain enforcement (revert if non-compliant) | [Compliance Transfer Flow](/docs/architecture/flows/compliance-transfer) | | **Multi-asset organization** | Instrument profiles, shared identity registries | Separate-contract-per-instrument model | [Legacy Types](/docs/architecture/components/asset-contracts/legacy-types) | *** ## Organizing multiple assets [#organizing-multiple-assets] ERC-3643 uses a **separate contract per instrument** model. Each financial instrument - a bond tranche, equity share class, or fund unit - is deployed as its own token contract with independent compliance configuration and lifecycle. This provides: * **Lifecycle isolation** - one bond can mature while related equity continues trading * **Compliance independence** - each instrument has its own module configuration and investor restrictions * **Upgrade independence** - upgrade one instrument without risk to others * **Clear accounting** - each token has its own `totalSupply`, holder list, and transaction history Related assets are linked through **on-chain references** (when behavior depends on another asset) or **identity claims** (when assets share an issuer or program for reporting). ERC-3643 was chosen over ERC-1400 for its modular compliance engine, on-chain identity integration via OnchainID, and active ecosystem support. *** ## DALP's recommended approach [#dalps-recommended-approach] **DALPAsset** is the recommended contract type for all new deployments. It extends SMART Protocol with the `SMARTConfigurable` extension, allowing any combination of [token features](/docs/architecture/components/token-features) to be attached at runtime - after deployment. * [DALPAsset](/docs/architecture/components/asset-contracts/dalp-asset) - full architecture and configuration model * [Legacy Types](/docs/architecture/components/asset-contracts/legacy-types) - compile-time types and when they still apply * [Legacy-Equivalent Presets](/docs/architecture/components/asset-contracts/instrument-profiles) - pre-built configurations per instrument type *** ## Where to read next [#where-to-read-next] * **How identity enforcement works** - [Identity & Compliance](/docs/architecture/security/identity-compliance) * **What each compliance rule does** - [Compliance Modules](/docs/architecture/security/compliance) * **Transfer enforcement step-by-step** - [Compliance Transfer Flow](/docs/architecture/flows/compliance-transfer) # Airdrop Source: https://docs.settlemint.com/docs/architecture/components/capabilities/airdrop The Airdrop capability provides a Merkle-proof-based token distribution system with three strategies -- time-bound, vesting, and push -- each deployed through the factory pattern with pluggable claim tracking. **Purpose:** Describe the Airdrop capability -- a token distribution system supporting three strategies with gas-efficient claim tracking and compliance integration. * **Doc type:** Reference ## What you'll find here [#what-youll-find-here] * Three airdrop strategy types and when to use each * Claim tracking mechanisms (bitmap vs amount-based) * Owned contracts and trust boundaries * Configuration surface and dependencies ## Boundary [#boundary] The Airdrop capability handles token distribution from a funded airdrop contract to eligible recipients. It does not handle token issuance (that is the SMART Protocol's responsibility) or off-chain Merkle tree generation (handled by platform tooling before deployment). ## Owned contracts [#owned-contracts] | Contract | Responsibility | | ---------------------------------- | ------------------------------------------------------------------------- | | DALPAirdrop | Base airdrop logic: Merkle verification, claim dispatch, owner withdrawal | | DALPTimeBoundAirdropImplementation | Time-constrained distribution with start/end enforcement | | DALPVestingAirdropImplementation | Progressive release via linear vesting schedule | | DALPPushAirdropImplementation | Admin-initiated batch distribution, no user claiming | | DALPBitmapClaimTracker | Binary claimed/unclaimed tracking, gas-efficient for large lists | | DALPAmountClaimTracker | Partial claim tracking, records amounts per user | | Factory + Proxy contracts | CREATE2 deployment and transparent upgradeability per strategy | ## Airdrop strategies [#airdrop-strategies] | Strategy | Use case | Claim model | Key configuration | | ---------- | --------------------------------------- | ---------------------------------------- | ------------------------------------------------ | | Time-Bound | Token launches, community rewards | User-initiated via Merkle proof | Start time, end time, expiration handling | | Vesting | Team and investor distributions | Progressive release via linear vesting | Vesting period, cliff duration, vesting strategy | | Push | Marketing campaigns, engagement rewards | Admin-initiated, no user action required | Batch size, recipient list | ### Time-Bound Airdrop [#time-bound-airdrop] Recipients claim tokens by submitting a Merkle proof within a configured time window. After expiration, unclaimed tokens can be withdrawn by the owner. This strategy suits large-scale distributions where recipients self-serve at their convenience. ### Vesting Airdrop [#vesting-airdrop] Tokens release progressively according to a linear vesting schedule with an optional cliff period. Recipients claim their vested portion at any time after the cliff. The DALPLinearVestingStrategy contract enforces the release curve on-chain. ### Push Airdrop [#push-airdrop] An administrator pushes tokens directly to recipient addresses in batches. No Merkle proof or user action is required. This strategy suits targeted campaigns where the distributor controls timing and recipient selection. ## Claim tracking [#claim-tracking] Two pluggable claim tracker strategies prevent double-claiming: * **DALPBitmapClaimTracker** -- stores a single bit per recipient. Optimal for simple all-or-nothing claims across large recipient lists. Gas cost is constant regardless of list size. * **DALPAmountClaimTracker** -- stores the claimed amount per recipient. Supports partial claims where users may claim a fraction of their allocation over multiple transactions. The claim tracker is selected at airdrop deployment and cannot be changed after initialization. ## Roles [#roles] Airdrop contracts use token-centric access control through ISMARTTokenAccessManager. This means role assignments are governed by the same access manager that controls the underlying token, ensuring consistent permission boundaries across the asset lifecycle. ## Trust boundaries [#trust-boundaries] * **Merkle proof verification** -- eligibility is cryptographically verified against the on-chain Merkle root. No off-chain oracle trust is required at claim time. * **Reentrancy protection** -- all claim and withdrawal operations use ReentrancyGuard. * **ERC-2771 meta-transaction support** -- trusted forwarder enables gasless claiming without compromising sender identity verification. * **Time enforcement** -- start and end timestamps are enforced on-chain; they cannot be bypassed by transaction ordering. ## Dependencies [#dependencies] | Dependency | Role | | ---------------------------------- | --------------------------------------------------------------- | | DALP System infrastructure | Factory deployment, access control integration | | Token contracts (SMART Protocol) | Token being distributed; must be funded to the airdrop contract | | Merkle tree generation (off-chain) | Produces the root and per-recipient proofs before deployment | ## Configuration surface [#configuration-surface] | Parameter | Scope | Mutability | | ------------------------ | -------------- | --------------------------------- | | Merkle root | All strategies | Immutable after deployment | | Start / end timestamps | Time-Bound | Immutable after deployment | | Vesting period and cliff | Vesting | Immutable after deployment | | Claim tracker strategy | All strategies | Selected at deployment, immutable | | Trusted forwarder | All strategies | Configured at deployment | Smart contracts are deployed and available. The UI wizard for creating and managing airdrops is in development. ## Related [#related] * [Airdrop distribution flow](/docs/architecture/flows/airdrop-distribution) for the step-by-step claim and distribution sequence * [Capabilities layer overview](/docs/architecture/components/capabilities) for how capabilities extend the platform * [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) for the compliance framework airdrops build on * [Component catalog](/docs/architecture/components) for the full platform inventory # Overview Source: https://docs.settlemint.com/docs/architecture/components/capabilities Capabilities are optional operational modules deployed through the factory pattern. Each capability adds specific functionality -- token distribution, treasury management, settlement, or token sales -- without modifying core platform services. **Purpose:** Describe the capabilities layer -- operational tools that extend DALP through a standardized factory deployment pattern. * **Doc type:** Reference ## What you'll find here [#what-youll-find-here] * How capabilities extend the platform without modifying core services * The factory deployment pattern for capability installation * Inventory of all four capabilities with responsibilities * Links to each capability's detailed architecture page ## Layer overview [#layer-overview] Capabilities are self-contained operational modules. Unlike infrastructure services that run continuously, capabilities are deployed on-demand through the factory pattern: a factory contract deploys and configures capability instances per asset or per tenant. Each instance operates independently with its own on-chain state. This pattern provides two architectural benefits. First, capabilities can be added or upgraded without redeploying core platform services. Second, each capability instance has isolated state, preventing cross-contamination between assets or tenants. All capabilities interact with the SMART Protocol for compliance enforcement and use infrastructure services (Execution Engine, Transaction Signer) for reliable execution. ## Components [#components] | Component | Responsibility | Details | | ------------------------------------------------------------------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------ | | [Airdrop](/docs/architecture/components/capabilities/airdrop) | Token distribution | Three strategies: direct push, merkle-drop for gas-efficient claims, and vesting schedules | | [Vault](/docs/architecture/components/capabilities/vault) | Multi-signature treasury management | Configurable approval thresholds, time-locks, and spending limits | | [XvP Settlement](/docs/architecture/components/capabilities/xvp-settlement) | Atomic cross-party settlement | Delivery-versus-payment with escrow, matching, and atomic execution | | [Token Sale (DAIO)](/docs/architecture/components/capabilities/token-sale) | Digital Asset Initial Offering | Configurable sale parameters, investor limits, and compliance-gated participation | | [Issuer-Signed Scalar Feed](/docs/architecture/components/capabilities/issuer-signed-scalar-feed) | Price data attestation | Issuer-signed price feeds with drift protection, history modes, and fixed-point precision | ## Related [#related] * [Component catalog](/docs/architecture/components) for the full platform inventory * [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) for the compliance framework capabilities build on * [Infrastructure layer](/docs/architecture/components/infrastructure) for the services capabilities depend on * [Key flows](/docs/architecture/flows) for end-to-end operation sequences involving capabilities # Issuer-Signed Scalar Feed Source: https://docs.settlemint.com/docs/architecture/components/capabilities/issuer-signed-scalar-feed The Issuer-Signed Scalar Feed is a factory-deployed capability that allows asset issuers to publish cryptographically signed price data with configurable history retention, drift protection, and fixed-point precision. **Purpose:** Describe the Issuer-Signed Scalar Feed capability — how issuers attest to price data on-chain through a factory-deployed feed contract with signature verification, history modes, and drift protection. * **Doc type:** Explanation * **What you'll find here:** * Factory deployment model and lifecycle * Configuration surface: history modes, drift allowance, decimals * Cryptographic signing model and verification * Fixed-point value format * Integration with FeedsDirectory for discovery * **Related:** * [Feeds system](/docs/architecture/components/infrastructure/feeds-system) — central registry and feed types overview * [Feeds update flow](/docs/architecture/flows/feeds-update-flow) — end-to-end lifecycle of publishing feed values * [Capabilities overview](/docs/architecture/components/capabilities) — factory pattern used by all capabilities *** ## At a glance [#at-a-glance] * Asset issuers **cryptographically sign** price values, providing direct attestation of authenticity * Deployed through the **factory pattern** — one feed instance per asset or subject, same model as Airdrop, Vault, XvP Settlement, and Token Sale * Three **history modes** control how much historical data is retained on-chain * **Drift allowance** protects against anomalous price reports by limiting value changes between updates * All values must be **positive** and use **fixed-point integer** representation with configurable decimals *** ## Configuration [#configuration] Each issuer-signed scalar feed instance is configured at deployment. Parameters are immutable after creation. | Parameter | Options / Range | Purpose | | ------------------- | ----------------------------- | ------------------------------------------------- | | **History mode** | Latest-only · Bounded · Full | Controls on-chain retention of historical values | | **Drift allowance** | Percentage (e.g. 10%) | Maximum allowed change between consecutive values | | **Positive-only** | Always enforced | Rejects zero or negative values on-chain | | **Decimals** | Fixed integer (e.g. 2, 8, 18) | Precision for fixed-point representation | ### History modes [#history-modes] **Latest-only** — Stores only the most recent signed value. Minimizes storage cost. Suitable when only current pricing matters. **Bounded** — Retains a configured number of recent values. Enables recent history queries for verification or trend analysis. Older values are pruned automatically when the limit is reached. **Full** — Maintains the complete history of all signed values. Provides maximum transparency and full audit trails. Use when historical verification is a regulatory or operational requirement. *** ## Signing model [#signing-model] Each update requires a cryptographic attestation from the authorized issuer. The feed contract verifies the signature on-chain before accepting the value. A signed update includes: | Component | Description | | ------------- | ------------------------------------------------------- | | **Value** | The numeric price being reported (fixed-point integer) | | **Timestamp** | When the issuer signed the value | | **Signature** | Issuer's cryptographic signature over value + timestamp | **Verification:** The feed contract recovers the signer from the signature and checks it matches the authorized issuer address. If the signature is invalid or from an unauthorized address, the update is rejected on-chain. **Non-repudiation:** Because the issuer's private key produced the signature, the issuer cannot deny having attested to that value at that timestamp. Applications and auditors can independently verify any historical signed value. *** ## Value format [#value-format] Values use **fixed-point integer** representation to avoid floating-point ambiguity. The decimal count is fixed per feed instance and determines how to interpret the stored integer: ``` stored_value ÷ 10^decimals = human-readable price ``` **Example:** A price of 150.00 EUR with `decimals = 2` is stored as `15000` (since 15000 ÷ 10² = 150.00). This ensures: * Consistent precision across all values for a given feed * No floating-point rounding errors * Unambiguous interpretation by all consumers *** ## Integration with FeedsDirectory [#integration-with-feedsdirectory] Issuer-signed scalar feeds register in the FeedsDirectory like any other feed type. The directory maps a **(subject, topic)** pair to the feed contract address. **Registration flow:** 1. Factory contract deploys a new feed instance with the configured parameters 2. Feed is registered in the FeedsDirectory with subject, topic, feed address, kind (scalar), and schema hash 3. Consumers discover the feed by querying the directory with the same subject + topic **Consumer read path:** 1. Consumer queries FeedsDirectory with (subject, topic) 2. Directory returns the feed contract address 3. Consumer calls the feed contract to retrieve the current signed value, signature, timestamp, and sequence number The directory indirection means the underlying feed can be replaced (e.g. upgraded to a new implementation) without consumers changing their code. *** ## See also [#see-also] * [Feeds system](/docs/architecture/components/infrastructure/feeds-system) — central registry, Chainlink adapter, trust model * [Feeds update flow](/docs/architecture/flows/feeds-update-flow) — step-by-step lifecycle including validation checkpoints * [Capabilities overview](/docs/architecture/components/capabilities) — factory deployment pattern shared by all capabilities # Token Sale (DAIO) Source: https://docs.settlemint.com/docs/architecture/components/capabilities/token-sale The Token Sale capability provides compliant primary distribution infrastructure for Digital Asset Initial Offerings (DAIOs), with ERC20 payment support, optional vesting, soft-cap finalization, and identity-gated buyer eligibility. The Token Sale capability provides primary distribution infrastructure for Digital Asset Initial Offerings. It checks buyer eligibility through the SMART Protocol compliance layer, collects payment in approved ERC20 currencies, supports optional terms acknowledgement, and can apply vesting schedules to purchased tokens. ## Scope [#scope] Token Sale covers the offering itself: sale deployment, payment configuration, eligibility checks, purchase recording, finalization, refunds, and token withdrawal. Secondary trading, lifecycle compliance outside the purchase path, and post-offering asset servicing remain with the SMART Protocol and other DALP capabilities. ## Contract architecture [#contract-architecture] | Contract | Responsibility | | ---------------------------------- | ------------------------------------------------------------------------------------ | | IDALPTokenSale | Interface defining the complete Token Sale API (functions, events, errors) | | DALPTokenSaleImplementation | Main logic: compliance checks, vesting, multi-currency pricing, lifecycle management | | DALPTokenSaleProxy | Factory-managed proxy preserving state and address across implementation updates | | DALPTokenSaleFactoryImplementation | Factory for CREATE2 deployment of new sale instances with deterministic addresses | The proxy pattern allows implementation updates without redeploying a sale or changing its address. The factory uses CREATE2 so teams can predict a sale address before deployment. ## Roles [#roles] | Role | Source | Permissions | | ---------------------------- | --------------------- | -------------------------------------------------------------------------------- | | SALE\_ADMIN\_ROLE | Token access manager | Configure sale parameters, manage lifecycle transitions, set vesting and pricing | | FUNDS\_MANAGER\_ROLE | Token access manager | Withdraw collected sale proceeds | | GOVERNANCE\_ROLE | Token access manager | Create token sales for the governed token | | TOKEN\_FACTORY\_MODULE\_ROLE | System access control | Create token sales through approved system automation | | SYSTEM\_MANAGER\_ROLE | System access control | Update the shared Token Sale implementation used by the factory | Role assignments flow through the token's ISMARTTokenAccessManager, so sale administration uses the same access control framework as the underlying asset. ## Sale lifecycle [#sale-lifecycle] The token sale progresses through setup, active sale phases, and final outcomes. Transitions are enforced on-chain. | Phase | Status | Behavior | | ----------- | -------------------- | -------------------------------------------------------------------------------------------------------------- | | Setup | `SETUP` | Configure timing, pricing, payment currencies, purchase limits, and vesting before purchases open. | | Presale | `PRESALE` (optional) | Accept purchases from whitelisted presale investors only. Auto-transitions to public sale at presale end time. | | Public Sale | `PUBLIC_SALE` | Accept purchases from all eligible buyers. Enforce per-investor limits and hard cap. | | Paused | `PAUSED` | Temporarily halt all purchases. Configuration preserved. Can resume to presale or public sale. | | Ended | `ENDED` | Manually concluded. Can be finalized after the sale closes. | | Success | `SUCCESS` | Finalized successfully. Buyers can withdraw tokens when vesting and soft-cap rules allow it. | | Failed | `FAILED` | Finalized below the configured soft cap. Buyers can claim refunds for recorded contributions. | The usual path is setup, optional presale, public sale, and finalization. Paused is a reversible detour from an active phase. If a soft cap is configured, finalization records whether the sale succeeded or failed. ## Payment support [#payment-support] The token sale accepts approved ERC20 tokens as payment. Each accepted currency has a configurable price ratio that determines the exchange rate between payment currency and sale tokens. Price ratios are set by the sale administrator during setup and can accommodate different decimal precisions across payment currencies. The contract calculates token amounts at purchase time based on the configured ratio. ## Vesting [#vesting] When vesting is configured, purchased tokens release according to the configured schedule: * A **cliff period** must elapse before any tokens can be withdrawn * After the cliff, tokens release linearly over the configured **vesting duration** * Investors withdraw their vested portion at any time after the cliff Without vesting, tokens distribute immediately upon purchase. ## Compliance [#compliance] Buyer eligibility is enforced through the SMART Protocol compliance layer. Before any purchase executes, the contract checks whether the underlying token can transfer the purchased amount from the sale contract to the buyer. Purchases that fail the token's compliance rules are rejected. This compliance check is the same mechanism used throughout DALP for transfer restrictions and investor verification. The same rule path applies during the offering and later transfers. ## Event-indexed sale state [#event-indexed-sale-state] Token Sale contracts emit sale-state data for DALP to build the indexed read model from lifecycle events over time. `DALPTokenSaleCreated` seeds the sale address, token address, sale timing, hard cap, purchase limits, and initial optional configuration such as payment currencies, soft cap, presale settings and whitelist, vesting settings, and terms hash. Later events keep mutable sale state current: `SaleParametersUpdated` identifies the operator and carries `saleStartTime`, `saleEndTime`, `hardCap`, `minPurchase`, `maxPurchase`, `softCap`, and `softCapReached` as sale-state fields; `PaymentCurrencyAdded` and `PaymentCurrencyRemoved` update accepted payment currencies; `PresaleConfigured`, `VestingConfigured`, `PresaleWhitelistUpdated`, and `TermsHashSet` update optional sale controls; `SaleStatusUpdated` and `PhaseTransitioned` update sale status; `TokensPurchased` records purchases and sold totals; `SaleTimingExtended` updates the sale end time; and `SoftCapReached` marks the soft cap as reached. Use the API, CLI, or Token Sale read model for operational queries. Contract events provide audit evidence and indexing inputs. A current application view needs the creation event plus the later lifecycle, configuration, purchase, timing, and soft-cap events. The API remains the stable integration boundary for applications. ## Trust boundaries [#trust-boundaries] * **Eligibility checks**: every purchase is checked through the SMART Protocol compliance layer before tokens are allocated. * **Reentrancy protection**: purchase, refund, token withdrawal, and fund withdrawal operations use ReentrancyGuard. * **Pausable operations**: the sale administrator can halt purchases during incident response and resume the active phase later. * **Setup-time controls**: hard cap, purchase limits, payment currencies, presale settings, vesting, and terms hash are configured before activation. Active sales can be extended through the sale timing control. ## Dependencies [#dependencies] | Dependency | Role | | -------------------------------- | -------------------------------------------------- | | SMART Protocol compliance layer | Buyer eligibility verification | | Token contracts (SMART Protocol) | The digital asset being sold | | System access control | System automation and implementation management | | Token access manager | Role assignments for sale and funds administration | ## Configuration surface [#configuration-surface] | Parameter | Scope | Mutability | | ------------------------------------------- | ------------- | ----------------------------------------------------------------- | | Sale timing (start, end, presale window) | Sale instance | Set during setup; sale end can be extended while active or paused | | Price ratios per payment currency | Sale instance | Configurable by SALE\_ADMIN\_ROLE during setup | | Per-investor purchase limits (min/max) | Sale instance | Set during setup | | Hard cap (total tokens available) | Sale instance | Immutable after activation | | Vesting parameters (start, duration, cliff) | Sale instance | Set during setup, immutable after activation | | Accepted payment currencies | Sale instance | Managed by SALE\_ADMIN\_ROLE during setup | Token Sale smart contracts and developer APIs are available. Use the API or CLI to manage token-sale setup and the sale lifecycle when your deployment exposes those operations outside the UI. ## Related [#related] * [DAIO offering flow](/docs/architecture/flows/daio-offering) for the step-by-step participation and settlement sequence * [Capabilities layer overview](/docs/architecture/components/capabilities) for how capabilities extend the platform * [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) for the compliance and identity framework * [Component catalog](/docs/architecture/components) for the full platform inventory # Vault Source: https://docs.settlemint.com/docs/architecture/components/capabilities/vault The Vault capability provides multi-signature treasury management for DAOs and corporate treasuries, with role-based access control, configurable confirmation thresholds, and support for ETH, ERC20, and general contract call transactions. **Purpose:** Describe the Vault capability -- a multi-signature treasury system with role separation, configurable approval thresholds, and emergency controls. * **Doc type:** Reference ## What you'll find here [#what-youll-find-here] * Multi-signature transaction lifecycle (propose, confirm, execute) * Role separation and access control model * Supported transaction types * Trust boundaries and failure modes ## Boundary [#boundary] The Vault manages custody of ETH and ERC20 tokens through a multi-signature approval process. It does not handle token issuance, compliance verification, or investor identity -- those responsibilities belong to the SMART Protocol and compliance layer. The Vault operates as a standalone treasury tool that integrates with DALP System infrastructure for deployment and access control. ## Owned contracts [#owned-contracts] | Contract | Responsibility | | ------------------------------ | ---------------------------------------------------------------------------- | | DALPVault | Multi-sig logic: proposals, confirmations, execution, pause, role management | | DALPVaultFactoryImplementation | Factory for CREATE2 deployment of new vault instances | ## Roles [#roles] | Role | Permissions | | ---------------- | -------------------------------------------------------------------------------- | | SIGNER\_ROLE | Propose new transactions, confirm pending transactions, revoke own confirmations | | EMERGENCY\_ROLE | Pause and unpause all vault operations | | GOVERNANCE\_ROLE | Modify vault parameters (confirmation threshold, auto-execution toggle) | Role separation ensures that day-to-day signers cannot unilaterally change vault rules, and governance changes require a distinct authorization path from transaction approval. ## Transaction lifecycle [#transaction-lifecycle] The vault follows a proposal-confirmation-execution model: 1. **Proposal** -- A signer submits a transaction specifying the target address, value, and calldata. The vault stores the proposal and records the proposer's confirmation. 2. **Confirmation** -- Additional signers review and confirm the proposal. Each confirmation is recorded individually and can be revoked before execution. 3. **Execution** -- When the confirmation count reaches the required threshold, the transaction executes. If auto-execution is enabled, execution triggers automatically upon the final confirmation. Otherwise, any signer can trigger execution manually after threshold is met. ## Transaction types [#transaction-types] | Type | Description | | ---------------- | --------------------------------------------------------- | | ETH transfers | Native currency sent to any address | | ERC20 transfers | Token transfers using standard ERC20 approval mechanics | | Contract calls | Arbitrary calldata execution against any contract address | | Batch operations | Multiple transactions bundled in a single proposal | All transaction types follow the same proposal-confirmation-execution lifecycle. The vault does not differentiate between simple transfers and complex contract interactions at the approval layer. ## Trust boundaries [#trust-boundaries] * **Role separation** -- Signers, emergency operators, and governance are distinct roles. No single role can both approve transactions and change vault parameters. * **Configurable confirmation threshold** -- The number of required confirmations is set by governance. Increasing the threshold raises the coordination cost of unauthorized transactions. * **Pausable operations** -- Emergency role holders can immediately halt all vault operations, preventing execution of pending transactions during incident response. * **Audit trail** -- Every proposal, confirmation, revocation, and execution emits on-chain events, providing an immutable record for compliance and forensic review. ## Dependencies [#dependencies] | Dependency | Role | | ----------------------------------------- | ----------------------------------------------- | | DALP System infrastructure | Factory deployment, system-level access control | | Access control (ISMARTTokenAccessManager) | Role assignment and enumeration | ## Configuration surface [#configuration-surface] | Parameter | Scope | Mutability | | ---------------------- | -------------- | ----------------------------------- | | Required confirmations | Vault instance | Modifiable by GOVERNANCE\_ROLE | | Auto-execution toggle | Vault instance | Modifiable by GOVERNANCE\_ROLE | | Signer list | Vault instance | Managed via role grants/revocations | ## Failure modes [#failure-modes] | Failure | System behavior | | -------------------------- | ---------------------------------------------------------------------------------------- | | Insufficient confirmations | Transaction remains pending; no funds move | | Signer key compromise | Other signers can revoke the compromised signer's role; emergency pause halts operations | | Paused state | All proposals and executions blocked until unpaused by EMERGENCY\_ROLE | The Vault capability is in development. Smart contracts are available but the UI is not yet integrated into the Asset Console. ## Related [#related] * [Capabilities layer overview](/docs/architecture/components/capabilities) for how capabilities extend the platform * [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) for the compliance framework * [Infrastructure layer](/docs/architecture/components/infrastructure) for services the vault depends on * [Component catalog](/docs/architecture/components) for the full platform inventory # XvP Settlement Source: https://docs.settlemint.com/docs/architecture/components/capabilities/xvp-settlement The XvP Settlement capability provides atomic cross-party token exchanges with all-or-nothing execution guarantees, multi-party support, optional hashlock coordination for cross-chain legs, and per-sender approval management. **Purpose:** Describe the XvP Settlement capability -- an atomic settlement engine ensuring all-or-nothing token exchanges across multiple parties with optional cross-chain coordination. * **Doc type:** Reference ## What you'll find here [#what-youll-find-here] * Multi-party settlement mechanics and flow types * Hashlock coordination for cross-chain legs * Approval system and execution gates * Trust boundaries and expiration safety ## Boundary [#boundary] The XvP Settlement engine handles the atomic execution of token exchanges between parties. It does not handle price discovery, order matching, or compliance verification -- those are upstream concerns resolved before a settlement is created. The settlement contract guarantees that either all flows execute together or none execute at all. ## Owned contracts [#owned-contracts] | Contract | Responsibility | | -------------------------------------- | ----------------------------------------------------------------- | | DALPXvPSettlementImplementation | Settlement logic: flow management, approvals, hashlock, execution | | DALPXvPSettlementFactoryImplementation | Factory for CREATE2 deployment of settlement instances | | DALPXvPSettlementProxy | Transparent upgradeable proxy for each settlement instance | ## Settlement model [#settlement-model] A settlement consists of one or more **flows**, each specifying a sender, receiver, token, and amount. Any number of participants can be involved, and a single settlement can contain multiple token flows across different ERC20 tokens. ### Flow types [#flow-types] | Type | Condition | Behavior | | ------------- | ---------------------- | ----------------------------------------------------------------------------- | | Local flow | `externalChainId = 0` | Executed on the current chain; sender must provide ERC20 allowance | | External flow | `externalChainId != 0` | Recorded on-chain but not executed locally; represents a leg on another chain | Local flows are validated for ERC20 compliance before execution. External flows skip token introspection -- they serve as a coordination record for legs that settle on other networks. XVP settlement configuration ## Approval system [#approval-system] Each sender in a local flow must explicitly approve the settlement before execution can proceed. The approval system provides: * **Per-sender approvals** -- each party approves the entire settlement, not individual flows * **Revocation support** -- any sender can revoke approval before execution * **Auto-execution option** -- when enabled, execution triggers automatically once all local approvals (and hashlock, if applicable) are satisfied Only senders in local flows must provide ERC20 allowances and approvals. Senders appearing only in external flows do not need to approve on this chain. ## Hashlock coordination [#hashlock-coordination] When any flow targets an external chain (`externalChainId != 0`), the settlement requires a shared hashlock for cross-chain coordination: 1. **Creation** -- the settlement creator provides a `hashlock` (hash of a secret) when any flow is external 2. **External HTLC** -- counterparties deploy Hash Time-Locked Contracts on external chains using the same hashlock 3. **Secret reveal** -- once the secret is revealed on the external chain, anyone can call `revealSecret(bytes secret)` on the settlement contract. This is permissionless -- counterparties cannot block completion once the secret is public. 4. **Execution gate** -- local transfers execute only after both conditions are met: all local approvals received AND hashlock satisfied For pure local settlements (all flows have `externalChainId = 0`), the hashlock is optional and can be omitted entirely. ## Roles [#roles] | Role | Permissions | | --------------------- | -------------------------------------------------------- | | Settlement creator | Creates the settlement, defines all flows and parameters | | Senders (local flows) | Approve the settlement, provide ERC20 allowances | | Anyone | Reveal the hashlock secret (permissionless) | There is no dedicated admin role on the settlement itself. Once created, the settlement's terms are immutable -- the creator cannot modify flows or parameters after deployment. ## Trust boundaries [#trust-boundaries] * **Atomic execution** -- all local flows execute in a single transaction or none execute. There is no intermediate state where some transfers have completed and others have not. * **Expiration safety** -- every settlement has a configurable expiration date. After expiration, the settlement cannot execute and no funds are at risk of being locked indefinitely. * **ERC20 allowance verification** -- local flows are validated for sufficient token allowances before execution. External flows do not require local allowances. * **Withholding protection** -- the permissionless `revealSecret` function prevents any single party from blocking settlement completion by withholding the hashlock secret. ## Dependencies [#dependencies] | Dependency | Role | | -------------------------- | ----------------------------------------------- | | ERC20 token contracts | Tokens being exchanged in local flows | | DALP System infrastructure | Factory deployment, system-level access control | XVP REST API for programmatic settlement operations ## Configuration surface [#configuration-surface] | Parameter | Scope | Mutability | | ------------------- | ------------------- | ---------------------------------------------------- | | Expiration date | Settlement instance | Immutable after creation | | Auto-execution flag | Settlement instance | Immutable after creation | | Hashlock | Settlement instance | Set at creation when external flows exist; immutable | | Flow definitions | Settlement instance | Immutable after creation | ## Operator surfaces [#operator-surfaces] XvP settlement operations are exposed through the Asset Console, API, and CLI. The same settlement state is used across those channels: settlement flows, local approvals, cancellation votes, expiration status, stored secret metadata, and execution eligibility. Use each channel for a different operating mode: * **Asset Console:** human operators can review settlement detail, inspect flow and approval status, and trigger the settlement actions available for the current state. * **API:** applications can create settlements, list or read settlement state, approve or revoke approval, execute eligible settlements, submit or withdraw cancellation requests, withdraw expired settlements, reveal secrets, and decrypt stored settlement secrets. * **CLI:** operators and test workflows can call the `xvp-settlements` command group for `list`, `read`, `create`, `approve`, `revoke-approval`, `execute`, `cancel`, `withdraw-cancel`, `withdraw-expired`, `reveal-secret`, and `decrypt` operations. Withdrawal is split between cancellation-request withdrawal and expired-settlement withdrawal. For external-chain legs, DALP records the leg and gates local execution on the hashlock secret. DALP does not turn separate chains into one transaction. The external-chain workflow must lock and release the matching leg, then reveal the shared secret so the local settlement can proceed. ## Failure modes [#failure-modes] | Failure | System behavior | | ---------------------------- | -------------------------------------------------------------------------------- | | Missing approval | Settlement remains pending; no transfers execute | | Expiration reached | Settlement becomes non-executable; funds remain with original owners | | Hashlock not revealed | Local execution blocked until secret is provided; expiration eventually releases | | Insufficient ERC20 allowance | Execution reverts; settlement returns to approved-but-unexecuted state | ## Related [#related] * [XvP settlement flow](/docs/architecture/flows/xvp-settlement) for the step-by-step execution sequence * [Capabilities layer overview](/docs/architecture/components/capabilities) for how capabilities extend the platform * [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) for the compliance framework * [Component catalog](/docs/architecture/components) for the full platform inventory # Component Catalog Source: https://docs.settlemint.com/docs/architecture/components Master inventory of all DALP components organized by architectural layer: platform interfaces, infrastructure services, operational capabilities, and the on-chain SMART Protocol. Each entry links to its detailed architecture page. **Purpose:** Provide a single navigable index of every component in the platform, grouped by architectural layer. * **Doc type:** Reference ## What you'll find here [#what-youll-find-here] * Complete component inventory with responsibilities * Grouping by architectural layer (platform, infrastructure, capabilities, contracts) * Direct links to each component's detailed architecture page * Boundary context for understanding ownership and dependencies ## Platform [#platform] User-facing interfaces that operators and integrators interact with directly. | Component | Responsibility | | --------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | | [Asset Console](/docs/architecture/components/platform/asset-console) | Web interface for asset lifecycle management, compliance workflows, and portfolio views | | [Unified API](/docs/architecture/components/platform/unified-api) | OpenAPI-documented programmatic access to all platform operations | [Platform layer overview](/docs/architecture/components/platform) for boundary context. ## Infrastructure [#infrastructure] Backend services that power the platform's blockchain interactions and workflow orchestration. | Component | Responsibility | | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | | [Execution Engine](/docs/architecture/components/infrastructure/execution-engine) | Reliable workflow orchestration with persistent state and exactly-once semantics (Restate) | | [Key Guardian](/docs/architecture/components/infrastructure/key-guardian) | Secure cryptographic key storage with HSM and cloud KMS integration | | [Transaction Signer](/docs/architecture/components/infrastructure/transaction-signer) | Transaction preparation, gas estimation, nonce management, and signing | | [Contract Runtime](/docs/architecture/components/infrastructure/contract-runtime) | Smart contract interaction, ABI encoding, and call routing | | [Chain Indexer](/docs/architecture/components/infrastructure/chain-indexer) | Blockchain event processing, data translation, and queryable state projection | | [Chain Gateway](/docs/architecture/components/infrastructure/chain-gateway) | Multi-network connectivity with failover and load balancing | | [EVM RPC Node](/docs/architecture/components/infrastructure/evm-rpc-node) | Blockchain network access for transaction submission and state queries | | [Feeds System](/docs/architecture/components/infrastructure/feeds-system) | Trusted market data feeds for pricing, NAV calculations, and reference data | [Infrastructure layer overview](/docs/architecture/components/infrastructure) for boundary context. ## Asset Contracts [#asset-contracts] The foundational contract primitive — DALPAsset — and specialized legacy types for existing deployments. | Component | Responsibility | | ---------------------------------------------------------------- | --------------------------------------------------------------- | | [Asset Contracts](/docs/architecture/components/asset-contracts) | DALPAsset and specialized token types for financial instruments | See [Token Features](/docs/architecture/components/token-features) for runtime-pluggable extensions to DALPAsset. ## Token Features [#token-features] Runtime-pluggable extensions that add fees, governance, lifecycle, and yield to DALPAsset tokens. | Component | Responsibility | | -------------------------------------------------------------- | -------------------------------------------------------------------------------------- | | [Token Features](/docs/architecture/components/token-features) | Runtime-pluggable features for DALPAsset: fees, analytics, governance, maturity, yield | ## Capabilities [#capabilities] Operational tools that extend the platform through the factory deployment pattern. | Component | Responsibility | | --------------------------------------------------------------------------- | ------------------------------------------------------------------------- | | [Airdrop](/docs/architecture/components/capabilities/airdrop) | Token distribution across three strategies (push, merkle-drop, vesting) | | [Vault](/docs/architecture/components/capabilities/vault) | Multi-signature treasury management with configurable approval thresholds | | [XvP Settlement](/docs/architecture/components/capabilities/xvp-settlement) | Atomic cross-party settlement with delivery-versus-payment guarantees | | [Token Sale (DAIO)](/docs/architecture/components/capabilities/token-sale) | Digital Asset Initial Offering with configurable sale parameters | [Capabilities layer overview](/docs/architecture/components/capabilities) for boundary context. ## Related [#related] * [Overview](/docs/architecture/overview) for architecture principles and quality attributes * [Key flows](/docs/architecture/flows) for cross-component operation sequences * [Integrations](/docs/architecture/integrations) for external system boundaries * [Security](/docs/architecture/security) for trust boundaries and controls mapping * [Deployment topology](/docs/architecture/overview/deployment-topology) for runtime zones and network boundaries # Chain Gateway Source: https://docs.settlemint.com/docs/architecture/components/infrastructure/chain-gateway The Chain Gateway is DALP's eRPC-based gateway for outbound EVM JSON-RPC traffic. It routes application requests through configured upstream RPC endpoints with method-aware timeouts, retries, hedging, and caching. ## Overview [#overview] The Chain Gateway is DALP's eRPC-based gateway for outbound EVM JSON-RPC traffic. DALP services call the gateway instead of connecting directly to every blockchain node or provider endpoint. The gateway gives operators one place to configure upstream RPC endpoints, request policies, cache storage, ingress exposure, and service health dependencies. Application code can keep using the configured DALP RPC URL while the gateway handles routing and failure handling behind that endpoint. ## Architecture [#architecture] ## What operators configure [#what-operators-configure] The deployment chart enables the gateway by default and exposes it internally as `dalp-erpc`. The gateway configuration is grouped under `erpc` in the DALP Helm values. | Area | What it controls | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Upstreams | The EVM RPC endpoints the gateway can route to. Each upstream has an identifier, endpoint URL, EVM settings, and optional upstream-specific failsafe rules. | | Network integrity | EVM request integrity checks, including highest-block consistency and `eth_getLogs` block-range enforcement. | | Failsafe policies | Method-aware timeouts, retries, retry jitter, exponential backoff, and hedged requests. | | Cache and shared state | Redis-backed response caching and shared gateway state. | | Exposure | Optional Ingress, HTTPRoute, or OpenShift Route settings for publishing the JSON-RPC endpoint outside the cluster. The chart creates the network entry point only. | | Startup dependencies | A TCP dependency check that waits for Redis before the gateway starts. | Enabling `erpc.ingress.enabled`, `erpc.httpRoute.enabled`, or `erpc.openShiftRoute.enabled` makes the JSON-RPC endpoint reachable from outside the cluster. The default Ingress, HTTPRoute, and OpenShift Route templates configure host, path, TLS, and gateway attachment only. Before exposing the endpoint, keep it internal or put these boundary controls in front of it: * authentication and authorization for RPC callers * strict IP allowlists, or another internal-only exposure model * rate limiting or request throttling * network-level protections from your Kubernetes, OpenShift, ingress controller, or edge gateway policy ## Request policies [#request-policies] DALP ships gateway policies for the request patterns that commonly stress EVM RPC infrastructure: | Request pattern | Gateway behaviour | | ----------------------------- | ------------------------------------------------------------------------ | | `eth_getLogs` | Longer timeout, retries with backoff, jitter, and a hedged request path. | | Trace and debug methods | Longer timeout with limited retry attempts. | | Block and transaction reads | Shorter timeout with limited retries. | | Unfinalized or realtime reads | Short timeout, limited retries, and hedging. | | Finalized reads | Longer timeout and more retry attempts. | | Default requests | Catch-all timeout, retries, jitter, and hedging. | These policies reduce the chance that a transient RPC endpoint issue becomes an application-level failure. Operators still need healthy upstream endpoints and monitoring for the gateway and chain nodes. ## How DALP services use it [#how-dalp-services-use-it] DALP service configuration points to the gateway URL as the default RPC endpoint. The default in-cluster form is: ```text http://dalp-erpc:4000/settlemint/evm/ ``` Block explorer components also use the same gateway for JSON-RPC and trace requests, so explorer reads follow the same routing layer as the rest of the deployment. ## Operational notes [#operational-notes] * Configure at least one upstream RPC endpoint for each EVM network the deployment serves. * Use multiple upstreams when the environment needs failover or load distribution. * Keep Redis available because the gateway uses it for response caching and shared state. * Expose the gateway through Ingress, HTTPRoute, or an OpenShift Route only when external RPC access is required. * Monitor gateway latency, errors, and upstream health alongside the chain node metrics. ## See also [#see-also] * [EVM RPC Node](/docs/architecture/components/infrastructure/evm-rpc-node) for the node endpoint behind the gateway * [Chain Indexer](/docs/architecture/components/infrastructure/chain-indexer) for event processing * [Transaction Signer](/docs/architecture/components/infrastructure/transaction-signer) for transaction submission * [Self-hosting architecture](/docs/architecture/self-hosting) for the wider deployment model * [Observability](/docs/architecture/operability/observability) for monitoring # Chain Indexer Source: https://docs.settlemint.com/docs/architecture/components/infrastructure/chain-indexer The Chain Indexer transforms blockchain-optimized event logs and storage into domain model data structures optimized for application queries, providing millisecond-latency access to historical blockchain state. ## Overview [#overview] The Chain Indexer bridges the gap between blockchain data structures and application requirements. Blockchain storage optimizes for consensus verification, not application queries. The indexer transforms event logs into domain models that serve application needs efficiently. Application queries demand millisecond response times. Blockchain node queries for historical data can take seconds or fail entirely for complex aggregations. The Chain Indexer maintains query-optimized representations of blockchain state for responsive application experiences. ## Data translation [#data-translation] ## Event processing [#event-processing] ### Event subscription [#event-subscription] The indexer subscribes to contract events through the Chain Gateway. New block notifications trigger event retrieval for monitored contracts. Historical sync processes missed blocks during indexer downtime. ### Handler execution [#handler-execution] Event handlers receive decoded event parameters. Handlers execute business logic to update domain models. Handler execution is deterministic—the same events always produce the same state. ### State computation [#state-computation] Some domain models require computed state from multiple events. Transfer events update balance aggregates. Compliance events affect investor verification status. The indexer maintains these computed values for efficient queries. ## Domain models [#domain-models] The indexer constructs domain models optimized for application use cases: | Domain model | Source events | Query patterns | | -------------------- | ---------------------- | ------------------------------------- | | Asset balances | Transfer, Mint, Burn | Balance by holder, asset totals | | Investor portfolios | Transfer, Verification | Holdings by investor, portfolio value | | Transaction history | All events | Filtered by asset, investor, time | | Compliance status | Verification events | Current status, history | | Distribution records | Distribution events | Pending, completed, by period | ## Consistency handling [#consistency-handling] ### Block confirmation [#block-confirmation] Events from recent blocks may revert due to chain reorganizations. The indexer tracks confirmation depth and marks recent data as provisional. Applications display confirmation status for recent transactions. ### Reorg recovery [#reorg-recovery] Chain reorganizations require state rollback and reprocessing. The indexer maintains rollback capability for configurable block depths. Detected reorgs trigger: 1. Identification of affected blocks 2. State rollback to fork point 3. Reprocessing of canonical chain 4. Notification to connected clients ### Idempotent processing [#idempotent-processing] Event processing is idempotent. Reprocessing the same events produces identical state. This property enables safe recovery from any failure scenario. ## Query performance [#query-performance] ### Indexed access [#indexed-access] Database schemas optimize for expected query patterns. Indexes cover common filter and sort operations. Query analysis guides index maintenance. ### Materialized aggregates [#materialized-aggregates] Frequently accessed aggregates materialize in dedicated tables. Balance totals, transaction counts, and status summaries update incrementally rather than computing on demand. ### Pagination support [#pagination-support] Large result sets paginate efficiently through cursor-based navigation. Consistent ordering guarantees stable pagination across concurrent updates. ## Real-time updates [#real-time-updates] Connected clients receive real-time state updates through WebSocket connections. Event processing triggers notifications to subscribed clients. Update latency from block confirmation to client notification measures in milliseconds. ## Monitoring [#monitoring] Indexer health exposes through multiple metrics: | Metric | Purpose | Alert threshold | | --------------- | ------------------- | ------------------ | | Block lag | Processing currency | Blocks behind > 10 | | Processing time | Handler performance | P99 > 100ms | | Error rate | Handler failures | Rate > 0.1% | | Reorg depth | Chain stability | Depth > 3 blocks | Blockchain indexer monitoring all on-chain transactions ## See also [#see-also] * [Chain Gateway](/docs/architecture/components/infrastructure/chain-gateway) for blockchain connectivity * [Database](/docs/architecture/operability/database) for storage architecture * [Contract Runtime](/docs/architecture/components/infrastructure/contract-runtime) for event decoding # Contract Runtime Source: https://docs.settlemint.com/docs/architecture/components/infrastructure/contract-runtime The Contract Runtime provides a secure abstraction for smart contract interactions, managing ABI encoding, call execution, and state queries with automatic retry and error classification. ## Overview [#overview] The Contract Runtime translates high-level operations into smart contract calls. This component handles ABI encoding, call construction, and result decoding while abstracting blockchain interaction complexity. Smart contracts present binary interfaces that require precise encoding. Method selectors, parameter encoding, and result parsing demand careful implementation. The Contract Runtime centralizes this complexity, providing typed interfaces for application code. ## Interaction patterns [#interaction-patterns] ## ABI management [#abi-management] The Contract Runtime maintains an ABI registry for all deployed contracts. Contract deployment automatically registers ABIs. Version management tracks ABI changes across contract upgrades. | ABI source | Registration timing | Update mechanism | | ------------------ | ------------------- | -------------------- | | Platform contracts | Build time | Deployment pipeline | | User contracts | Deployment time | Automatic extraction | | External contracts | Configuration time | Manual registration | ## Operation types [#operation-types] ### Read operations [#read-operations] View and pure functions execute without transaction submission. Read calls route to Chain Gateway replica nodes for performance. Results decode through ABI definitions with type validation. ### Write operations [#write-operations] State-modifying functions require transaction submission through the Transaction Signer. The runtime constructs complete transaction objects including: * Target contract address * Encoded function call data * Gas limit estimation * Value transfer (if applicable) ### Multicall batching [#multicall-batching] Multiple read operations batch into single RPC calls when targeting compatible contracts. Batching reduces network round trips and improves response latency for complex queries. ## Error classification [#error-classification] Contract calls can fail in multiple ways. The runtime classifies errors to enable appropriate handling: | Error type | Cause | Handling | | ------------------ | ------------------------------- | --------------------------- | | Revert | Contract logic rejection | Surface to workflow | | Out of gas | Insufficient gas limit | Retry with higher limit | | Nonce collision | Concurrent transaction conflict | Resubmit with correct nonce | | Contract not found | Invalid address | Configuration error | | Invalid parameters | ABI encoding mismatch | Developer error | ## Gas optimization [#gas-optimization] The Contract Runtime implements gas optimization strategies: **Efficient encoding**: Parameter encoding uses gas-optimal patterns where alternatives exist. **Calldata compression**: Large parameters compress when contract supports decompression. **Access list generation**: EIP-2930 access lists pre-warm storage for gas reduction. **Simulation validation**: Complex transactions simulate before submission to catch failures before gas expenditure. ## Event decoding [#event-decoding] Contract events decode through registered ABIs. The Chain Indexer uses Contract Runtime decoding for event processing. Event parameters extract with full type information for domain model construction. ## Upgrade handling [#upgrade-handling] Contract upgrades require ABI registry updates. The runtime supports: **Proxy patterns**: Transparent and UUPS proxies route through consistent addresses while ABIs update. **Version tracking**: Multiple ABI versions coexist for historical data decoding. **Migration support**: Upgrade workflows coordinate ABI updates with contract deployment. ## Security considerations [#security-considerations] The Contract Runtime enforces security boundaries: * Only approved contract addresses accept calls * Parameter validation prevents injection attacks * Value transfers require explicit authorization * Administrative functions restrict to authorized roles ## See also [#see-also] * [Transaction Signer](/docs/architecture/components/infrastructure/transaction-signer) for transaction submission * [Chain Gateway](/docs/architecture/components/infrastructure/chain-gateway) for RPC connectivity * [Chain Indexer](/docs/architecture/components/infrastructure/chain-indexer) for event processing * [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) for contract architecture # EVM RPC Node Source: https://docs.settlemint.com/docs/architecture/components/infrastructure/evm-rpc-node The EVM RPC Node provides the direct interface to blockchain networks, handling JSON-RPC requests for transaction submission, state queries, and event subscription across public and private EVM-compatible chains. ## Overview [#overview] The EVM RPC Node is the blockchain network access point for DALP operations. It provides transaction submission, state queries, and event streaming through JSON-RPC. * **Doc type:** Reference * **What you'll find here:** * Network connectivity architecture * Supported networks (L1, L2, appchains, testnets, private) * RPC capabilities * Security model * **Related:** * [Chain Gateway](/docs/architecture/components/infrastructure/chain-gateway) for load balancing and failover * [Transaction Signer](/docs/architecture/components/infrastructure/transaction-signer) for transaction submission * [Chain Indexer](/docs/architecture/components/infrastructure/chain-indexer) for event processing Production deployments connect through the Chain Gateway, which manages load balancing and failover across multiple endpoints. ## Network connectivity [#network-connectivity] ## Supported networks [#supported-networks] DALP operates across all EVM-compatible networks without application changes. Network-specific configuration handles consensus differences, gas models, and confirmation requirements. ### Layer 1 mainnets [#layer-1-mainnets] | Network | Description | | ----------------- | ------------------------------------------ | | Ethereum | Primary EVM network with largest ecosystem | | Polygon PoS | High-throughput sidechain with low fees | | BNB Smart Chain | EVM-compatible chain with fast finality | | Avalanche C-Chain | Sub-second finality with Snowman consensus | | XDC Network | Enterprise-grade hybrid blockchain | | Gnosis Chain | Community-owned network (formerly xDai) | ### Layer 2 rollups [#layer-2-rollups] | Network | Type | Description | | ------------- | ---------- | --------------------------------------- | | Arbitrum One | Optimistic | Largest L2 by TVL with Nitro technology | | Base | Optimistic | OP Stack chain incubated by Coinbase | | OP Mainnet | Optimistic | Bedrock-based flagship OP Stack chain | | Linea | ZK | ConsenSys zkEVM with type 2 equivalence | | zkSync Era | ZK | Matter Labs ZK Stack rollup | | Scroll | ZK | Community-driven zkEVM | | Polygon zkEVM | ZK | Polygon type 2 zkEVM | ### Specialized and appchains [#specialized-and-appchains] | Network | Description | | --------------- | ---------------------------------------------------------------------------- | | ADI Chain | UAE-based ZK Stack chain for institutional finance and regulated stablecoins | | Immutable zkEVM | Gaming-focused Polygon zkEVM | | Worldchain | World ID verified OP Stack chain | ### Testnets [#testnets] | Network | Mainnet equivalent | | ---------------- | -------------------------- | | Sepolia | Ethereum testnet | | Holesky | Ethereum validator testnet | | Amoy | Polygon PoS testnet | | Arbitrum Sepolia | Arbitrum One testnet | | Base Sepolia | Base testnet | ### Private networks [#private-networks] | Client | Use case | | ------------------ | --------------------------------------------- | | Hyperledger Besu | Enterprise features with permissioning | | Go-Ethereum (Geth) | Reference implementation for private networks | | Nethermind | .NET-based client with enterprise plugins | | Erigon | Archive-optimized client for analytics | ### Network resources [#network-resources] * [Chainlist](https://chainlist.org) - Complete EVM network directory with public RPC endpoints * [L2Beat](https://l2beat.com/scaling/tvs) - Layer 2 network security and TVL analytics ## RPC capabilities [#rpc-capabilities] * **Transaction submission**: Routes signed transactions to the network mempool with structure validation before broadcast * **State queries**: Read current or historical blockchain state (balances, contract storage, call simulations) * **Event subscription**: WebSocket connections for real-time event streaming and new block notifications * **Trace APIs**: Debug and trace APIs for transaction execution details and gas breakdowns (client-dependent) ## Security [#security] * **Authentication**: API keys for managed providers; reverse proxy or node-level auth for self-hosted * **Network isolation**: Private nodes deploy within isolated network segments with firewall restrictions * **TLS encryption**: All RPC communication uses TLS with certificate verification ## See also [#see-also] * [Chain Gateway](/docs/architecture/components/infrastructure/chain-gateway) for load balancing and failover * [Transaction Signer](/docs/architecture/components/infrastructure/transaction-signer) for transaction submission * [Chain Indexer](/docs/architecture/components/infrastructure/chain-indexer) for event processing # DALP Execution Engine Source: https://docs.settlemint.com/docs/architecture/components/infrastructure/execution-engine The DALP Execution Engine orchestrates digital asset lifecycle operations with guaranteed delivery, automatic retry handling, and transparent failure recovery, ensuring complex multi-step processes complete reliably even through system failures. ## Overview [#overview] The DALP Execution Engine coordinates multi-step operations across platform components. Workflows persist at each step, enabling reliable completion despite process restarts, network failures, or partial execution errors. Blockchain operations present unique orchestration challenges. Transaction confirmation requires minutes rather than milliseconds. Gas prices fluctuate unpredictably. Nonce conflicts occur under concurrent load. The DALP Execution Engine addresses these realities through purpose-built workflow patterns. ## The challenge of blockchain orchestration [#the-challenge-of-blockchain-orchestration] Traditional request-response patterns fail for blockchain operations. A single asset issuance may require: 1. Verify investor eligibility 2. Deploy token contract 3. Wait for deployment confirmation 4. Configure compliance rules 5. Mint initial supply 6. Register with identity provider 7. Notify stakeholders Each step can fail independently. Steps 2-5 involve blockchain transactions that may take minutes. Naive implementations lose state if the process restarts between steps. ## Workflow patterns [#workflow-patterns] ### Persistent state machines [#persistent-state-machines] Every workflow maintains persistent state that survives process boundaries. State transitions record to storage before execution proceeds. Restart recovery reads last persisted state and continues from that checkpoint. ### Exactly-once semantics [#exactly-once-semantics] Workflow steps execute exactly once regardless of retries or restarts. Unique operation identifiers prevent duplicate blockchain transactions. Idempotency keys ensure side effects occur only on first execution. ### Virtual object pattern [#virtual-object-pattern] Long-running entities like transaction nonces maintain consistent state through virtual objects. Concurrent access serializes through the execution engine. No distributed locking or coordination protocols required. ## Workflow architecture [#workflow-architecture] ## Failure recovery [#failure-recovery] ### Automatic retry [#automatic-retry] Transient failures trigger automatic retry with exponential backoff. Retry policies configure per-operation based on expected failure patterns. Blockchain transaction retries increment gas prices to resolve stuck transactions. ### Compensating transactions [#compensating-transactions] Permanent failures trigger compensating workflows. If token minting fails after contract deployment, the engine initiates contract pause and investor notification workflows. Partial state never persists without explicit handling. ### Dead letter handling [#dead-letter-handling] Operations exhausting retry budgets route to dead letter queues. Operations teams receive alerts for manual intervention. Replay mechanisms enable resumption after root cause resolution. ## Observability [#observability] The engine provides complete visibility into workflow execution: **State inspection**: Current state and history for any workflow instance accessible through administrative interfaces. **Distributed tracing**: Correlation identifiers flow through all workflow steps. Trace spans capture timing and outcomes for performance analysis. **Progress dashboards**: Real-time views of active workflows by type and state. Bottleneck identification through queue depth monitoring. **Audit logs**: Every state transition records for compliance review. Retention policies ensure availability for regulatory examination periods. ## See also [#see-also] * [Transaction Signer](/docs/architecture/components/infrastructure/transaction-signer) for blockchain operations * [Key Guardian](/docs/architecture/components/infrastructure/key-guardian) for secure key storage * [Observability](/docs/architecture/operability/observability) for monitoring dashboards * [Unified API](/docs/architecture/components/platform/unified-api) for API entry point * [Asset Console](/docs/architecture/components/platform/asset-console) for UI entry point # Feeds system Source: https://docs.settlemint.com/docs/architecture/components/infrastructure/feeds-system The Feeds system provides reliable, system-guaranteed price and foreign exchange data for digital assets through a centralized directory with multiple feed types and subject scopes. The Feeds system is the registry and operating model for price and foreign exchange data in DALP. The directory separates feed discovery from feed delivery. Platform workflows, token contracts, APIs, and external integrations can resolve the current feed without hard-coding the underlying source. Related pages: * [Issuer-Signed Scalar Feed](/docs/architecture/components/capabilities/issuer-signed-scalar-feed) for issuer-attested price data * [Feeds update flow](/docs/architecture/flows/feeds-update-flow) for the feed value lifecycle * [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) for token contracts that consume feeds * [Execution Engine](/docs/architecture/components/infrastructure/execution-engine) for workflows that use feed data ## At a glance [#at-a-glance] * Feeds provide trusted price and FX data to the platform through a centralized directory * The FeedsDirectory separates **discovery** (which feed serves a given subject + topic) from **delivery** (individual feed contracts) * Two feed types: **issuer-signed scalar feeds** (factory-deployed capability) and **Chainlink aggregator adapters** (infrastructure wrapper) * Feeds can be global (economy-wide FX rates) or token-specific (asset base prices) * Only authorized roles can register, replace, or remove feeds *** ## What feeds are in DALP [#what-feeds-are-in-dalp] Feeds are on-chain data sources that supply trusted market information, including prices, exchange rates, and valuations, to platform consumers. Each feed is a contract that returns data in a pinned format. The FeedsDirectory maps a **(subject, topic)** pair to the feed contract address, so consumers never hard-code feed addresses. When a feed is registered, the directory captures: | Field | Description | | ----------------- | -------------------------------------------------------------------- | | **Subject** | Token address or global subject (address zero) for economy-wide data | | **Topic** | Data type the feed provides (base price, FX rate) | | **Feed contract** | Address applications query for data | | **Feed kind** | Scalar (numeric) or bytes (structured) | | **Schema hash** | Pins the expected data format for consistency | *** ## Where feeds are used [#where-feeds-are-used] | Consumer | Purpose | Failure impact | | --------------------------- | --------------------------------------------------------- | -------------------------------------------------- | | **Compliance modules** | Limit checks and valuation requirements | Transfer blocked if feed stale or missing | | **Yield / distribution** | Determine distribution amounts based on current prices | Distribution delayed or calculated on stale values | | **Asset Console** | Display current portfolio value in preferred currency | UI shows outdated valuations | | **Execution Engine** | Incorporate feed data into multi-step workflows | Workflow paused pending fresh data | | **External DeFi protocols** | Consume prices via Chainlink-compatible adapter interface | Integration returns stale round data | *** ## Feed types supported [#feed-types-supported] ### Issuer-signed scalar feed (capability) [#issuer-signed-scalar-feed-capability] A factory-deployed capability where the asset issuer cryptographically signs and publishes price data. Each feed instance is created through a factory contract, following the same pattern as other DALP capabilities (Airdrop, Vault, XvP Settlement, Token Sale). * **Deployment model:** factory pattern, one instance per asset or subject * **Data format:** fixed-point integer with configurable decimals * **Key properties:** history modes (latest-only, bounded, full), drift allowance, positive-value requirement, signature verification See [Issuer-Signed Scalar Feed](/docs/architecture/components/capabilities/issuer-signed-scalar-feed) for configuration, signing model, and value format details. ### Chainlink aggregator adapter (infrastructure) [#chainlink-aggregator-adapter-infrastructure] A wrapper contract that presents any DALP feed through the **AggregatorV3Interface**, the de facto standard consumed by DeFi protocols, lending platforms, and external analytics tools. **Why it exists:** External integrations need a stable address. Feed replacement does not change the adapter address. The adapter resolves the current feed from the FeedsDirectory on every call. Consumers do not need to update their pointers. | Property | Detail | | --------------------- | -------------------------------------------------------------------------------- | | **Interface** | Chainlink `AggregatorV3Interface` (`latestRoundData`, `decimals`, `description`) | | **Address stability** | Permanent, survives feed replacement in the directory | | **Resolution** | Dynamic, queries FeedsDirectory per call for current feed address | | **Configuration** | Subject + topic pair (same mapping as the directory) | | **Deployment** | One adapter per (subject, topic) combination | **Use cases:** DeFi protocol integration, cross-platform data sharing, portfolio valuation by external trackers, oracle aggregation, audit and compliance feeds for external systems. *** ## Trust model & boundaries [#trust-model--boundaries] | Action | Who can do it | Scope | | ------------------------------------ | ----------------------------------------------- | ----------------------------------- | | **Register / replace / remove feed** | Feeds Manager role (system-level) | Any subject, including global feeds | | **Create feed + adapter** | GOVERNANCE role holder on a specific DALP token | That token only (not global feeds) | | **Read feed data** | Any contract or off-chain caller | Unrestricted | * Feed registration is a privileged operation because unauthorized changes to pricing data could affect compliance decisions and valuations * Schema hash pinning ensures consumers always know the expected data format; format changes require explicit feed replacement * Global feeds (address zero subject) can only be managed by the Feeds Manager, not by individual token governance roles *** ## Operational model [#operational-model] ### Signals [#signals] * **Feed registered / replaced / removed**: directory events indexed by the chain indexer * **Value updated**: feed-level events (issuer-signed feeds emit on each signed update) * **Outlier flagged**: drift allowance exceeded on an issuer-signed feed ### Exchange rate refresh [#exchange-rate-refresh] DALP refreshes active FX feeds from the configured exchange rate provider on a recurring schedule. The refresh cycle groups active feeds by base currency, fetches one provider payload for each base, and submits validated rates in batches. Provider response keys are normalised to uppercase ISO currency codes before submission. The provider fetch uses a primary endpoint with a fallback mirror. The scheduler retries transient provider, network, timeout, and payload-shape failures with backoff. A base currency rejected by both endpoints as not found is treated as a permanent provider rejection for that cycle, so the scheduler logs the issue and waits for the next scheduled refresh instead of spending the retry budget on an unsupported base. The refresh cycle does not submit a new on-chain value when the provider timestamp for that base has not changed. Timestamps are tracked per base currency, so a failed EUR refresh does not block USD, and a successful USD refresh does not suppress a later EUR retry. Before submission, DALP drops missing quote currencies and rates that are zero, negative, non-finite, or outside the configured sanity bounds. If every feed for a base fails validation, the scheduler keeps that base eligible for the next refresh instead of advancing its stored timestamp. ### Failure modes [#failure-modes] | Failure | System behavior | | ------------------------------------------- | --------------------------------------------------------------------- | | Feed stale (no updates) | Consumers read last-known value; compliance may block | | Feed removed from directory | Discovery returns zero address; consumers must handle | | Invalid signature | Issuer-signed feed rejects the update on-chain | | Drift exceeded | Value flagged as outlier; consumers decide risk tolerance | | Adapter target missing | Adapter call reverts; external integrations see failure | | Exchange rate provider temporarily fails | Scheduler retries with backoff, then waits for the next refresh cycle | | Exchange rate base is unsupported | Scheduler skips that base for the cycle and logs the rejection | | Exchange rate quote missing or invalid | Scheduler skips the affected feed and retries on a later cycle | | Organisation signer or CLAIM purpose absent | Scheduler skips submission until the organisation signer is ready | See [Feeds update flow](/docs/architecture/flows/feeds-update-flow) for the full lifecycle including validation checkpoints and recovery. *** ## Dependencies [#dependencies] **On-chain:** * FeedsDirectory contract (central registry) * Factory contracts (deploy issuer-signed feed and adapter instances) * Token contracts (subjects for token-specific feeds) **Off-chain:** * Chain indexer (indexes directory and feed events for the platform UI and API) * Execution Engine (orchestrates workflows that consume feed data) *** ## See also [#see-also] * [Issuer-Signed Scalar Feed](/docs/architecture/components/capabilities/issuer-signed-scalar-feed) for configuration, signing model, and value format * [Feeds update flow](/docs/architecture/flows/feeds-update-flow) for the feed value update lifecycle * [Compliance modules](/docs/architecture/security/compliance) for how feeds support compliance checks # Overview Source: https://docs.settlemint.com/docs/architecture/components/infrastructure The infrastructure layer contains the backend services that orchestrate workflows, manage cryptographic keys, sign transactions, index blockchain events, and provide network connectivity. These services are not accessed directly by users -- they operate behind the platform interfaces. **Purpose:** Describe the infrastructure layer boundary -- the backend services that execute blockchain operations, manage keys, and maintain queryable state. * **Doc type:** Reference ## What you'll find here [#what-youll-find-here] * Role of the infrastructure layer in the architecture stack * Inventory of all eight infrastructure services with responsibilities * Links to each component's detailed architecture page ## Layer overview [#layer-overview] Infrastructure services sit between the platform interfaces and the blockchain networks. They handle the operational complexity of blockchain interaction: workflow orchestration with guaranteed delivery, cryptographic key management, transaction signing with nonce serialization, event indexing, and multi-network routing. These services are internal to the platform. External consumers interact only through the Asset Console or Unified API. Infrastructure services communicate with each other through the Execution Engine's workflow coordination, not through direct service-to-service calls. ## Components [#components] | Component | Responsibility | Details | | ------------------------------------------------------------------------------------- | ------------------------------------- | -------------------------------------------------------------------------------------- | | [Execution Engine](/docs/architecture/components/infrastructure/execution-engine) | Reliable workflow orchestration | Persistent state machines, exactly-once semantics, compensating transactions (Restate) | | [Key Guardian](/docs/architecture/components/infrastructure/key-guardian) | Secure cryptographic key storage | HSM integration, cloud KMS support (AWS/Azure/GCP), key rotation | | [Transaction Signer](/docs/architecture/components/infrastructure/transaction-signer) | Transaction preparation and signing | Gas estimation, nonce management, EIP-1559 support, meta-transactions | | [Contract Runtime](/docs/architecture/components/infrastructure/contract-runtime) | Smart contract interaction | ABI encoding/decoding, call routing, event parsing | | [Chain Indexer](/docs/architecture/components/infrastructure/chain-indexer) | Event processing and data translation | Blockchain event subscription, state projection, queryable read models | | [Chain Gateway](/docs/architecture/components/infrastructure/chain-gateway) | Multi-network connectivity | RPC routing, failover, load balancing across providers | | [EVM RPC Node](/docs/architecture/components/infrastructure/evm-rpc-node) | Blockchain network access | Transaction submission, state queries, block monitoring | | [Feeds System](/docs/architecture/components/infrastructure/feeds-system) | Trusted market data feeds | Price feeds, NAV calculations, reference data with configurable sources | Infrastructure monitoring with real-time API metrics ## Related [#related] * [Component catalog](/docs/architecture/components) for the full platform inventory * [Platform layer](/docs/architecture/components/platform) for the user-facing interfaces these services power * [Capabilities](/docs/architecture/components/capabilities) for operational tools built on this infrastructure * [Key flows](/docs/architecture/flows) for cross-service operation sequences * [Operability](/docs/architecture/operability) for observability and failure mode documentation # Key Guardian Source: https://docs.settlemint.com/docs/architecture/components/infrastructure/key-guardian The Key Guardian service manages cryptographic key material with defense-in-depth security, supporting encrypted database storage, secret managers, hardware security modules, and third-party custody providers including DFNS and Fireblocks. The Key Guardian protects private keys that control digital assets. It routes each signing request to the configured storage backend. Supported backends range from encrypted database storage and cloud secret managers to hardware security modules and third-party custody providers. Raw key material does not leave the selected secure boundary in plaintext. Key Guardian sits behind the [Transaction Signer](/docs/architecture/components/infrastructure/transaction-signer), which requests signatures for DALP workflows. For the full transaction path from policy checks to custody signing and broadcast, see [Signing Flow](/docs/architecture/flows/signing-flow). ## Storage hierarchy [#storage-hierarchy] | Storage tier | Protection level | Use case | | ------------------------ | ------------------------------- | ------------------------------- | | Encrypted database | Application-level encryption | Development, low-value assets | | Cloud secret manager | Platform-managed encryption | Standard production deployments | | Hardware security module | FIPS 140-2 Level 3 | Regulated financial services | | Third-party custody | Delegated institutional custody | Highest security requirements | ## Security architecture [#security-architecture] The Guardian API receives signature requests without exposing raw key material. Storage routers direct requests to appropriate backends based on key metadata. ## Key lifecycle [#key-lifecycle] * **Generation**: HSM-backed keys generate entirely within hardware. Software keys use cryptographically secure random sources with immediate encryption before memory clearing. * **Rotation**: Replaces active signing keys while maintaining historical keys for verification. Coordinates with blockchain address updates and registry notifications. * **Recovery**: Enterprise deployments use sharded backups with threshold signature schemes requiring multiple custodians. * **Revocation**: Compromised keys are immediately removed from active use. Smart contract permissions update to reject signatures from revoked keys. ## DFNS integration [#dfns-integration] DFNS provides delegated MPC custody as a pluggable backend. * **Policy enforcement**: DFNS policy approvals remain provider-owned. See [DFNS policy enforcement](/docs/architecture/integrations/custody-providers#policy-enforcement) for the canonical status vocabulary, error handling, and approval boundary, and [Signing Flow](/docs/architecture/flows/signing-flow) for interaction details. * **Multi-party approval boundary**: DFNS policy approvals are separate from DALP smart-wallet multisig approvals, which use a UserOperation approval flow instead of a custody-policy decision flow. * **Audit integration**: DFNS audit logs synchronize with DALP records for unified compliance reporting. ## Fireblocks integration [#fireblocks-integration] Fireblocks provides MPC custody through vault accounts. Switching between DFNS and Fireblocks keeps DALP signing requests consistent, while provider-specific approval and wallet behavior remains behind the signer adapter. * **Vault model**: Keys organized into vault accounts, each containing one or more asset wallets. DALP supports creating, listing, and managing vaults. * **Transaction signing**: All signing happens through Fireblocks MPC -- no single private key ever exists. Supports message signing, transaction signing with fee estimation, and typed data signing. * **Transaction Authorization Policy (TAP)**: Enforces amount thresholds, whitelisted destinations, velocity limits, and multi-approver requirements. * **Approval limitation**: Fireblocks approval remains provider-owned and is resolved through the Fireblocks Console or a Co-Signer appliance. See [Fireblocks TAP behavior](/docs/architecture/integrations/custody-providers#transaction-authorization-policy-tap) for the canonical polling and approval flow. ## Comparing custody providers [#comparing-custody-providers] | Capability | DFNS | Fireblocks | | ----------------------------- | -------------------------- | --------------------------------------------- | | MPC signing | Distributed key shards | Distributed key shards | | Provider policy decision flow | Provider-owned status flow | Console/Co-Signer only | | Policy engine | DFNS policy rules | Transaction Authorization Policy (TAP) | | Wallet model | Flat wallet list | Vault → asset hierarchy | | DALP signing use | Configured EVM wallets | Fireblocks vault wallets for EVM transactions | ## Access control and audit [#access-control-and-audit] * Only Transaction Signer components can request signatures * Signature requests require valid workflow context * Rate limiting prevents bulk signature extraction * All access attempts log for security review | Operation | Logged attributes | | ----------------- | ---------------------------------------------------------------------- | | Key generation | Key identifier, algorithm, storage tier, generator identity | | Signature request | Key identifier, message hash, requester identity, workflow correlation | | Rotation | Old key identifier, new key identifier, initiator, approval chain | | Access denial | Key identifier, requester, denial reason | ## See also [#see-also] * [Transaction Signer](/docs/architecture/components/infrastructure/transaction-signer) for signature operations * [Signing Flow](/docs/architecture/flows/signing-flow) for the end-to-end transaction signing sequence including DFNS and Fireblocks paths # Transaction Signer Source: https://docs.settlemint.com/docs/architecture/components/infrastructure/transaction-signer The Transaction Signer service handles secure preparation, signing, and broadcasting of blockchain transactions with gas management, nonce coordination, account abstraction support, and configurable custody signers including local keys, DFNS, Fireblocks, and Luna HSM. ## Overview [#overview] The Transaction Signer prepares, signs, and broadcasts blockchain transactions. The signer manages gas estimation, nonce coordination, transaction lifecycle monitoring, and custody signer selection without exposing key material. Blockchain transactions require careful coordination. Nonce conflicts cause failures. Insufficient gas results in stuck transactions. The Transaction Signer handles these concerns through purpose-built mechanisms that ensure reliable transaction delivery. ## Transaction lifecycle [#transaction-lifecycle] ## Gas management [#gas-management] ### Estimation [#estimation] Gas estimation queries the target chain with actual transaction parameters. The signer applies configurable buffers to estimates, preventing failures from estimation variance. ### Price strategy [#price-strategy] Gas price selection balances confirmation speed against cost. Configurable strategies support: | Strategy | Behavior | Use case | | -------- | ------------------------------------ | ------------------------------- | | Fast | Priority fee targets quick inclusion | Time-sensitive operations | | Standard | Base fee plus moderate priority | Normal operations | | Economy | Minimum viable fee | Cost-sensitive batch operations | ### Stuck transaction resolution [#stuck-transaction-resolution] Transactions pending beyond threshold durations trigger resolution workflows. The signer resubmits with increased gas prices while maintaining nonce consistency. Replacement transactions explicitly cancel stuck originals when appropriate. ## Nonce coordination [#nonce-coordination] Concurrent transaction submission requires careful nonce management. The signer implements virtual object patterns for nonce state: **Sequential assignment**: Each signing address maintains ordered nonce assignment. Concurrent requests serialize through the DALP Execution Engine. **Gap prevention**: Transaction failures trigger nonce reclamation. Subsequent transactions receive recycled nonces to prevent gaps that block future transactions. **Recovery handling**: System restarts query chain state to reconcile nonce records with confirmed transactions. ## Account abstraction [#account-abstraction] The Transaction Signer supports ERC-4337 account abstraction for enhanced transaction patterns: **User operations**: Transactions submit through bundler infrastructure rather than direct RPC calls. **Paymaster integration**: Gas fees pay from designated accounts rather than transaction signers. **Batched execution**: Multiple operations execute in single transactions for gas efficiency. **Signature aggregation**: Compatible wallets aggregate signatures for further gas reduction. ## Signing modes [#signing-modes] DALP uses one signer interface across local keys, custody providers, and HSM-backed signing. The configured provider determines where private key operations happen and whether an approval step is required before a signature is returned. | Mode | Mechanism | Approval behaviour | Typical use case | | ---------- | ------------------------------------------------ | ------------------------------------------------------------------------------ | ----------------------------------------------- | | Local | Key Guardian signature | No external approval workflow | Development or controlled operator environments | | DFNS | DFNS custody signing and policy checks | Provider policy can auto-approve or pause for approval | MPC custody with provider-managed controls | | Fireblocks | Fireblocks custody signing and TAP policy checks | Provider policy can auto-approve or pause for approval | MPC custody with Fireblocks policy controls | | Luna HSM | Thales Luna 7 HSM partition signing | HSM quorum can return a pending approval state until quorum approval completes | Hardware-backed custody signing | ## Failure handling [#failure-handling] ### Transient failures [#transient-failures] Network timeouts and RPC errors trigger automatic retry. Exponential backoff prevents overwhelming recovering services. Retry attempts use increased gas prices to resolve potential underpricing. ### Permanent failures [#permanent-failures] Reverted transactions surface to workflows for business logic handling. Compensating transactions initiate where appropriate. Failed operations generate alerts for operations review. ### Reorg handling [#reorg-handling] Block reorgs can reverse confirmed transactions. The signer monitors confirmation depth before marking transactions final. Reorged transactions resubmit automatically with appropriate nonce handling. ## Monitoring [#monitoring] Transaction status exposes through multiple channels: * Real-time status updates via WebSocket connections * Workflow state queries through administrative API * Aggregate metrics in observability dashboards * Alert triggers for failed or stuck transactions ## See also [#see-also] * [Signing Flow](/docs/architecture/flows/signing-flow) for the end-to-end transaction signing sequence (compliance → unified signer → DFNS/Fireblocks → broadcast) * [Key Guardian](/docs/architecture/components/infrastructure/key-guardian) for key storage * [Contract Runtime](/docs/architecture/components/infrastructure/contract-runtime) for transaction construction * [Chain Gateway](/docs/architecture/components/infrastructure/chain-gateway) for network access * Account abstraction (ERC-4337) for gasless transactions and batched operations # Asset Console Source: https://docs.settlemint.com/docs/architecture/components/platform/asset-console The Asset Console provides a comprehensive web interface for digital asset lifecycle management. Built with enterprise authentication, theme customization for Asset Console branding, and multi-language support, the console adapts to institutional requirements while maintaining consistent security standards. ## Overview [#overview] The Asset Console is the primary interface for platform operators managing digital assets. The console provides role-based workflows that guide users through complex operations while maintaining complete audit trails. Every action requiring blockchain interaction passes through wallet verification to prevent unauthorized transactions. Institutional asset management demands interfaces purpose-built for compliance and operational rigor. The Asset Console addresses these requirements through structured workflows, mandatory verification gates, comprehensive activity logging, and extensive customization options for enterprise deployments. The console communicates through the Unified API. This architecture ensures consistent behavior between console operations and programmatic API access. The same validation, permissions, and audit logging apply regardless of access method. ## Core capabilities [#core-capabilities] Asset Designer 7-step wizard ### Asset creation and configuration [#asset-creation-and-configuration] The console guides issuers through asset tokenization with structured forms and validation. Required fields ensure regulatory compliance while optional parameters accommodate jurisdiction-specific requirements. Asset preview displays exact on-chain representation before deployment. Centralized asset management view ### Portfolio management [#portfolio-management] Investors access consolidated portfolio views showing holdings across asset types. The interface reflects confirmed blockchain state, and historical transaction records provide complete audit trails for tax and compliance reporting. **Holdings overview**: Real-time valuations aggregate positions across all asset types—bonds, equities, deposits, stablecoins—into a unified view. Performance metrics track returns over configurable periods. **Transaction history**: Complete transaction logs with filtering, export capabilities, and categorization for tax reporting. Each transaction links to its blockchain record for verification. **Compliance status**: Investors see their verification status, claim expirations, and any pending requirements. Proactive notifications alert users before claims expire to prevent transfer restrictions. ### Compliance workflows [#compliance-workflows] Compliance officers review investor verification submissions through dedicated queues. Risk scoring surfaces priority reviews while bulk actions reduce repetitive work in high-volume operations. All decisions record timestamps, reviewers, and rationale for regulatory defense. Post-deployment asset analytics ### Distribution management [#distribution-management] The console manages dividend payments, interest distributions, and yield allocations. Calculation previews display per-investor amounts before execution. Post-distribution reports confirm delivery and flag any failed transfers for remediation. ## Customization [#customization] The Asset Console supports comprehensive customization for enterprise deployments, covering both visual branding and language localization. ### White-labeling [#white-labeling] Enterprise deployments apply consistent branding to the Asset Console through the theme system. Customizable elements include logo variants, authentication-screen imagery, background images, favicons, Apple touch images, color tokens, and typography. Theme preview diffs and full theme configurations use these public payload keys: | Key | Controls | | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | `logo` | Primary, compact, and authentication logo URLs such as `lightUrl`, `darkUrl`, `lightIconUrl`, `darkIconUrl`, `authLightUrl`, and `authDarkUrl` | | `images` | Authentication overlays, background images, favicons, Apple touch images, and favicon variants | | `fonts` | `sans` and `mono` font family, source, weight, preload, and custom URL settings | | `cssVars.light` / `cssVars.dark` | Light-mode and dark-mode color tokens | | `metadata` | Full theme updates include version and update metadata for optimistic concurrency | Administrators can preview a diff with `baseVersion` before applying the reviewed theme change. ### Public branding assets [#public-branding-assets] Uploaded branding assets are intended for direct rendering in the user interface. Logo, authentication-screen, background, favicon, and Apple touch assets can be served through public `/dalp/...` asset URLs so browsers can load the active theme without an API call. Do not use public branding asset URLs for investor files, token documents, KYC evidence, or operational attachments. Those files stay behind the relevant document workflows and should be retrieved through the KYC or token document download APIs when an authorized user needs access. ### Internationalization [#internationalization] The console provides full multi-language support with type-safe translations across all interface elements. Supported languages include English (en-US), German (de-DE), Japanese (ja-JP), and Arabic (ar-SA) with full right-to-left layout support. Language selection can be configured through user preference, browser detection, or deployment-wide defaults via Helm values. ## Accessibility [#accessibility] The Asset Console implements WCAG 2.1 Level AA accessibility standards: * Full keyboard navigation for all workflows * Screen reader optimization with semantic markup * Color contrast ratios meeting accessibility guidelines * Focus indicators for interactive elements * Skip navigation links for efficient browsing ## See also [#see-also] * [Authentication and security](/docs/architecture/security/authentication) for identity management * [Roles and tenancy](/docs/architecture/security/authorization) for access control and multi-tenant configuration * [Unified API](/docs/architecture/components/platform/unified-api) for programmatic access * [DALP Execution Engine](/docs/architecture/components/infrastructure/execution-engine) for operation processing # Overview Source: https://docs.settlemint.com/docs/architecture/components/platform The platform layer provides the two primary interfaces to DALP: the Asset Console web application and the Unified API. Both share identical business logic, permissions, and audit logging through the same backend services. **Purpose:** Describe the platform layer boundary -- the user-facing interfaces that operators and integrators use to access DALP capabilities. * **Doc type:** Reference ## What you'll find here [#what-youll-find-here] * Role of the platform layer in the overall architecture * Boundary between platform interfaces and backend infrastructure * Links to detailed pages for each platform component ## Layer overview [#layer-overview] The platform layer sits at the top of the DALP architecture stack. It translates user intent into structured requests that flow through the Unified API to the Execution Engine. Neither component contains business logic -- they delegate all operations to infrastructure services and the SMART Protocol. Both interfaces enforce the same authentication, authorization, and audit controls. An action performed through the Asset Console produces identical backend behavior as the equivalent API call. This consistency simplifies compliance certification and security review. ## Components [#components] | Component | Responsibility | Details | | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | | [Asset Console](/docs/architecture/components/platform/asset-console) | White-label web interface for asset lifecycle management, compliance workflows, portfolio views, and distribution management | Role-based UI with WCAG 2.1 AA accessibility | | [Unified API](/docs/architecture/components/platform/unified-api) | OpenAPI 3.1 documented programmatic access to all platform operations | Type-safe procedures organized by domain namespace | ## Related [#related] * [Component catalog](/docs/architecture/components) for the full platform inventory * [Infrastructure layer](/docs/architecture/components/infrastructure) for the services behind these interfaces * [Security](/docs/architecture/security) for authentication and authorization architecture # Unified API Source: https://docs.settlemint.com/docs/architecture/components/platform/unified-api The Unified API provides type-safe programmatic access to all digital asset lifecycle operations. With automatic OpenAPI generation, the API combines type safety, comprehensive documentation, and multi-layer security including wallet verification for blockchain transactions. ## Overview [#overview] The Unified API exposes all DALP platform capabilities through a type-safe, documented interface. Integration engineers connect existing systems to asset lifecycle operations without understanding blockchain implementation details. Every procedure validates inputs, enforces permissions, and produces consistent responses. Enterprise integrations demand predictable interfaces with comprehensive documentation. The Unified API delivers OpenAPI 3.1 specifications generated directly from procedure definitions, ensuring documentation stays synchronized with implementation. Interactive exploration through Swagger UI at `/api` enables rapid integration development. ## Procedure namespaces [#procedure-namespaces] Procedures organize by domain rather than REST resource conventions. This organization reflects how operators think about platform capabilities. | Namespace | Purpose | Example procedures | | --------- | -------------------------- | -------------------------------------------------- | | `token` | Asset lifecycle operations | Create, mint, burn, transfer, pause | | `user` | User management | List users, assign roles, manage permissions | | `account` | Wallet operations | Generate address, check balance, sign transactions | | `contact` | Investor relationships | Register investor, record verifications | | `asset` | Asset metadata | Update documents, configure compliance rules | | `system` | Platform administration | Health checks, configuration, audit logs | ## API design principles [#api-design-principles] The API follows consistent design patterns across all procedures: **Contract-first development**: Schema definitions validate all inputs before business logic executes. Changes to schemas immediately surface as errors in consuming code. **Automatic documentation**: OpenAPI 3.1 specifications generate from procedure definitions. No manual synchronization required between implementation and documentation. **Consistent serialization**: Complex types like large numbers, dates, and blockchain-specific values serialize correctly across the wire. **Middleware composition**: Cross-cutting concerns like authentication, rate limiting, and audit logging apply consistently across procedure groups. REST API documentation for programmatic access to platform capabilities ## Interactive documentation [#interactive-documentation] Swagger UI provides interactive API exploration at `/api`. Developers authenticate, construct requests, and execute procedures directly from documentation. Response schemas display alongside actual responses for validation. The OpenAPI specification exports for client generation in any language. Generated clients benefit from type safety matching the server implementation. ## Meta-transaction support [#meta-transaction-support] The API supports meta-transactions through the underlying ERC-2771 integration. Callers can submit signed transaction payloads without holding native tokens for gas. A configured relayer service sponsors transaction costs while the user's signature authorizes the operation. This capability enables: * **Simplified user experience** - Investors interact with tokens without managing cryptocurrency for fees * **Sponsored operations** - Issuers cover transaction costs for their investors * **Gasless workflows** - Automated systems execute transactions without native token management Meta-transactions work transparently through the API—callers submit signed payloads, and the platform handles relay coordination. ## Enterprise messaging integration [#enterprise-messaging-integration] The API supports integration with enterprise financial messaging systems for institutions requiring connectivity to existing infrastructure: * **Standards-based messaging** - Structured message formats for corporate actions, settlement instructions, and asset servicing * **Automated reconciliation** - Outbound notifications for completed transactions enable downstream system updates * **Audit trail synchronization** - Transaction events can trigger messages to compliance and reporting systems These integrations connect blockchain-native operations to traditional financial infrastructure, enabling hybrid workflows where DALP handles tokenization while existing systems manage related processes. ## Error handling [#error-handling] Errors return structured responses with consistent formats across all procedures. Error responses include machine-readable codes alongside human-readable messages. | Category | HTTP status | Action | | ----------------------- | ----------- | ------------------------------ | | Validation errors | 400 | Fix request payload | | Authentication failures | 401 | Reauthenticate | | Authorization denied | 403 | Check role permissions | | Resource not found | 404 | Verify identifier | | Rate limited | 429 | Retry after delay | | Server errors | 500 | Retry with exponential backoff | Blockchain-specific errors include transaction details: gas estimation failures, revert reasons, and nonce conflicts surface as structured error responses. ## See also [#see-also] * [Authentication](/docs/architecture/security/authentication) for identity and API key management * [Security](/docs/architecture/security/wallet-verification) for wallet verification and rate limiting * [Asset Console](/docs/architecture/components/platform/asset-console) for web interface * [DALP Execution Engine](/docs/architecture/components/infrastructure/execution-engine) for operation coordination * [API integration guide](/docs/developer-guides/api-integration/getting-started) for implementation details # AUM Fee Source: https://docs.settlemint.com/docs/architecture/components/token-features/aum-fee Time-based management fee on DALPAsset. Accrues over time based on total supply and is collected by minting new tokens to the fee recipient as an inflationary mechanism. AUM Fee charges a time-based management fee as a percentage of Assets Under Management. Collection mints new tokens to the configured recipient, so the fee is paid through dilution rather than a treasury transfer. Related pages: * [Token Features Catalog](/docs/architecture/components/token-features) * [Asset Contracts](/docs/architecture/components/asset-contracts) * [Treasury Distribution](/docs/architecture/flows/treasury-distribution) *** ## Interface (capabilities) [#interface-capabilities] This feature exposes the following capabilities. Fee collection is inflationary: it mints new tokens to the configured recipient rather than transferring existing tokens. | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | ------------------- | ----------------- | --------------------------------------- | ----------------------------- | --------------------- | -------------------------------------------- | | Collect accrued fee | Anyone | None (uses elapsed time + total supply) | Mints tokens to fee recipient | `AUMFeeCollected` | Permissionless trigger; accrues continuously | | Set fee rate | `GOVERNANCE_ROLE` | New rate (basis points) | Updates annual fee rate | `FeeRateUpdated` | Blocked after freeze | | Set fee recipient | `GOVERNANCE_ROLE` | Recipient address | Redirects future collections | `FeeRecipientUpdated` | Effective immediately | | Freeze fee rate | `GOVERNANCE_ROLE` | None | Permanently locks rate | `FeeRateFrozen` | Irreversible | Accrued fees, current rate, recipient, and freeze status are available as read-only queries. *** ## Accrued estimate read [#accrued-estimate-read] `GET /api/v2/tokens/{tokenAddress}/aum-fee/accrued-estimate` reads the token's attached AUM Fee feature and returns the current accrued fee estimate. DALP reads `getAccruedFees()` and `getLastCollectionTime()` together, then returns the estimate in token units with the annual rate, last collection time, measurement time, and feature contract address. The response uses the standard single-resource envelope. `data` is `null` when the token has no attached AUM Fee feature or the attached feature has not been initialised. If the chain read is unavailable, treat the estimate as temporarily unavailable rather than as zero accrued fees. Use this read when an integration needs the same accrued-fee anchor shown in the token workspace before collecting fees. *** ## Token workspace surface [#token-workspace-surface] Tokens with the AUM Fee feature show an AUM Fee tile in the asset detail workspace. The tile summarizes the annual rate, fee recipient, last collection time, accrued estimate, total collected amount, and whether the rate is frozen. Use **View AUM fee details** to open the token's `/aum-fee` detail page. The detail page separates: * **Configuration:** annual rate, fee recipient, frozen state, and last collected time. * **Collection stats:** estimated accrued fees and total collected fees. If the token does not have the AUM Fee feature attached, the AUM Fee detail page shows an empty state instead of management controls. Users with the matching token permissions see a **Manage AUM fee** menu on the detail page. Depending on their permissions, the menu can include actions to collect accrued fees, set the annual rate, set the fee recipient, and freeze the rate. Users without any of those permissions do not see the menu. *** ## Business impact [#business-impact] * **Holders:** Fee accrual is inflationary: new tokens are minted, diluting existing holder percentage ownership. NAV per token decreases proportionally over time. * **Issuer / recipient:** Fee collected as newly minted tokens to configured `feeRecipient`. No treasury drawdown required. * **Economics:** Fee accrues continuously based on time elapsed and current total supply. Collection is triggered on demand (not automatic). *** ## Risks & abuse cases [#risks--abuse-cases] * **Uncapped dilution:** If `freezeFeeRate()` is not called after launch, GOVERNANCE\_ROLE can raise the fee rate at any time, retroactively increasing the accrued fee on next collection. * **Delayed collection:** Accrued fees do not execute until someone triggers collection. Large time gaps between collections produce large single-mint events, which may alarm holders or analytics systems. * **Fee recipient manipulation:** `setFeeRecipient()` can redirect fee flow to any address. No time-lock by default. *** ## Controls & guardrails [#controls--guardrails] | Role | Actions | Recommended guardrail | | ----------------- | ----------------------------------------------- | ------------------------------------------------------------------------ | | `GOVERNANCE_ROLE` | `setFeeBps()`: set fee rate in basis points | Call `freezeFeeRate()` immediately after launch to lock rate permanently | | `GOVERNANCE_ROLE` | `setFeeRecipient()`: set collection destination | Multi-sig controlled; audit recipient changes | | `GOVERNANCE_ROLE` | `freezeFeeRate()`: permanently lock fee rate | **Call at launch**: prevents future rate changes | *** ## Failure modes & edge cases [#failure-modes--edge-cases] * **Zero supply:** If total supply is zero, no fee accrues regardless of time. Collection produces a zero-mint no-op. * **Rate frozen at zero:** If `freezeFeeRate()` is called with `feeBps = 0`, the feature remains active but never collects. Disable it entirely instead. * **Reentrant collection:** If collection is called during a lifecycle hook chain, downstream hooks see the inflated supply in the same transaction. Order analytics features after AUM Fee. *** ## Auditability & operational signals [#auditability--operational-signals] * `AUMFeeCollected(collector, recipient, feeAmount, timestamp)`: emitted on each collection. Monitor for anomalous mint sizes. * `FeeRateUpdated(sender, oldFeeBps, newFeeBps)`: emitted on rate changes. Alert on any post-launch rate change. * `FeeRateFrozen(sender)`: emitted once on freeze. Absence of this event after launch is a configuration risk. *** ## Dependencies [#dependencies] * **No external ERC-20 dependency**: fee paid in the token itself (minting). * No treasury contract required. * No other features required, but analytics features (Historical Balances) should run after AUM Fee in the hook order. *** ## Compatibility & ordering notes [#compatibility--ordering-notes] * Run AUM Fee before Historical Balances and Voting Power in the feature array: analytics hooks must see post-fee supply. * No conflict with compliance modules (AUM Fee does not go through transfer compliance checks: it mints directly). * Compatible with Maturity Redemption: AUM Fee stops having economic impact once transfers are blocked post-maturity, but it continues accruing until collection or feature removal. *** ## Change impact [#change-impact] * **Enable after launch:** Retroactive accrual does not occur: accrual starts from the moment the feature is activated. * **Disable:** Accrued-but-uncollected fees are lost. Collect before removing the feature. * **Rate change:** Takes effect immediately on next collection calculation. Freeze rate to prevent future changes. * **Recipient change:** Effective immediately for all future collections. *** ## See also [#see-also] * [Token Features Catalog](/docs/architecture/components/token-features): return to the full feature catalog * [Asset Contracts](/docs/architecture/components/asset-contracts): deployment architecture and role model * [Treasury Distribution](/docs/architecture/flows/treasury-distribution): yield and fee distribution flows # Conversion Source: https://docs.settlemint.com/docs/architecture/components/token-features/conversion Convertible instrument pair for DALPAsset — Conversion (loan-side) and Conversion Minter (equity-side). Manages trigger types, executes holder-initiated and forced conversions, and coordinates cross-token burns and mints. **Purpose:** The Conversion feature pair implements convertible instruments. Conversion (Loan-Side) attaches to the convertible loan token and manages triggers and execution. Conversion Minter (Equity-Side) attaches to the equity token and mints equity when authorized by the loan-side conversion. * **Doc type:** Reference * **What you'll find here:** Business impact, risks, deployment sequence, controls, failure modes, auditability, dependencies, and ordering notes * **Related:** * [Token Features Catalog](/docs/architecture/components/token-features) * [Asset Contracts](/docs/architecture/components/asset-contracts) * [Compliance Modules](/docs/architecture/security/compliance) *** ## Interface (capabilities) [#interface-capabilities] This feature exposes capabilities across a cooperative two-contract pair. The Conversion feature (loan-side) manages triggers and executes conversions. The Conversion Minter (equity-side) mints target tokens when authorized. **Conversion (Loan-Side):** | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | -------------------------- | ----------------- | ----------------------------------- | ------------------------------------------------ | ---------------------------------------------------------------- | ------------------------------------ | | Publish conversion trigger | `GOVERNANCE_ROLE` | Trigger ID, price, expiry, metadata | Creates trigger definition | `TriggerPublished` | Denomination must match config | | Disable trigger | `GOVERNANCE_ROLE` | Trigger ID | Deactivates trigger | `TriggerDisabled` | Existing conversions unaffected | | Convert tokens | Token holder | Principal amount + trigger ID | Burns loan tokens; requests equity mint | `ConversionInitiated`, `ConversionFinalized` | Requires valid trigger + open window | | Force convert | `CUSTODIAN_ROLE` | Holder address, amount, trigger ID | Burns holder's loan tokens; requests equity mint | `ForcedConversion`, `ConversionInitiated`, `ConversionFinalized` | Mandatory convertibles | | Set conversion window | `GOVERNANCE_ROLE` | Start + end timestamps | Updates when conversions are allowed | `ConversionWindowUpdated` | — | **Conversion Minter (Equity-Side):** | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | -------------------- | -------------------- | -------------------------------- | -------------------------------- | ---------------------------- | --------------------------------- | | Authorize converter | `GOVERNANCE_ROLE` | Converter contract address | Adds to minter allowlist | `ConverterAuthorized` | Required during deployment | | Remove converter | `GOVERNANCE_ROLE` | Converter contract address | Removes from minter allowlist | `ConverterDeauthorized` | Audit allowlist post-deployment | | Mint from conversion | Authorized converter | Recipient, amount, conversion ID | Mints equity tokens to recipient | `TargetIssuedFromConversion` | Replay-protected by conversion ID | Trigger details, conversion records, available principal, and authorization status are available as read-only queries on their respective contracts. *** ## Business impact [#business-impact] * **Holders (loan token):** Can convert their loan token balance to equity tokens when a valid trigger condition is met. Token balance is the source of truth for convertible principal — no separate position registration. * **Holders (equity):** Equity supply increases as loan tokens are converted. Existing equity holders are diluted proportionally. * **Issuer:** Sets conversion triggers (price thresholds, discount rates, conversion caps, time windows). Can force conversion for mandatory convertibles via `CUSTODIAN_ROLE`. * **Economics:** Loan tokens are burned on conversion; equity tokens are minted by the Conversion Minter. Two-contract cooperative pair — both must be configured correctly. *** ## Risks & abuse cases [#risks--abuse-cases] * **Deployment sequence error:** Incorrect deployment order (e.g., deploying the loan token before authorizing the Conversion Minter) will leave the conversion feature unable to mint equity. The entire conversion mechanism fails silently or reverts. * **Conversion Minter allowlist not locked:** If `addAuthorizedConverter()` is called but the allowlist is not reviewed post-deployment, unauthorized addresses added later could trigger unauthorized equity mints. * **`forceConvert()` misuse:** CUSTODIAN\_ROLE can force conversion for mandatory convertibles. Without documented governance controls, this action can be executed without holder consent. * **Trigger front-running:** Price-based triggers observable on-chain can be front-run. Conversion window triggers with defined open/close dates are more predictable. *** ## Deployment sequence (critical) [#deployment-sequence-critical] Incorrect deployment order will break the authorization chain. Follow this exact sequence: 1. Deploy equity token (target) with Conversion Minter feature attached 2. Deploy loan token (source) with Conversion feature attached, configured with equity token address 3. Call `addAuthorizedConverter()` on the Conversion Minter, authorizing the loan-side Conversion feature contract address This sequence ensures the equity token is deployed before the loan token references it, and the Conversion Minter's allowlist is populated before any conversion can be triggered. *** ## Controls & guardrails [#controls--guardrails] | Contract | Role | Actions | Recommended guardrail | | -------------------------- | ----------------- | --------------------------------------------------------------- | ---------------------------------------------------------------- | | Conversion (Loan) | `GOVERNANCE_ROLE` | `publishTrigger()`, `disableTrigger()`, `setConversionWindow()` | Review all trigger parameters before publishing; test on testnet | | Conversion (Loan) | `CUSTODIAN_ROLE` | `forceConvert()` — mandatory conversion at maturity | Document governance process; require multi-sig approval | | Conversion Minter (Equity) | `GOVERNANCE_ROLE` | `addAuthorizedConverter()`, `removeAuthorizedConverter()` | Audit allowlist post-deployment; remove test addresses | *** ## Failure modes & edge cases [#failure-modes--edge-cases] * **Trigger not met:** Conversion attempts when no trigger condition is active revert. Holders must wait for a valid trigger window. * **Conversion Minter not authorized:** If the loan-side Conversion feature is not on the Conversion Minter's allowlist, all conversion attempts revert. Verify authorization as part of deployment verification. * **Equity supply cap:** If the equity token has a supply cap (via compliance module), conversions that would exceed the cap revert. Ensure the cap accounts for the full potential loan conversion volume. * **Replay protection:** Each conversion event carries a replay protection nonce. The same conversion request cannot be submitted twice. *** ## Auditability & operational signals [#auditability--operational-signals] **Conversion (Loan-Side):** * `ConversionInitiated(conversionId, holder, triggerId, loanAmount)` / `ConversionFinalized(conversionId, holder, equityAmount)` — emitted per successful conversion. * `ForcedConversion(holder, loanAmount, triggeredBy)` — emitted when CUSTODIAN\_ROLE forces conversion. * `TriggerPublished(triggerId, type, parameters)` / `TriggerDisabled(triggerId)` — emitted on trigger lifecycle changes. **Conversion Minter (Equity-Side):** * `ConverterAuthorized(converterAddress)` / `ConverterDeauthorized(converterAddress)` — emitted on allowlist changes. * `TargetIssuedFromConversion(conversionId, recipient, amount, sourceLoanToken, triggerId)` — emitted per conversion mint. Primary provenance signal. * `ConversionWindowUpdated(start, end)` — emitted when the conversion window is changed. Alert on mid-lifecycle window changes. *** ## Dependencies [#dependencies] * **Two DALPAsset tokens** — one for the loan instrument, one for the equity instrument. Both must be deployed before configuration. * **Deployment coordination** — the three-step deployment sequence (above) is a hard dependency. * No external ERC-20 required for the conversion mechanism itself. *** ## Compatibility & ordering notes [#compatibility--ordering-notes] **Conversion (Loan-Side):** * `supportsRewriting = false` — uses `canUpdate()` to restrict transfer of already-converted loan tokens (MarkConverted debt method). The four lifecycle hooks (`onMinted`, `onBurned`, `onTransferred`, `onRedeemed`) are no-ops for this feature. * **Order first** in the loan token's feature array (transfer restriction behavior). **Conversion Minter (Equity-Side):** * No hooks — order in the equity token's feature array is irrelevant. Compatible with compliance modules on both tokens — compliance runs before features; a holder must pass compliance to receive converted equity tokens. *** ## Change impact [#change-impact] * **Enable at deployment:** Both features must be configured at deployment. Adding them post-deployment requires the full three-step authorization sequence. * **Disable Conversion (Loan):** Pending conversions in-flight may be affected. Existing trigger definitions become inactive. Loan tokens are no longer convertible. * **Disable Conversion Minter (Equity):** Equity can no longer be minted for conversions. All in-flight and future conversion attempts on the loan side will revert. * **Remove authorized converter:** Immediately prevents future conversion-triggered mints. Any in-progress conversions that have not yet minted equity will revert. *** ## See also [#see-also] * [Token Features Catalog](/docs/architecture/components/token-features) — return to the full feature catalog * [Asset Contracts](/docs/architecture/components/asset-contracts) — deployment architecture and role model * [Compliance Modules](/docs/architecture/security/compliance) — compliance checks on conversion recipients # External Transaction Fee Source: https://docs.settlemint.com/docs/architecture/components/token-features/external-transaction-fee Fixed fee in a separate ERC-20 token, such as USDC, charged on every mint, burn, or transfer. Fee amounts use the fee token's units and allowance. External Transaction Fee charges a fixed fee in a separate ERC-20 token, such as USDC, on every mint, burn, or transfer. The transferred token amount is not reduced; the payer must also hold and approve enough of the separate fee token. ## Interface [#interface] Fees are denominated in the configured fee token and collected from the payer's allowance. | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | --------------------------------- | ----------------- | -------------------------------------------------------------------- | ------------------------------------------- | ---------------------- | ------------------------------------ | | Collect external fee on operation | Automatic (hook) | Triggered on mint, burn, transfer | Transfers fee token from payer to recipient | `ExternalFeeCollected` | Payer must have approved fee token | | Set fee amounts | `GOVERNANCE_ROLE` | Fixed mint, burn, and transfer fees in the fee token's smallest unit | Updates fee amounts | `FeesUpdated` | Blocked after freeze | | Set fee token | `GOVERNANCE_ROLE` | ERC-20 token address | Changes fee denomination token | `FeeTokenUpdated` | All payers must re-approve new token | | Set fee recipient | `GOVERNANCE_ROLE` | Recipient address | Redirects future collections | `FeeRecipientUpdated` | Effective immediately | | Freeze fees | `GOVERNANCE_ROLE` | None | Permanently locks configuration | `FeesFrozen` | Irreversible | *** ## Business impact [#business-impact] * **Holders / payers:** Gross-based: payer must hold both the token being transferred and sufficient balance of the fee ERC-20 token with sufficient allowance to the fee contract. Every operation has an additional cost denominated in the external token. * **Issuer / recipient:** Fee collected in a stable or known ERC-20 asset, enabling predictable fee revenue independent of the token's own price. * **Economics:** Decouples fee economics from the token's own value. Useful when fees must be denominated in a stable asset. *** ## Amount units [#amount-units] Configure mint, burn, and transfer fees in the external fee token, not in the asset's own token. For API calls, submit the raw smallest-unit amount for the configured fee token. In the dapp, the fee-setting form reads the fee token and scales the entered value with that token's decimals before submission. If you change the fee token, review the amounts before operating the asset again. The same displayed amount can map to a different raw value when the new fee token uses different decimals. *** ## Risks & abuse cases [#risks--abuse-cases] * **Transaction revert on insufficient allowance:** If the payer's external ERC-20 allowance is insufficient, the **entire transaction reverts**. Holders who have not approved the fee token will not be able to transfer. * **Fee token price risk:** If the external fee token, such as a non-stable ERC-20, appreciates significantly, fee burden on payers may become prohibitive. * **Fee lock-in after freeze:** Calling `freezeFees()` locks fee amounts permanently. Ensure fee levels are correct before freezing. *** ## Controls & guardrails [#controls--guardrails] | Role | Actions | Recommended guardrail | | ----------------- | -------------------------------------------------- | --------------------------------------------------------------------- | | `GOVERNANCE_ROLE` | `setFees()`: set fee amounts per operation | Call `freezeFees()` after launch to lock | | `GOVERNANCE_ROLE` | `setFeeToken()`: set the external ERC-20 fee token | Audit carefully because changing fee token changes holder obligations | | `GOVERNANCE_ROLE` | `setFeeRecipient()`: set collection destination | Multi-sig controlled | | `GOVERNANCE_ROLE` | `freezeFees()`: lock fee configuration permanently | **Call at launch** | *** ## Failure modes & edge cases [#failure-modes--edge-cases] * **No allowance:** Payer with zero allowance on the fee token will see every transfer revert. Onboarding flows must prompt users to approve the fee token. * **Fee token contract failure:** If the external ERC-20 contract is paused or fails, all token operations that trigger the fee hook will revert. Choose a battle-tested, non-upgradeable fee token. * **Fee token address change:** Changing `feeToken` mid-deployment requires all payers to re-approve the new token. Coordinate with holder communication. *** ## Auditability & operational signals [#auditability--operational-signals] * `ExternalFeeCollected(payer, feeToken, feeAmount, recipient)`: emitted per operation. Use for fee revenue tracking. * `FeesUpdated(sender, feeToken, amounts, recipient)`: emitted on configuration changes. * `FeesFrozen(sender)`: emitted once on freeze. *** ## Dependencies [#dependencies] * **External ERC-20 token contract**: must be deployed and accessible. Payers must hold this token and approve the fee contract. * Payer must have granted allowance to the feature contract before any transfer. *** ## Compatibility & ordering notes [#compatibility--ordering-notes] * `supportsRewriting = false`: does not modify the token transfer amount. * Order after Transaction Fee in the feature array (Transaction Fee rewrites the amount; External Transaction Fee collects separately and independently). * Compatible with compliance modules: runs after compliance checks on the token amount. * Compatible with all other features. *** ## Change impact [#change-impact] * **Enable after launch:** Immediately applies to all subsequent operations. Existing holders must approve the fee token before their next transfer. * **Disable:** No retroactive effect. Payer approvals on the fee token contract remain but become unused. * **Fee amount change (pre-freeze):** Effective immediately for all subsequent operations. * **Fee token change:** All payers must re-approve the new token address. *** ## See also [#see-also] * [Token Features Catalog](/docs/architecture/components/token-features): return to the full feature catalog * [Transaction Fee](/docs/architecture/components/token-features/transaction-fee): in-token fee collection alternative * [Asset Contracts](/docs/architecture/components/asset-contracts): deployment architecture and role model # Fixed Treasury Yield Source: https://docs.settlemint.com/docs/architecture/components/token-features/fixed-treasury-yield Fixed-rate yield paid to token holders at periodic intervals from a treasury. Holders claim completed-period yield, and Historical Balances supplies the snapshot data used for entitlement calculation. Fixed Treasury Yield calculates completed-period yield for DALPAsset holders from historical balance snapshots. A holder calls `claimYield()` to pull the available amount from the configured denomination asset treasury. Unclaimed yield stays in the treasury until claimed. Use this page as the reference for the feature's public capabilities, dependencies, failure modes, and audit signals. ## Interface [#interface] The yield schedule is configured when the feature is deployed. The configuration includes the rate, start date, end date, interval, denomination asset, and treasury address. The feature emits `FixedTreasuryYieldScheduleSet` when that schedule is set. Runtime capabilities are: | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | ------------------------------ | ------------------------------ | ------------------------------------------ | ---------------------------------------------------- | ------------------ | --------------------------------------------- | | Claim accrued yield | Token holder | None, claims all completed periods | Transfers denomination asset from treasury to holder | `YieldClaimed` | Pull-based. Unclaimed yield stays in treasury | | Set treasury address | `GOVERNANCE_ROLE` | Treasury address | Redirects future yield payouts | `TreasuryUpdated` | Fund the new treasury before changing | | Approve treasury allowance | Treasury wallet | Allowance amount in base units | Allows the yield schedule to spend payout assets | ERC-20 `Approval` | Wallet treasuries only | | Consume interest on conversion | Automatic, on burn or redeem | Principal amount and context | Deducts accrued interest for conversion calculation | `InterestConsumed` | Only active when paired with Conversion | | Close accrual on full exit | Automatic, hook on burn/redeem | Triggered when holder balance reaches zero | Stops future yield accrual for holder | `AccrualClosed` | Prevents claims after full redemption | Accrued yield, the period schedule, the current period, and unclaimed totals are available as read-only queries. ## Business impact [#business-impact] * **Holders:** Completed-period yield is claimable in proportion to the holder's balance at each interval snapshot. * **Issuer:** The issuer must keep enough denomination asset in the treasury throughout the yield period. Yield obligations are fixed by the configured rate, interval, and period range. * **Treasury wallet operator:** When the treasury is a wallet, the operator must approve enough denomination asset allowance for the yield schedule before holders claim yield. * **Economics:** Distribution is pull-based. The contract calculates claimable yield, but it does not push payments to every holder automatically. ## Risks and abuse cases [#risks-and-abuse-cases] * **Underfunded treasury:** If the treasury denomination asset balance falls below the total outstanding yield obligation, late claimants may find the treasury depleted. * **Missing wallet allowance:** If the treasury is a wallet and the yield schedule has insufficient denomination asset allowance, holder claims can fail even when the treasury balance is funded. * **Contract treasury path:** Contract treasuries do not use the wallet allowance approval flow. Integrations should not ask a contract treasury to submit the wallet approval step. * **Large holding concentration:** A single holder with a large share of supply can claim a large fraction of the funded treasury on first claim. * **Unclaimed yield accumulates:** Pull-based yield that is never claimed remains in the treasury. Operators should define an expiry or cleanup policy for long-running programmes. * **Rate configured but not funded:** The yield rate can be set without the treasury being funded. Off-chain monitoring should verify treasury funding before the yield period begins. ## Controls and guardrails [#controls-and-guardrails] | Role | Actions | Recommended guardrail | | ----------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `GOVERNANCE_ROLE` | `setTreasury()` to set the denomination asset treasury | Fund the new treasury before future claims draw from the treasury | | Treasury wallet | Approve denomination asset allowance for the yield feature | Before showing the approval action, read `denominationAssetTreasuryAllowance`, `requiredAllowance`, `allowanceCoveredPercentage`, and `treasuryIsContract` from yield coverage | The rate is part of the configured yield schedule. If a programme needs a different rate, deploy a new yield configuration rather than treating the live schedule as a mutable payment instruction. ## Failure modes and edge cases [#failure-modes-and-edge-cases] * **Historical Balances not active:** Fixed Treasury Yield cannot calculate yield without Historical Balances checkpoints. If Historical Balances is removed while yield is active, yield claims can fail. * **Attachment timing:** The indexer records the aggregate feature row at creation time and refreshes per-period values after the Historical Balances feature is attached. During that short pending window, aggregate reads can exist before detailed period rows are refreshed. * **Yield period ended but unclaimed:** Yield calculated from past intervals remains claimable until claimed. Platforms must handle long-tailed claim windows in their UX. * **Treasury address change mid-period:** If `setTreasury()` is called mid-period, future claims draw from the new treasury. Ensure the new treasury is funded before changing. * **Wallet treasury allowance not ready:** For wallet treasuries, claims require the yield schedule to have sufficient denomination asset allowance. Read `denominationAssetTreasuryAllowance`, `requiredAllowance`, `allowanceCoveredPercentage`, and `treasuryIsContract` from `GET /api/v2/tokens/{tokenAddress}/stats/yield-coverage` before prompting the treasury wallet to approve allowance. Prompt only when `treasuryIsContract` is `false` and either `denominationAssetTreasuryAllowance` is lower than `requiredAllowance` or `allowanceCoveredPercentage` is below full coverage. If `treasuryIsContract` is `true`, do not show the wallet approval flow. If `treasuryIsContract` is `null`, treasury classification is still catching up; do not trigger the wallet approval flow yet, and retry the coverage read later. * **Zero-balance holder at snapshot:** Holders with zero balance at an interval snapshot receive zero yield for that interval regardless of balance at other times in the period. ## Auditability and operational signals [#auditability-and-operational-signals] * `FixedTreasuryYieldScheduleSet(startDate, endDate, rate, interval, periodEndTimestamps, denominationAsset, treasury)` is emitted once when the yield schedule is configured. * `YieldClaimed(holder, claimedAmount, fromPeriod, toPeriod, periodAmounts, periodYields, totalYieldPerPeriod)` is emitted per claim and is the primary signal for treasury drawdown tracking. * `TreasuryUpdated(sender, oldTreasury, newTreasury)` is emitted on treasury address change. * `InterestConsumed(holder, amountWad, reason, consumedAt)` is emitted when accrued interest is consumed for conversion calculation. * `AccrualClosed(holder, closedAt)` is emitted when a holder's yield accrual is permanently closed after full exit. * Treasury denomination asset balance should be monitored continuously against remaining yield obligations. * Treasury allowance coverage should be monitored for wallet treasuries by comparing `denominationAssetTreasuryAllowance` with `requiredAllowance`, or by relying on `allowanceCoveredPercentage` from yield coverage reads. ## Dependencies [#dependencies] * **Historical Balances feature:** required for snapshot-based entitlement calculation. Register Historical Balances before Fixed Treasury Yield when enabling both features together. * **Denomination asset:** the ERC-20 asset paid as yield, such as a stablecoin or another approved payment asset. * **Treasury:** the address that funds claims. If the treasury is a wallet, it must approve the yield schedule to spend the payout asset. ## Compatibility and ordering notes [#compatibility-and-ordering-notes] * `supportsRewriting = false`. The feature does not rewrite transfers. * The feature does not restrict mint, burn, transfer, or redeem operations. Yield uses Historical Balances snapshots instead of transfer hooks. * It is compatible with Maturity Redemption. Yield can continue to accrue until maturity, and post-maturity transfer restrictions do not invalidate already claimable yield. * Historical Balances must be available for the yield feature to calculate holder and supply snapshots. When the asset also uses rewriting features, order Historical Balances after those features so snapshots capture final post-rewrite balances. ## Change impact [#change-impact] * **Enable after launch:** Yield accrual starts from the configured `startDate`. Past periods are not retroactively covered. * **Disable mid-period:** Existing claimable yield for completed intervals remains claimable, while future intervals stop accruing. Treasury funds can be withdrawn according to the programme's governance rules. * **Treasury address change:** Future claims draw from the new treasury. Fund the new treasury before the change affects claims. * **Rate change:** Deploy a new yield configuration for a different rate. ## See also [#see-also] * [Token Features Catalog](/docs/architecture/components/token-features) - return to the full feature catalog * [Historical Balances](/docs/architecture/components/token-features/historical-balances) - required dependency * [Yield coverage statistics](/docs/developer-guides/api-integration/yield-coverage-statistics) - API fields for treasury funding and allowance monitoring * [Maturity Redemption](/docs/architecture/components/token-features/maturity-redemption) - compatible lifecycle feature for bonds # Historical Balances Source: https://docs.settlemint.com/docs/architecture/components/token-features/historical-balances Point-in-time balance and total supply queries via on-chain checkpoints. Passive feature — no configuration, no roles, no economic impact. Required by Fixed Treasury Yield. **Purpose:** Historical Balances adds on-chain checkpoints to DALPAsset — recording balance and total supply at every mint, burn, and transfer. Any system can query token state at any past block. * **Doc type:** Reference * **What you'll find here:** Business impact, risks, controls, failure modes, auditability, dependencies, and ordering notes * **Related:** * [Token Features Catalog](/docs/architecture/components/token-features) * [Fixed Treasury Yield](/docs/architecture/components/token-features/fixed-treasury-yield) * [Voting Power](/docs/architecture/components/token-features/voting-power) * [Asset Contracts](/docs/architecture/components/asset-contracts) *** ## Interface (capabilities) [#interface-capabilities] This feature is entirely passive — no configuration, no roles, no economic impact. Every token operation creates a permanent on-chain checkpoint. | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | ----------------------------- | ---------------- | --------------------------------------- | ---------------------------------------- | ------------------- | -------------------------------------------- | | Record balance checkpoint | Automatic (hook) | Triggered on every mint, burn, transfer | Writes balance + total supply checkpoint | `CheckpointUpdated` | Storage grows with each operation | | Query historical balance | Anyone | Account address + block number | None (view-only) | None | Reverts for blocks before feature activation | | Query historical total supply | Anyone | Block number | None (view-only) | None | Required by Fixed Treasury Yield | Note: view-only rows are included here because query capabilities ARE this feature's primary purpose. *** ## Business impact [#business-impact] * **Holders:** Fully passive — no action required, no economic impact, no token movement. * **Issuer / platform:** Enables compliance reporting (point-in-time ownership), snapshot-based airdrop eligibility, and is a required dependency for Fixed Treasury Yield. * **Economics:** No economic impact. Pure data-recording feature. *** ## Risks & abuse cases [#risks--abuse-cases] * **Checkpoint storage growth:** Every token operation creates a new checkpoint entry per holder. High-frequency tokens accumulate large checkpoint histories, increasing on-chain storage costs over time. * **Query gas cost at scale:** Reading historical checkpoints at large block intervals or across many holders increases gas cost for callers (off-chain reads are free). No financial risk vectors — this is a passive observability feature. *** ## Controls & guardrails [#controls--guardrails] | Role | Actions | | ---- | --------------------------------------------------- | | None | No `GOVERNANCE_ROLE` or `CUSTODIAN_ROLE` parameters | No configuration. Tracking starts at deployment (feature activation) and cannot be paused or reconfigured. *** ## Failure modes & edge cases [#failure-modes--edge-cases] * **Pre-activation history:** Transfers before this feature was activated are not checkpointed. Historical queries for blocks before activation will return zero or the activation-time balance. * **Same-block updates:** Multiple transfers in the same block result in a single checkpoint update (the final state for that block), not one per transaction. *** ## Auditability & operational signals [#auditability--operational-signals] * `CheckpointUpdated(sender, account, oldBalance, newBalance)` — emitted on every balance checkpoint write. Monitor for unexpected checkpoint volume. * Checkpoint data is stored on-chain and queryable at any block via `getPastTotalSupply(blockNumber)` and `getPastBalanceOf(account, blockNumber)`. *** ## Dependencies [#dependencies] * No external dependencies. * Required by: **Fixed Treasury Yield** — cannot function without Historical Balances. * Recommended for: any token where compliance reporting, snapshot airdrops, or governance will be used. *** ## Compatibility & ordering notes [#compatibility--ordering-notes] * `supportsRewriting = false` — no amount modification. * **Order last** in the feature array — must run after fee features that rewrite amounts, so snapshots capture post-fee balances. * Compatible with all other features. Voting Power uses its own independent checkpoint mechanism — both can coexist without conflict. *** ## Change impact [#change-impact] * **Enable after launch:** Checkpoints start from the activation block. Pre-activation history is not available. * **Disable:** Existing checkpoints remain on-chain and are still queryable. No new checkpoints are created. Fixed Treasury Yield will stop functioning if Historical Balances is removed. * **No configuration to change.** *** ## See also [#see-also] * [Token Features Catalog](/docs/architecture/components/token-features) — return to the full feature catalog * [Fixed Treasury Yield](/docs/architecture/components/token-features/fixed-treasury-yield) — requires Historical Balances * [Voting Power](/docs/architecture/components/token-features/voting-power) — companion governance feature # Overview Source: https://docs.settlemint.com/docs/architecture/components/token-features Pluggable runtime extensions to DALPAsset. Fees, governance, lifecycle, and convertible instrument features — each configurable without redeployment via the ISMARTFeature interface. > **In development:** Token features are behind a feature flag and under active development. Interfaces and behavior may change before general availability. **Purpose:** Landing page for the DALPAsset token feature system — what features are, how to choose them, how they execute, and where to find each feature's detail page. * **Doc type:** Reference * **What you'll find here:** * Selection guide (features vs compliance modules vs capabilities) * How features execute (hooks, ordering, rewriting) * Categorized feature index with links to detail pages * Access control summary * **Related:** * [Asset Contracts](/docs/architecture/components/asset-contracts) * [Component Catalog](/docs/architecture/components) * [Compliance Modules](/docs/architecture/security/compliance) * [Treasury Distribution](/docs/architecture/flows/treasury-distribution) * [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) *** ## What token features are [#what-token-features-are] Token features are runtime-configurable extensions to DALPAsset via the `ISMARTFeature` interface. They extend token economics — fees, yield, governance, and lifecycle — without redeployment. **[DALPAsset](/docs/architecture/components/asset-contracts/dalp-asset) only.** Specialized legacy types (Bond, Equity, Fund, Deposit, RealEstate) have their capabilities compiled in at deployment and cannot use this system. Features run via lifecycle hooks in the order configured by the caller. Ordering is explicitly the caller's responsibility — there are no on-chain weights. *** ## Selection guide [#selection-guide] Before choosing a token feature, confirm the right tool: | Need | Right tool | | -------------------------------------------------------------------- | ------------------------------------------------------------ | | Restrict, approve, or reject transfers at legal/compliance level | [Compliance Modules](/docs/architecture/security/compliance) | | Standalone operational workflow — distribution, treasury, settlement | Capabilities (Airdrop, Vault, XvP) | | Extend token economics — fees, yield, governance, lifecycle | Token Features (this section) | *** ## How features work [#how-features-work] Features integrate through six lifecycle hooks: | Hook | Trigger | Notes | | --------------------------------- | ---------------------------------------------- | ------------------------------------------ | | `canUpdate(from, to, value, ...)` | Pre-check before any operation | View only — reverts to block the operation | | `onMinted(to, amount)` | After minting | | | `onBurned(from, amount)` | After burning | | | `onTransferred(from, to, amount)` | After transfers | | | `onRedeemed(from, amount)` | After redemptions | | | `onAttached()` | After feature registration via `setFeatures()` | | Features with `supportsRewriting() = true` can modify the transfer amount in-flight (e.g., deduct a fee before the amount reaches the recipient). Features execute in configured array order. **Recommended ordering:** transfer-restriction features first → fee collection → external fee hooks → analytics and governance last. `Transaction Fee` and `External Transaction Fee` rewrite amounts, so analytics features must run after them to snapshot post-fee balances. `AUM Fee` mints new tokens via `collectFee()`, so analytics features must also run after it to observe post-collection supply. | Position | Category | Features | | -------------------- | ---------------------- | ---------------------------------------------------- | | First | Transfer restriction | Maturity Redemption, Conversion (loan-side) | | After restrictions | Fee collection | AUM Fee, Transaction Fee, Transaction Fee Accounting | | After fee collection | External fee hooks | External Transaction Fee | | Last | Analytics & governance | Historical Balances, Voting Power | | Order irrelevant | No-hook utilities | Permit, Conversion Minter | *** ## Feature index [#feature-index] ### Fees & charges [#fees--charges] | Feature | Purpose | Detail | | -------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | | AUM Fee | Time-based management fee as % of AUM; inflationary — mints new tokens to recipient | [AUM Fee](/docs/architecture/components/token-features/aum-fee) | | Transaction Fee | Per-transaction fee deducted from transfer amount; `supportsRewriting = true` | [Transaction Fee](/docs/architecture/components/token-features/transaction-fee) | | Transaction Fee Accounting | Tracks fees per transaction for off-chain reconciliation; no on-chain collection | [Transaction Fee Accounting](/docs/architecture/components/token-features/transaction-fee-accounting) | | External Transaction Fee | Fixed fee in a separate ERC-20 (e.g., USDC) charged on every operation | [External Transaction Fee](/docs/architecture/components/token-features/external-transaction-fee) | ### Governance & snapshots [#governance--snapshots] | Feature | Purpose | Detail | | ------------------- | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | | Voting Power | Delegated voting with historical tracking; compatible with Governor contracts | [Voting Power](/docs/architecture/components/token-features/voting-power) | | Historical Balances | Point-in-time balance and total supply queries via checkpoints; required by Fixed Treasury Yield | [Historical Balances](/docs/architecture/components/token-features/historical-balances) | | Permit | EIP-2612 gasless approvals — sign off-chain, submit on-chain; no hooks | [Permit](/docs/architecture/components/token-features/permit) | ### Lifecycle & yield [#lifecycle--yield] | Feature | Purpose | Detail | | -------------------- | ----------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | Maturity Redemption | Bond maturity lifecycle — blocks transfers post-maturity; holders redeem for denomination asset | [Maturity Redemption](/docs/architecture/components/token-features/maturity-redemption) | | Fixed Treasury Yield | Fixed-rate yield at intervals from treasury; pull-based (holders claim); requires Historical Balances | [Fixed Treasury Yield](/docs/architecture/components/token-features/fixed-treasury-yield) | ### Transformation [#transformation] | Feature | Purpose | Detail | | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | | Conversion (Loan) + Conversion Minter (Equity) | Convertible instrument pair — triggers, burns loan tokens, mints equity; cooperative two-contract design | [Conversion](/docs/architecture/components/token-features/conversion) | *** ## Access control summary [#access-control-summary] | Role | Scope | | ----------------- | ------------------------------------------------------------------------------------- | | `GOVERNANCE_ROLE` | Configuration and policy changes — fee rates, triggers, schedules, treasury addresses | | `CUSTODIAN_ROLE` | Operational actions on behalf of holders — forced conversion, early maturity | # Maturity Redemption Source: https://docs.settlemint.com/docs/architecture/components/token-features/maturity-redemption Bond maturity lifecycle feature for DALPAsset. Manages the pre-maturity, mature, and post-maturity states. Post-maturity transfers are blocked; holders redeem tokens for a denomination asset at face value. **Purpose:** Maturity Redemption implements the maturity lifecycle for bond-type instruments. Before maturity, tokens transfer normally. After maturity, transfers are blocked and holders redeem tokens for a denomination asset at configured face value. * **Doc type:** Reference * **What you'll find here:** Business impact, risks, controls, failure modes, auditability, dependencies, and ordering notes * **Related:** * [Token Features Catalog](/docs/architecture/components/token-features) * [Asset Contracts](/docs/architecture/components/asset-contracts) * [Fixed Treasury Yield](/docs/architecture/components/token-features/fixed-treasury-yield) * [Compliance Modules](/docs/architecture/security/compliance) *** ## Interface (capabilities) [#interface-capabilities] This feature exposes the following capabilities. It implements a three-phase state machine: pre-maturity (normal transfers), mature (governance-triggered transition), post-maturity (transfers blocked, redemption enabled). | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | ----------------------------- | ----------------- | ----------------------------------- | -------------------------------------------------------- | ----------------- | -------------------------------------- | | Trigger maturity | `GOVERNANCE_ROLE` | None | Transitions token to matured state | `Matured` | Only after maturity date; irreversible | | Trigger early maturity | `EMERGENCY_ROLE` | None | Forces matured state before scheduled date | `MaturedEarly` | Emergency use only | | Redeem tokens | Token holder | Amount to redeem | Burns tokens; transfers denomination asset from treasury | `Redeemed` | Only available post-maturity | | Approve treasury allowance | Treasury wallet | Allowance amount in base units | Lets the feature spend denomination asset from treasury | ERC-20 `Approval` | Wallet treasuries only | | Set treasury address | `GOVERNANCE_ROLE` | Treasury address | Redirects redemption payouts | `TreasuryUpdated` | Fund treasury before maturity | | Block transfers post-maturity | Automatic (hook) | Triggered on every transfer attempt | Reverts non-redemption transfers | None (reverts) | Fail-closed via `canUpdate` | *** ## Business impact [#business-impact] * **Holders:** Pre-maturity: normal transfer behavior. Post-maturity: transfers blocked; must call `redeem(amount)` to receive denomination asset at face value. Tokens are burned on redemption. * **Issuer:** Must fund the configured treasury with sufficient denomination asset before or at maturity. For treasuries controlled by a wallet, the treasury also needs to approve the maturity-redemption feature to spend the denomination asset. Underfunded or under-approved treasuries block redemptions. * **Economics:** State machine with three states: pre-maturity → mature (triggered by GOVERNANCE\_ROLE) → post-maturity (redemption window). Token supply decreases as holders redeem. *** ## Treasury allowance and status signals [#treasury-allowance-and-status-signals] Use `GET /api/v2/tokens/{tokenAddress}/stats/bond-status` before and after treasury operations to track maturity-redemption readiness. The response separates treasury balance coverage from allowance coverage: * `denominationAssetBalanceAvailable` and `denominationAssetBalanceRequired` show how much denomination asset the treasury holds versus how much is required for redemption. * `coveredPercentage` shows balance coverage. It does not prove the feature can pull funds from a wallet treasury. * `denominationAssetTreasuryAllowance` shows the allowance the treasury has granted to the maturity-redemption feature on the denomination asset. * `allowanceCoveredPercentage` shows how much of the required redemption volume is covered by that allowance. * `treasuryIsContract` is `true` for contract treasuries, `false` for wallet treasuries, and `null` while classification is still catching up. * `treasuryAddress` and `featureAddress` identify the configured treasury and maturity-redemption feature when one is attached. For wallet treasuries, call `POST /api/v2/tokens/{tokenAddress}/maturity-redemption/treasury-allowance` from the treasury wallet to approve the feature to spend the denomination asset. The approve amount is submitted in base units. Contract treasuries cannot use this wallet-approval route; manage their denomination-asset approvals through the treasury contract's own controls. These status values are indexer-backed. A confirmed treasury top-up, treasury change, or approval transaction can briefly read as the previous value until indexing catches up. Treat `treasuryIsContract: null` as a wait-and-retry state rather than showing an approval action that may fail. *** ## Risks & abuse cases [#risks--abuse-cases] * **Underfunded treasury at maturity:** If the denomination asset treasury does not hold sufficient balance to cover all outstanding tokens at face value, redemptions will revert for holders who try to redeem when funds run out. * **`mature()` called too early:** GOVERNANCE\_ROLE can call `mature()` before the scheduled maturity date. Premature maturity blocks transfers unexpectedly. Use time-locked governance for `mature()` calls. * **Partial redemption cliff:** Holders who do not redeem during the redemption window (if one is defined) may lose access. Define a clear redemption window and communicate it to holders. * **Treasury address change:** `setTreasury()` can redirect redemption payouts. Audit all treasury address changes. *** ## Controls & guardrails [#controls--guardrails] | Role | Actions | Recommended guardrail | | ----------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------- | | `GOVERNANCE_ROLE` | `setTreasury()` — set denomination asset treasury address | Multi-sig; audit all changes; fund treasury before calling `mature()` | | Treasury wallet | approve denomination-asset allowance for the feature | Approve enough allowance for the expected redemption volume before holders redeem | | `GOVERNANCE_ROLE` | `mature()` — transition to post-maturity state | Time-lock or multi-sig to prevent premature maturity | | `EMERGENCY_ROLE` | `matureEarly()` — emergency early maturity trigger | Emergency use only; requires documented justification | *** ## Failure modes & edge cases [#failure-modes--edge-cases] * **Redemption revert on empty or unavailable treasury funds:** If treasury is depleted, later redemptions revert. For wallet treasuries, redemptions can also revert when the denomination-asset allowance is below the amount the feature needs to pull. First redeemers get paid; last redeemers may not if treasury balance or allowance is insufficient. * **`mature()` not called at scheduled date:** Maturity does not trigger automatically. An off-chain process must call `mature()` at the correct time. Missed maturity date means normal transfers continue past the intended date. * **Face value misconfiguration:** Face value is set at deployment. A misconfigured face value cannot be changed post-deployment (immutable by design). *** ## Auditability & operational signals [#auditability--operational-signals] * `Matured(maturityDate, denominationAsset, faceValue)` — emitted when `mature()` is called. * `MaturedEarly(triggeredBy, timestamp)` — emitted when `matureEarly()` is called. * `Redeemed(holder, tokenAmount, denominationAmount)` — emitted per redemption. Monitor for redemption volume and treasury drawdown rate. * Treasury balance and allowance monitoring: track denomination asset balance in treasury and, for wallet treasuries, the allowance granted to the maturity-redemption feature; alert when either side falls below the required redemption volume. *** ## Dependencies [#dependencies] * **Denomination asset (ERC-20)** — the asset paid to holders on redemption (e.g., USDC, stablecoin). Must be deployed and accessible. * **Treasury (wallet or contract)** — must hold sufficient denomination asset at maturity. Treasury address set via `setTreasury()`; wallet treasuries must also approve the maturity-redemption feature to spend the denomination asset. * **Off-chain process** — must call `mature()` at the scheduled maturity date. *** ## Compatibility & ordering notes [#compatibility--ordering-notes] * `supportsRewriting = false` — does not modify amounts. Post-maturity transfer blocking is enforced via `canUpdate()` (reverts for all non-redemption transfers). * **Order first** in the feature array (transfer restriction). Must run before fee features. * Compatible with Fixed Treasury Yield: both can be active simultaneously for coupon-paying bonds. Yield is pull-based and independent of maturity state. * Compliance modules run before features — compliance can block transfers pre-maturity; Maturity Redemption blocks post-maturity regardless. *** ## Change impact [#change-impact] * **Enable at deployment:** Must be configured at deployment with face value and maturity parameters. Cannot be added post-deployment without these parameters. * **Disable before maturity:** Can be removed without effect (no redemption has occurred). * **Disable after `mature()` is called:** Transfer blocking ceases; holders can no longer redeem. Do not disable post-maturity without a redemption alternative. * **Treasury address change:** Effective for all future redemptions. Existing redemption transactions in-flight complete at old treasury. *** ## See also [#see-also] * [Token Features Catalog](/docs/architecture/components/token-features) — return to the full feature catalog * [Fixed Treasury Yield](/docs/architecture/components/token-features/fixed-treasury-yield) — coupon payments compatible with maturity * [Compliance Modules](/docs/architecture/security/compliance) — transfer enforcement layer # Permit Source: https://docs.settlemint.com/docs/architecture/components/token-features/permit EIP-2612 gasless approvals for DALPAsset. Users sign approval messages off-chain; anyone can submit the signature on-chain to set an allowance without the holder paying gas. Permit adds EIP-2612 gasless approvals to DALPAsset tokens. A token holder signs an approval message off-chain, and a relayer or authorized application submits the signature on-chain to set the ERC-20 allowance without the holder sending the approval transaction. Permit is independent of token transfer hooks and governance configuration. It only sets an allowance after a valid owner signature, deadline, nonce, and EIP-712 domain are accepted. Related architecture pages: [token features catalog](/docs/architecture/components/token-features) and [asset contracts](/docs/architecture/components/asset-contracts). *** ## Interface (capabilities) [#interface-capabilities] This feature exposes the following capabilities. It implements EIP-2612 gasless approvals as a pure UX utility with no hooks, no lifecycle integration, and no governance configuration. | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | --------------------- | ----------------------------- | ------------------------------------------- | ------------------------------ | ---------- | --------------------------------------- | | Approve via signature | Anyone (with valid signature) | Owner, spender, amount, deadline, signature | Sets ERC-20 spending allowance | `Approval` | Nonce-based replay protection; no hooks | | Query nonce | Anyone | Account address | None (view-only) | None | Required to construct valid signatures | *** ## Business impact [#business-impact] * **Holders:** Can approve spending without paying gas. Enables single-transaction "approve-and-transfer" flows where the dApp submits the permit and transfer in one transaction. * **Issuer / platform:** Improves onboarding UX for integrations requiring allowances (e.g., DeFi protocols, capability contracts). No economic impact. * **Economics:** No token movement. Pure UX utility feature. *** ## Risks & abuse cases [#risks--abuse-cases] * **Phishing:** Users who sign permit messages without checking the spender, amount, deadline, and chain ID can inadvertently authorize malicious spenders. Wallet UX must clearly display permit details. * **Signature replay across chains:** Permit signatures are domain-separated (chain ID + contract address). A signature from one chain cannot be replayed on another, but users should be aware that signing is irreversible until the deadline expires. * **Expired permits:** Permit signatures include a deadline. Submitting after the deadline reverts. Callers must check deadline before submission. *** ## Controls & guardrails [#controls--guardrails] | Role | Actions | | ---- | --------------------------------------------------- | | None | No `GOVERNANCE_ROLE` or `CUSTODIAN_ROLE` parameters | Permit has no on-chain configuration. Nonce-based replay protection is automatic. Each signature can only be submitted once. *** ## Failure modes & edge cases [#failure-modes--edge-cases] * **Deadline expired:** Submitting a valid signature after the deadline reverts with no effect. * **Nonce mismatch:** If the holder submits any transaction that changes their nonce (including another permit), all outstanding permits for older nonces become invalid. * **Wrong chain ID:** Signatures from another chain (or testnet) will fail domain verification. *** ## Auditability & operational signals [#auditability--operational-signals] * `Approval(owner, spender, value)` - standard ERC-20 event emitted when permit is submitted successfully. Indistinguishable from a regular approval in event logs. * No permit-specific event in base EIP-2612. Submitter address is in transaction calldata, not event logs. *** ## Dependencies [#dependencies] * No external dependencies. * No other features required. * No hooks: completely independent of the feature hook execution chain. *** ## Compatibility & ordering notes [#compatibility--ordering-notes] * **No hooks**: does not interact with `onMinted`, `onBurned`, `onTransferred`, or `onRedeemed`. * Order in the feature array is irrelevant. * Compatible with all other features. Low-risk utility. * Compatible with compliance modules: permit sets an allowance; the subsequent transfer still goes through compliance checks. *** ## Change impact [#change-impact] * **Enable after launch:** Immediately enables gasless approvals. No retroactive effect. * **Disable:** Outstanding signed-but-not-submitted permits are no longer valid. Any integration relying on permit-based flows must fall back to standard `approve()`. * **No configuration to change.** *** ## See also [#see-also] * [Token Features Catalog](/docs/architecture/components/token-features) - return to the full feature catalog * [Asset Contracts](/docs/architecture/components/asset-contracts) - deployment architecture and role model * [Compliance Modules](/docs/architecture/security/compliance) - transfer enforcement (runs after permit sets allowance) # Transaction Fee Accounting Source: https://docs.settlemint.com/docs/architecture/components/token-features/transaction-fee-accounting Tracks per-transaction fees for off-chain reconciliation without collecting them on-chain. Emits events for invoicing and reporting workflows. No holder economic impact. Transaction Fee Accounting records fee obligations for mint, burn, transfer, and redemption operations without collecting tokens on-chain. Platforms use the emitted accounting events to invoice, report, or settle those obligations outside the token contract. Governance users can set account exemptions from the transaction fee accounting capability card when an address should be excluded from fee tracking. Related pages: * [Token Features Catalog](/docs/architecture/components/token-features) * [Asset Contracts](/docs/architecture/components/asset-contracts) * [Treasury Distribution](/docs/architecture/flows/treasury-distribution) ## Interface [#interface] Unlike [Transaction Fee](/docs/architecture/components/token-features/transaction-fee), Transaction Fee Accounting tracks fee obligations without moving or withholding tokens. | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | ----------------------- | ----------------- | --------------------------------------------- | ------------------------------------------------ | --------------------- | ------------------------------------------------------- | | Record fee on operation | Automatic (hook) | Triggered on mint, burn, transfer, redemption | Increments accrued counter; no token movement | `FeeAccrued` | Does not rewrite amounts | | Reconcile accrued fees | `GOVERNANCE_ROLE` | None | Resets accrued counter; closes accounting period | `FeesReconciled` | Off-chain settlement trigger | | Set fee rates | `GOVERNANCE_ROLE` | Mint, burn, and transfer rates in bps | Updates fee rate configuration | `FeeRatesUpdated` | Redemptions use the burn fee rate; blocked after freeze | | Set fee recipient | `GOVERNANCE_ROLE` | Recipient address | Redirects future reconciliation target | `FeeRecipientUpdated` | Effective immediately | | Set fee exemption | `GOVERNANCE_ROLE` | Account address + exempt flag | Marks address as exempt from tracking | `FeeExemptionSet` | Available from the dapp capability card and API | | Freeze fee rates | `GOVERNANCE_ROLE` | None | Permanently locks all rates | `FeeRatesFrozen` | Irreversible | *** ## Business impact [#business-impact] * **Holders:** No on-chain economic impact. Fees are tracked, not collected, and token amounts are not rewritten. * **Issuer / platform:** Fee obligations accumulate off-chain; platform must run a reconciliation workflow to actually collect. * **Economics:** Decouples fee obligation recording from settlement. Enables periodic bulk invoicing rather than per-transaction collection. *** ## Risks & abuse cases [#risks--abuse-cases] * **Reconciliation drift:** If `reconcileFees()` is not called on schedule, accrued fee obligations grow without bound in accounting records. Platforms must maintain a disciplined reconciliation cadence. * **Exemption scope creep:** `setFeeExemption()` can exempt specific addresses from fee tracking. If over-used, fee accounting becomes incomplete. Exempting the zero address is valid and excludes mint-side or burn-side fee tracking because mint and burn operations use the zero address on one side of the token movement. * **No on-chain enforcement:** Fee obligations are advisory. Off-chain systems must act on emitted events to collect. There is no automatic enforcement mechanism. *** ## Controls & guardrails [#controls--guardrails] | Role | Actions | Recommended guardrail | | ----------------- | ------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------- | | `GOVERNANCE_ROLE` | `setFeeRates()` - set mint, burn, and transfer rates | Document changes and freeze rates when they should no longer move | | `GOVERNANCE_ROLE` | `reconcileFees()` - mark accrued fees as reconciled for a period | Automate via scheduled off-chain job | | `GOVERNANCE_ROLE` | `setFeeExemption(address, exempt)` - exempt a holder, system address, or the zero address for mint/burn accounting paths | Audit exemption list; keep exemptions narrow and time-bound | | `GOVERNANCE_ROLE` | `freezeFeeRates()` - permanently lock the configured rates | Treat as irreversible | *** ## Failure modes & edge cases [#failure-modes--edge-cases] * **Accounting without collection:** Recorded obligations that are never settled create audit discrepancies. Establish a settlement SLA. * **Concurrent fee events:** Fee accruals are added to the existing indexed total. Reconciliation resets the accrued total and adds the reconciled amount to the reconciled total, so reporting jobs should treat `FeeAccrued` and `FeesReconciled` as period events rather than overwriting earlier events. * **Rate changes mid-period:** If fee rates change during a billing period, events reflect the rate at the time of the transaction. Off-chain systems must handle rate changes in reconciliation logic. * **High event volume:** High-frequency tokens produce large event logs. Ensure off-chain indexing infrastructure handles the volume. *** ## Auditability & operational signals [#auditability--operational-signals] * `FeeAccrued(payer, from, to, feeType, operationAmount, feeBps, feeAmount, timestamp)` - emitted per tracked operation. Use `feeAmount` for the accrued obligation and `feeType` to distinguish mint, burn, transfer, and redemption operations. * `FeesReconciled(caller, recipient, amount, periodEnd)` - emitted when accrued fees are reconciled. After reconciliation, the accrued total returns to zero and the reconciled total increases by the reconciled amount. * `FeeRatesUpdated(sender, oldRates, newRates)` - emitted when fee rates change. * `FeeRecipientUpdated(sender, oldRecipient, newRecipient)` - emitted when the recipient changes. * `FeeRatesFrozen(sender)` - emitted when rate changes are permanently locked. * `FeeExemptionSet(sender, account, exempt)` - emitted when exemption status changes. *** ## Dependencies [#dependencies] * No external ERC-20 required. * No other features required. * Off-chain reconciliation infrastructure required (indexer, invoicing system, or subgraph). *** ## Compatibility & ordering notes [#compatibility--ordering-notes] * `supportsRewriting = false` - does not modify transfer amounts. Safe to run at any position. * Mutually exclusive with Transaction Fee in asset templates and token creation requests. Use Transaction Fee for on-chain collection, or Transaction Fee Accounting for off-chain settlement records. * Compatible with all other features. There are no ordering constraints. *** ## Change impact [#change-impact] * **Enable after launch:** Historical transactions before activation are not retroactively tracked. * **Disable:** In-progress uncollected obligations remain in off-chain records. Reconcile before disabling. * **Rate change:** Effective immediately. Off-chain systems must handle rate history for correct reconciliation. *** ## See also [#see-also] * [Token Features Catalog](/docs/architecture/components/token-features) - return to the full feature catalog * [Transaction Fee](/docs/architecture/components/token-features/transaction-fee) - on-chain fee collection alternative * [Treasury Distribution](/docs/architecture/flows/treasury-distribution) - distribution and settlement flows # Transaction Fee Source: https://docs.settlemint.com/docs/architecture/components/token-features/transaction-fee Per-transaction fee deducted from transfer amounts on every mint, burn, or transfer. Uses supportsRewriting to reduce the amount in-flight before compliance checks and recipient receipt. Transaction Fee deducts a per-transaction fee from mint, burn, and transfer operations by rewriting the token amount in-flight. The recipient receives `amount - fee`; the configured fee recipient receives the fee in the same token. Redemption emits the same collection signal and uses the burn fee calculation, but the redemption payout asset is calculated by the redemption feature before the transaction-fee rewrite runs. *** ## Interface (capabilities) [#interface-capabilities] This feature exposes the following capabilities. It is a rewriting feature: it modifies the token amount in-flight for mint, burn, and transfer operations, so the recipient receives the post-fee amount. | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | ----------------------- | ----------------- | ------------------------------------------------------ | ---------------------------------------- | ------------------------- | -------------------------- | | Deduct fee on operation | Automatic (hook) | Triggered on mint, burn, transfer, redeem | Rewrites amount; routes fee to recipient | `TransactionFeeCollected` | `supportsRewriting = true` | | Set fee rates | `GOVERNANCE_ROLE` | Rates per operation type (mint, burn, transfer in bps) | Updates fee rate configuration | `FeeRatesUpdated` | Blocked after freeze | | Set fee recipient | `GOVERNANCE_ROLE` | Recipient address | Redirects future fee collection | `FeeRecipientUpdated` | Effective immediately | | Freeze fee rates | `GOVERNANCE_ROLE` | None | Permanently locks all rates | `FeeRatesFrozen` | Irreversible | *** ## Business impact [#business-impact] * **Holders:** Holders receive the post-fee token amount on mint, burn, and transfer operations. Senders initiate transfers for the gross amount; recipients receive the net amount. * **Issuer / recipient:** Fees are collected in the token itself and routed to the configured `feeRecipient`. * **Economics:** Fees are deducted on mint, burn, and transfer operations. Redemption uses the burn fee calculation for the in-token collection signal; it does not reduce the denomination-asset payout calculated by a redemption feature. *** ## Risks & abuse cases [#risks--abuse-cases] * **Compliance module ordering risk:** Because `supportsRewriting = true`, the amount is rewritten before compliance modules run their checks. A compliance module checking minimum transfer amounts will see the post-fee (reduced) amount. Coordinate fee rates and compliance thresholds. * **Rate freeze timing:** Leaving `freezeFeeRates()` uncalled allows GOVERNANCE\_ROLE to raise rates after deployment. Call at launch. * **High-frequency fee accumulation:** Platforms with high transaction volume should monitor fee recipient balance; unexpected recipient addresses can drain value. *** ## Controls & guardrails [#controls--guardrails] | Role | Actions | Recommended guardrail | | ----------------- | -------------------------------------------- | ------------------------------------ | | `GOVERNANCE_ROLE` | `setFeeRates()`: set fee rates per operation | Call `freezeFeeRates()` after launch | | `GOVERNANCE_ROLE` | `setFeeRecipient()`: set fee destination | Multi-sig; audit all changes | | `GOVERNANCE_ROLE` | `freezeFeeRates()`: lock rates permanently | **Call at launch** | *** ## Failure modes & edge cases [#failure-modes--edge-cases] * **Zero-amount transfer after fee:** If fee equals the full transfer amount, the recipient receives zero tokens and the fee recipient receives the full amount. No revert: check minimum transfer amounts in compliance modules. * **Fee on minting:** Minting to a holder results in holder receiving `amount - mint_fee`. If mint semantics require exact amounts, account for the fee in the mint call. *** ## Auditability and operational signals [#auditability-and-operational-signals] * `TransactionFeeCollected(from, feeAmount, operationType)` is emitted per fee-bearing operation. Use it for reconciliation and monitoring. * `FeeRatesUpdated(sender, mintRate, burnRate, transferRate)` is emitted on rate changes. * `FeeRatesFrozen(sender)` is emitted once on freeze. DALP also exposes indexed collection rows for the active transaction-fee feature at `GET /api/v2/tokens/{tokenAddress}/transaction-fee/collections`. The read returns a paginated `data`, `meta`, and `links` response with `counterpartyAddress`, `operationType`, `feeAmount`, `feeAmountExact`, `blockNumber`, `blockTimestamp`, `txHash`, and `logIndex` for each row. Use this collection read when a dashboard or integration needs collection history without scanning token events itself. The endpoint returns an empty collection when the token has no attached transaction-fee feature. *** ## Dependencies [#dependencies] * No external ERC-20 required: fee paid in the token itself. * `supportsRewriting = true`: interacts with the amount-rewriting pipeline. Must run before analytics features. *** ## Compatibility & ordering notes [#compatibility--ordering-notes] * **Must run before:** Historical Balances, Voting Power (analytics hooks must see post-fee amounts). * **Compliance module interaction:** Compliance checks receive the post-rewrite amount. If any module enforces minimum amounts, ensure thresholds account for maximum fee rates. * Compatible with AUM Fee (both collect in-token; order doesn't matter between them, but both before analytics). * Compatible with External Transaction Fee (two separate fee mechanisms; External runs after). *** ## Change impact [#change-impact] * **Enable after launch:** Only applies to transactions from activation point forward. * **Disable:** No retroactive effect. In-flight transactions complete at old rate. * **Rate change before freeze:** Effective immediately on next transaction. * **Recipient change:** Effective immediately for all future collections. *** ## See also [#see-also] * [Token Features Catalog](/docs/architecture/components/token-features): return to the full feature catalog * [Transaction Fee Accounting](/docs/architecture/components/token-features/transaction-fee-accounting): off-chain tracking alternative * [Compliance Modules](/docs/architecture/security/compliance): transfer-level enforcement layer # Voting Power Source: https://docs.settlemint.com/docs/architecture/components/token-features/voting-power Delegated voting with historical tracking on DALPAsset. Enables on-chain governance participation compatible with Governor contracts. Requires holder delegation to activate voting weight. **Purpose:** Voting Power adds delegated voting with historical tracking to DALPAsset tokens. Token holders must explicitly delegate their voting weight (to themselves or another address) to participate in governance. * **Doc type:** Reference * **What you'll find here:** Business impact, risks, controls, failure modes, auditability, dependencies, and ordering notes * **Related:** * [Token Features Catalog](/docs/architecture/components/token-features) * [Asset Contracts](/docs/architecture/components/asset-contracts) * [Historical Balances](/docs/architecture/components/token-features/historical-balances) *** ## Interface (capabilities) [#interface-capabilities] This feature exposes the following capabilities. Holders must explicitly delegate their voting weight before it becomes active — undelegated tokens carry no governance power. | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | ------------------------------ | ----------------------------- | ----------------------------------- | ------------------------------------------------------- | ----------------------------------------- | ---------------------------------------- | | Delegate voting power | Token holder | Delegatee address (self or another) | Assigns holder's voting weight to delegatee | `DelegateChanged`, `DelegateVotesChanged` | Must delegate before governance snapshot | | Delegate by signature | Anyone (with valid signature) | Signed delegation parameters | Assigns voting weight without holder gas | `DelegateChanged`, `DelegateVotesChanged` | Gasless via EIP-712 | | Record checkpoints on transfer | Automatic (hook) | Triggered on mint, burn, transfer | Updates voting power checkpoints for affected delegates | `DelegateVotesChanged` | Only affects delegated balances | Current and historical voting power, past total supply, and delegation status are available as read-only queries. *** ## Business impact [#business-impact] * **Holders:** Voting power equals token balance at the time of a governance snapshot, but only if the holder has delegated (to themselves or another address). Undelegated tokens have no voting weight. * **Issuer / governance:** Enables on-chain governance contracts (e.g., OpenZeppelin Governor) to query voting power at any historical block. Governance proposals can require quorum relative to total delegated power. * **Economics:** No token movement. Purely governance-additive. *** ## Risks & abuse cases [#risks--abuse-cases] * **Low participation from non-delegation:** If holders do not call `delegate()`, their tokens have no voting power. Governance quorum may be difficult to reach. Onboarding flows should prompt delegation. * **Delegation to a single address:** If many holders delegate to one address, that address gains outsized governance power. Monitor delegation concentration. * **Flash loan governance attacks:** Without a snapshot-based quorum mechanism in the governance contract, a flash loan could temporarily inflate delegated power at proposal creation. Ensure governance contract uses snapshot blocks. *** ## Controls & guardrails [#controls--guardrails] | Role | Actions | Recommended guardrail | | -------------------------------------------------- | -------------------------------------------------------------- | ------------------------------------------------ | | Holders | `delegate(address)` — delegate voting power to self or another | Prompt at onboarding; remind undelegated holders | | No on-chain governance role for the feature itself | Feature has no `GOVERNANCE_ROLE` or `CUSTODIAN_ROLE` actions | — | *** ## Failure modes & edge cases [#failure-modes--edge-cases] * **Transfer before delegation:** A holder who transfers tokens before delegating has never had active voting power — no retroactive delegation occurs. * **Delegation after snapshot:** Delegating after a governance snapshot block is too late for that proposal. Delegation must be in place at the snapshot block. * **Checkpoint bloat:** High-frequency transfer tokens accumulate many checkpoints per holder. This is expected behavior but increases on-chain storage over time. *** ## Auditability & operational signals [#auditability--operational-signals] * `DelegateChanged(delegator, fromDelegate, toDelegate)` — emitted on delegation change. * `DelegateVotesChanged(delegate, previousBalance, newBalance)` — emitted on vote balance change (transfer, mint, burn with delegation active). * Historical checkpoints queryable at any block — governance contracts use these for proposal snapshot queries. *** ## Dependencies [#dependencies] * No external dependencies. * Compatible with Historical Balances — both use checkpoints independently. * Requires governance contract integration (e.g., OpenZeppelin Governor or equivalent) to have utility. *** ## Compatibility & ordering notes [#compatibility--ordering-notes] * `supportsRewriting = false` — no amount modification. * Order last in the feature array (after fee features). This ensures delegation snapshots reflect post-fee balances. * Compatible with all other features. No ordering conflicts beyond the general "analytics last" convention. *** ## Change impact [#change-impact] * **Enable after launch:** Historical delegation state starts from activation. Pre-activation transfer history does not generate checkpoint entries. * **Disable:** Voting power queries stop working. Any active governance proposals that referenced this token's voting power at a past block will still resolve (checkpoints remain on-chain), but future governance is blocked. * **No configuration to change** — feature has no GOVERNANCE\_ROLE parameters. *** ## See also [#see-also] * [Token Features Catalog](/docs/architecture/components/token-features) — return to the full feature catalog * [Historical Balances](/docs/architecture/components/token-features/historical-balances) — companion snapshot feature * [Asset Contracts](/docs/architecture/components/asset-contracts) — role model and deployment architecture # Airdrop Distribution Flow Source: https://docs.settlemint.com/docs/architecture/flows/airdrop-distribution How the DALP airdrop distribution flow operates -- from Merkle root deployment through proof verification, claim tracking, and token transfer across all three airdrop strategies. ## Related [#related] * [Airdrop](/docs/architecture/components/capabilities/airdrop) -- contracts, roles, and configuration * [Signing Flow](/docs/architecture/flows/signing-flow) -- transaction signing and custody * [Compliance Transfer](/docs/architecture/flows/compliance-transfer) -- transfer validation for compliance-enabled tokens * [Asset Issuance](/docs/architecture/flows/asset-issuance) -- how assets are created before distribution *** ## Flow overview [#flow-overview] The airdrop distribution flow moves tokens from a funded airdrop contract to eligible recipients. Eligibility is verified cryptographically using Merkle proofs -- no off-chain oracle is trusted at claim time. The flow applies to all three strategy types, with each variant adding constraints on timing, release schedule, or distribution model. ## Happy path -- claim-based airdrop [#happy-path----claim-based-airdrop] 1. **Admin deploys airdrop** -- the airdrop contract is deployed with a Merkle root encoding the full recipient list and allocations. Strategy-specific parameters (time window, vesting schedule) are set at deployment and are immutable. 2. **Admin funds the contract** -- tokens are transferred to the airdrop contract address. The contract must hold sufficient balance to cover all allocations. 3. **Recipient submits claim** -- an eligible recipient generates a Merkle proof off-chain and calls `claim(proof, amount)` on the airdrop contract. 4. **Merkle proof verification** -- the contract verifies the submitted proof against the stored root. Invalid proofs revert immediately. 5. **Claim tracker check** -- the contract queries the claim tracker to confirm the recipient has not exceeded their allocation. 6. **Token transfer** -- tokens move from the airdrop contract to the recipient address via ERC20 transfer. 7. **Claim recorded** -- the claim tracker updates state to prevent duplicate claims. ## Strategy variants [#strategy-variants] Each strategy modifies the base flow with additional constraints: ### Time-bound airdrop [#time-bound-airdrop] * **Claim window enforced** -- the contract checks that `block.timestamp` falls between the configured start and end times. Claims outside this window revert. * **Post-expiration withdrawal** -- after the end time, the contract owner can withdraw unclaimed tokens. Recipients can no longer claim. ### Vesting airdrop [#vesting-airdrop] * **Cliff period** -- no tokens release until the cliff duration elapses from the vesting start time. * **Progressive release** -- after the cliff, tokens unlock linearly over the vesting period. Recipients can claim their vested portion at any time. * **Multiple claims** -- recipients call `claim()` repeatedly as more tokens vest. The amount tracker records cumulative claims against the total allocation. ### Push airdrop [#push-airdrop] * **Admin-initiated** -- the administrator calls a batch distribution function, pushing tokens directly to recipient addresses. No Merkle proof or recipient action is required. * **No claim tracker** -- distribution is controlled entirely by the admin. The Merkle root still validates recipient eligibility, but the claim lifecycle is inverted. ## Claim tracking strategies [#claim-tracking-strategies] | Strategy | Mechanism | Best for | Gas profile | | ------------------------------- | ------------------------------ | -------------------------------- | --------------------------------------------------- | | Bitmap (DALPBitmapClaimTracker) | One bit per recipient | All-or-nothing claims | Constant cost, gas-efficient for large lists | | Amount (DALPAmountClaimTracker) | Tracks claimed amount per user | Partial claims, vesting airdrops | Higher per-claim cost, supports progressive release | The claim tracker is selected at deployment and cannot be changed. Bitmap tracking is the default for time-bound and push strategies. Amount tracking is required for vesting airdrops. ## Failure modes [#failure-modes] * **Invalid Merkle proof** -- proof does not match the stored root. Transaction reverts with no state change. * **Already claimed (bitmap)** -- recipient's bit is already set. Transaction reverts. * **Claim exceeds allocation (amount)** -- cumulative claimed amount would exceed the Merkle-encoded allocation. Transaction reverts. * **Outside claim window (time-bound)** -- `block.timestamp` is before start or after end. Transaction reverts. * **Before cliff (vesting)** -- vesting cliff has not elapsed. No tokens are releasable; transaction reverts. * **Insufficient airdrop balance** -- the contract does not hold enough tokens to fulfill the claim. ERC20 transfer reverts. Smart contracts for all three airdrop strategies are deployed and available. The Asset Console UI for creating and managing airdrops is in development. ## Related resources [#related-resources] * [Airdrop](/docs/architecture/components/capabilities/airdrop) -- contracts, roles, and configuration * [Signing Flow](/docs/architecture/flows/signing-flow) -- how transactions are signed and broadcast * [Compliance Transfer](/docs/architecture/flows/compliance-transfer) -- transfer validation when tokens carry compliance rules * [Asset Issuance](/docs/architecture/flows/asset-issuance) -- how assets are created before airdrops * [Capabilities overview](/docs/architecture/components/capabilities) -- how capabilities extend the platform # Asset Issuance Source: https://docs.settlemint.com/docs/architecture/flows/asset-issuance Step-by-step flow for issuing a new digital asset on the DALP platform, from infrastructure deployment through identity setup, compliance configuration, and initial token operations. Step-by-step reference for the complete asset issuance workflow, from platform infrastructure through first token operations. ## Related [#related] * [Asset Contracts](/docs/architecture/components/asset-contracts) — token types and configurations * [Signing Flow](/docs/architecture/flows/signing-flow) — transaction signing sequence * [Authorization](/docs/architecture/security/authorization) — role assignments *** ## Deployment phases [#deployment-phases] Asset issuance spans 7 phases. Phases 1-3 execute once per platform deployment. Phases 4-7 repeat for each new instrument. *** ## Phase details [#phase-details] Asset issuance begins in the Asset Designer ### Phase 1: Infrastructure deployment [#phase-1-infrastructure-deployment] Deploys all implementation contracts and the system factory. On SettleMint networks with genesis allocations, implementation contracts are predeployed — execution begins at Phase 2. | Step | Transaction | Sender | | ---- | --------------------------------------------------------------------- | -------- | | 1 | Deploy 43 implementation contracts (system, token, addon, compliance) | Deployer | | 2 | Deploy DALPSystemFactory with all implementation addresses | Deployer | **Output:** System factory address ### Phase 2: System bootstrap [#phase-2-system-bootstrap] Creates the platform system instance and registers all factories. | Step | Transaction | Role required | | ---- | ------------------------------------------------------------------ | ------------- | | 1 | `createSystem()` on SystemFactory | Deployer | | 2 | `bootstrap()` on System proxy | Deployer | | 3 | Register token factories (Bond, Deposit, Equity, Fund, StableCoin) | Deployer | | 4 | Register addon factories (yield schedule, XvP, airdrops, vault) | Deployer | **Output:** System proxy, access manager, identity registry, compliance contract, all factory registries ### Phase 3: Identity & compliance setup [#phase-3-identity--compliance-setup] Configures the identity verification system and global compliance rules. | Step | Transaction | Role required | | ---- | ------------------------------------------------------------ | ------------------------- | | 1 | Grant identity manager role | System admin | | 2 | Create identities for actors | Each actor | | 3 | Register identities in registry | Identity manager | | 4 | Add trusted claim issuer(s) | Claim policy manager | | 5 | Issue KYC/AML claims to identities | Actors (signed by issuer) | | 6 | Add global compliance modules (country block, address block) | Compliance manager | ### Phase 4: Asset creation (per instrument) [#phase-4-asset-creation-per-instrument] Creates a new token through the appropriate factory. | Step | Transaction | Role required | | ---- | ----------------------------------------------------------------------------- | -------------- | | 1 | Call `create()` on the asset factory | Token manager | | 2 | Token factory deploys proxy, creates identity, registers in identity registry | Automatic | | 3 | Add asset metadata claims (classification, ISIN) | Token contract | **Output:** Token proxy address, token identity, per-token access manager ### Phase 5: Role assignment (per instrument) [#phase-5-role-assignment-per-instrument] Assigns per-token roles to operators. | Step | Transaction | Role required | | ---- | ------------------------------------------ | ------------- | | 1 | Grant supply management role | Token admin | | 2 | Grant emergency role | Token admin | | 3 | Grant governance role (if features needed) | Token admin | ### Phase 6: Feature attachment (per instrument) [#phase-6-feature-attachment-per-instrument] Optionally attaches runtime features via addon factories. | Step | Transaction | Role required | | ---- | ------------------------------------------------------------- | ------------------ | | 1 | Deploy feature proxy via addon factory (e.g., yield schedule) | Governance | | 2 | Attach addon to token contract | Token admin | | 3 | Configure per-token compliance modules | Compliance manager | ### Phase 7: Initial operations [#phase-7-initial-operations] Validates the token with first operations. | Step | Transaction | Role required | | ---- | ------------------------------------------------------ | ----------------- | | 1 | Mint tokens to initial holders | Supply management | | 2 | Transfer between verified holders | Token holders | | 3 | Verify compliance enforcement (blocked transfers fail) | — | *** Review and deploy confirmation ## Key dependencies [#key-dependencies] * Identity registration (Phase 3) must complete before any token operations * Global compliance modules (Phase 3) apply to all tokens automatically * Per-token compliance modules (Phase 6) are additive to global modules * All transactions execute through the [Signing Flow](/docs/architecture/flows/signing-flow) *** ## See also [#see-also] * [Signing Flow](/docs/architecture/flows/signing-flow) for how each transaction is signed * [Deployment Architecture](/docs/architecture/components/asset-contracts/deployment-architecture) for factory deployment patterns * [Authorization](/docs/architecture/security/authorization) for role definitions # Compliance Transfer Source: https://docs.settlemint.com/docs/architecture/flows/compliance-transfer Step-by-step sequence for how DALP validates token transfers through the compliance engine, from identity resolution through sequential module evaluation to post-transfer state updates. Step-by-step reference for the compliance validation sequence that every token transfer passes through before execution. ## Related [#related] * [Identity & Compliance](/docs/architecture/security/identity-compliance) — architecture explanation * [Compliance Modules](/docs/architecture/security/compliance) — module catalog * [Signing Flow](/docs/architecture/flows/signing-flow) — complete transaction security sequence * [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) — core protocol architecture *** ## Transfer validation sequence [#transfer-validation-sequence] Every token transfer (including mints, burns, and redemptions) passes through the compliance engine before any state changes occur. ### Step-by-step flow [#step-by-step-flow] 1. **Transfer initiation** — User calls `transfer(to, amount)` on the token contract 2. **Compliance check** — Token contract calls `canTransfer(from, to, amount)` on the compliance contract before any state changes 3. **Identity resolution** — Compliance contract queries the identity registry to map wallet addresses to OnchainID contracts for both sender and recipient. Missing mappings fail immediately with "Identity not registered" 4. **Sequential module evaluation** — Compliance contract iterates through modules in configuration order: * Each module receives token address, sender, recipient, amount, and encoded parameters * Modules that require claims query the sender's/recipient's OnchainID contracts * Claim validation checks: claim exists for required topic, trusted issuer issued it, claim has not expired, claim data matches expected values * First module returning `false` immediately reverts the entire transaction with a reason string (fail-fast) 5. **Balance transfer** — After all modules pass, token contract executes `_transfer(from, to, amount)` 6. **State update hooks** — Token contract calls `transferred(from, to, amount)` on the compliance contract, which forwards to all configured modules for state updates: * Investor count modules increment/decrement holder counts * Supply tracking modules update accumulation * Time lock modules record acquisition timestamps * Transfer approval modules consume single-use approvals *** Compliance verification checks gate all token transfers ## Key invariants [#key-invariants] * **Pre-execution validation**: `canTransfer` completes before any balance changes, so failed checks cost only validation gas * **Fail-fast evaluation**: First failing module stops all evaluation — no unnecessary gas spent on subsequent modules * **Atomic state updates**: Post-transfer hooks must all succeed or the entire transfer reverts * **Module ordering matters**: Modules evaluate in configuration order. Place restrictive checks (country, identity) before expensive checks (supply tracking, investor counting) *** ## Failure modes [#failure-modes] | Failure | Cause | Behavior | | ----------------------- | ------------------------------------------------------ | ------------------------------------------------------------ | | Identity not registered | Wallet has no OnchainID mapping | Immediate revert, no module evaluation | | Claim missing | Required claim topic not found on identity | Module returns false, transaction reverts with module reason | | Untrusted issuer | Claim exists but issuer not in trusted registry | Module returns false, same as missing claim | | Expired claim | Claim's expiry timestamp \< block.timestamp | Module returns false, revert with "Claim expired" | | Module limit exceeded | Supply cap, investor count, or holding period violated | Module returns false with specific reason string | | State hook failure | Post-transfer module state update fails | Entire transfer reverts | *** ## See also [#see-also] * [Identity & Compliance](/docs/architecture/security/identity-compliance) — OnchainID architecture and two-layer policy model * [Compliance Modules](/docs/architecture/security/compliance) — full catalog of built-in modules * [Signing Flow](/docs/architecture/flows/signing-flow) — end-to-end transaction signing including custody layer # Digital Asset Initial Offering Source: https://docs.settlemint.com/docs/architecture/flows/daio-offering How DALP's Digital Asset Initial Offering (DAIO) mechanism works architecturally — from offering configuration through investor participation to settlement and lock-up enforcement. ## Overview [#overview] The Digital Asset Initial Offering (DAIO) is DALP's primary distribution mechanism — the structured workflow that moves newly issued digital assets from the issuer into verified investors' custody accounts. It integrates with DALP's compliance, custody, and settlement layers so that every aspect of primary distribution is governed by the same infrastructure that manages the rest of the digital asset lifecycle. A DAIO is not a separate system. It is a capability embedded within the digital asset itself, configured at offering creation and enforced on-chain throughout the offering period. ## How a DAIO fits into the DALP architecture [#how-a-daio-fits-into-the-dalp-architecture] The DAIO module sits between the issuer and the investor, enforcing offering terms while delegating eligibility decisions to the compliance layer and settlement to the atomic settlement engine. ## Offering configuration and atomicity [#offering-configuration-and-atomicity] When an issuer creates a DAIO, all offering parameters — participation limits, accepted settlement currencies, lock-up schedule, early access terms, and legal terms reference — are applied in a single operation. This atomic configuration has two important implications: 1. **No partial state:** The offering cannot exist in a partially configured state. From the moment it is created, it is fully operational and ready to accept investor participation. 2. **Immutable terms:** Core offering parameters cannot be changed after creation. This protects investors by ensuring the terms they participate against remain stable. ## Investor participation flow [#investor-participation-flow] Each participation request goes through the following stages: 1. **Eligibility check** — The compliance layer verifies the investor's identity, jurisdiction, accreditation status, and any other compliance rules configured on the asset. If any check fails, the participation is rejected before any funds move. 2. **Participation limit check** — The DAIO enforces per-investor limits (minimum and maximum). Participations outside these bounds are rejected. 3. **Terms acceptance** — The investor's on-chain acknowledgement of the offering terms is recorded. 4. **Atomic settlement** — The investor's settlement currency and the issuer's digital assets exchange simultaneously. Both legs complete together or neither completes. There is no intermediate state where funds have moved but assets have not, or vice versa. ## Lock-up enforcement [#lock-up-enforcement] If a lock-up schedule is configured on the offering, acquired digital assets are subject to transfer restrictions enforced at the compliance layer: * During the cliff period, no acquired assets can be transferred, regardless of the buyer or the transfer amount. * After the cliff, assets unlock progressively according to the configured vesting schedule. * The lock-up is applied per investor and per acquisition — later participations do not reset the clock on earlier ones. Lock-up enforcement is implemented at the same layer as all other compliance rules. It cannot be bypassed by the investor and requires no operational intervention by the issuer to enforce. ## Soft cap and refund mechanics [#soft-cap-and-refund-mechanics] When a soft cap is configured, the offering tracks total participation throughout the offering period. If the offering closes without reaching the soft cap: * No further participations are accepted * Investors are eligible to reclaim their settlement funds * No digital assets are distributed This protects investors from scenarios where the offering is undersubscribed and the issuer proceeds with an insufficient capital raise. The mechanics are enforced on-chain — refund eligibility cannot be denied if the soft cap is not met. ## Early access phase [#early-access-phase] The early access phase is a time-bounded window before the main offering opens. Its architecture is identical to the main offering, with two additional controls: * **Address allowlist** — Only designated custody account addresses can participate during the early access window. All other participation attempts are rejected. * **Discount pricing** — Early access participants pay a percentage discount relative to the main offering price, calculated at the time of participation. When the early access end time is reached, the allowlist is no longer enforced and the main offering opens to all eligible investors. ## Integration with the broader DALP lifecycle [#integration-with-the-broader-dalp-lifecycle] A DAIO is one phase of the digital asset lifecycle, not a standalone product. After the offering closes: * Issued digital assets remain subject to all ongoing compliance rules (transfer restrictions, jurisdictional controls, holding period requirements) * Lock-up schedules continue to be enforced by the compliance layer * The asset moves into its servicing phase — coupon distributions, yield payments, or redemptions at maturity are handled by DALP's servicing capabilities This continuity — from offering through servicing to redemption — means the issuer manages the entire lifecycle within a single platform, with a single compliance framework and a single custody integration. ## Related [#related] * [Architecture flows](/docs/architecture/flows) * [Token sale capability](/docs/architecture/components/capabilities/token-sale) # Feeds update flow Source: https://docs.settlemint.com/docs/architecture/flows/feeds-update-flow End-to-end lifecycle of publishing, validating, and consuming feed values in DALP, covering both issuer-signed scalar feeds and Chainlink adapter reads. ## Related [#related] * [Feeds system](/docs/architecture/components/infrastructure/feeds-system) — registry, feed types, trust model * [Issuer-Signed Scalar Feed](/docs/architecture/components/capabilities/issuer-signed-scalar-feed) — configuration and signing model * [Signing flow](/docs/architecture/flows/signing-flow) — foundational transaction signing sequence *** ## Flow overview [#flow-overview] * An authorized party (issuer signer or oracle operator) produces a new value for a registered feed * The value is submitted as an on-chain transaction to the feed contract * The feed contract validates the update against its configured rules (signature, timestamp, drift, positivity, decimals) * On success, the feed emits events and stores the value according to its history mode * The chain indexer picks up the events and updates the off-chain database * Consumers read values on-chain (direct contract call or via Chainlink adapter) or off-chain (via Unified API / Asset Console) *** ## Happy path sequence [#happy-path-sequence] 1. **Update origin** — Issuer signer prepares a signed value (value + timestamp + signature) or an oracle operator submits an aggregated data point 2. **Transaction submission** — The signed update is submitted to the feed contract via the Execution Engine and Transaction Signer 3. **Signature verification** — Feed contract recovers the signer and checks it matches the authorized issuer address 4. **Timestamp / nonce check** — Ensures the update is newer than the last accepted value (prevents replay) 5. **Drift validation** — Compares the new value against the previous value; flags as outlier if the change exceeds the configured drift allowance 6. **Positivity check** — Rejects zero or negative values 7. **Decimals consistency** — Ensures the value aligns with the feed's fixed decimal configuration 8. **History mode handling** — Stores the value according to the configured mode: * Latest-only: overwrites the single stored value * Bounded: appends and prunes if limit exceeded * Full: appends to permanent history 9. **Event emission** — Feed contract emits a value-updated event with value, timestamp, sequence number, and outlier flag 10. **Indexer pickup** — Chain indexer detects the event and writes the new value to the off-chain database 11. **On-chain consumer read** — Any contract calls the feed directly (or via a Chainlink adapter) to get the latest value 12. **Off-chain consumer read** — Asset Console and Unified API query the indexed data for display and API responses *** ## Trust & validation checkpoints [#trust--validation-checkpoints] | Checkpoint | What is verified | Provider | Failure outcome | | ---------------------- | -------------------------------------------------------------- | -------------- | ------------------------------------------------- | | **Signature** | Signer matches authorized issuer | Feed contract | Transaction reverts; value rejected | | **Timestamp ordering** | New timestamp > last accepted timestamp | Feed contract | Transaction reverts; replay prevented | | **Drift allowance** | Value change within configured percentage | Feed contract | Value accepted but flagged as outlier | | **Positivity** | Value > 0 | Feed contract | Transaction reverts; zero/negative rejected | | **Decimals** | Value precision matches feed configuration | Feed contract | Transaction reverts; format mismatch | | **Authorization** | Caller holds required role (Feeds Manager or token governance) | FeedsDirectory | Registration/replacement reverts; read unaffected | *** ## Failure modes [#failure-modes] * **Stale feed** — No updates for an extended period. Consumers read the last-known value. Compliance modules may block transfers if staleness exceeds acceptable thresholds. * **Paused feed** — Issuer deliberately stops publishing. Same consumer impact as stale, but intentional. Monitoring should distinguish paused from stale. * **Key compromise** — Issuer's signing key is compromised. Attacker can publish arbitrary values. Mitigation: Feeds Manager removes the compromised feed from the directory and registers a replacement. * **Chain reorg** — A submitted update may be reverted by a chain reorganization. The indexer must handle reorgs by re-processing affected blocks. * **Invalid signature** — Malformed or unauthorized signature. Transaction reverts on-chain; no value is stored. * **Drift exceeded** — Value accepted but flagged as outlier. Consumers with strict tolerance may ignore outlier-flagged values. * **High volume** — Rapid consecutive updates may cause nonce conflicts in the Transaction Signer. The nonce manager serializes submissions to prevent conflicts. * **Adapter target missing** — Chainlink adapter resolves to a removed feed (directory returns zero address). Adapter call reverts; external integrations see failure. * **Indexer lag** — Off-chain database temporarily behind on-chain state. Asset Console and API show slightly stale data until indexer catches up. *** ## Operational signals [#operational-signals] | Signal | Source | Monitoring use | | -------------------- | -------------- | -------------------------------------------------- | | `FeedValueUpdated` | Feed contract | Track update frequency and detect staleness | | `OutlierFlagged` | Feed contract | Alert on drift-exceeded events | | `FeedRegistered` | FeedsDirectory | Audit trail for feed lifecycle changes | | `FeedReplaced` | FeedsDirectory | Detect feed swaps; verify adapter resolution | | `FeedRemoved` | FeedsDirectory | Alert consumers that a feed is no longer available | | Indexer block height | Chain indexer | Detect indexer lag by comparing to chain head | **Alert conditions:** * No `FeedValueUpdated` event for a feed within its expected update cadence * `OutlierFlagged` events exceeding a threshold count within a time window * Indexer lag exceeding acceptable staleness for off-chain consumers *** ## Change impact [#change-impact] | Change | What happens | Who is affected | | ------------------------------ | ---------------------------------------------------------------- | ----------------------------------- | | **Feed replaced in directory** | Adapter automatically resolves to new feed; consumers unaware | External integrations (transparent) | | **Feed removed** | Discovery returns zero address; consumers must handle gracefully | All consumers of that subject+topic | | **Drift allowance changed** | Requires deploying a new feed instance (config is immutable) | Issuer operations | | **History mode changed** | Requires deploying a new feed instance | Consumers relying on history depth | | **Issuer key rotated** | New feed instance with new authorized signer; old feed replaced | Issuer operations, directory admin | | **Adapter redeployed** | External integrations must update their pointer address | All external consumers (breaking) | *** ## Sequence diagram [#sequence-diagram] ```mermaid sequenceDiagram participant Issuer as Issuer Signer participant EE as Execution Engine participant Feed as Feed Contract participant Dir as FeedsDirectory participant Idx as Chain Indexer participant App as Consumer App Issuer->>EE: Submit signed value EE->>Feed: Write transaction Feed->>Feed: Verify signature Feed->>Feed: Validate timestamp, drift, positivity Feed->>Feed: Store value (history mode) Feed-->>Idx: Emit FeedValueUpdated event Idx->>Idx: Index event to off-chain DB App->>Dir: Query (subject, topic) Dir-->>App: Feed contract address App->>Feed: Read latest value Feed-->>App: Value + signature + timestamp ``` **Question answered:** What steps does a feed value go through from issuer to consumer? **Participants:** Issuer Signer and Consumer App are external actors; Execution Engine, Feed Contract, FeedsDirectory, and Chain Indexer are [infrastructure components](/docs/architecture/components/infrastructure). **Key takeaways:** 1. All writes go through the Execution Engine for reliable transaction management 2. Validation is enforced on-chain — invalid updates never reach storage 3. Consumers discover feeds through the directory, never hard-coding feed addresses *** ## See also [#see-also] * [Feeds system](/docs/architecture/components/infrastructure/feeds-system) — registry, Chainlink adapter, trust model * [Issuer-Signed Scalar Feed](/docs/architecture/components/capabilities/issuer-signed-scalar-feed) — configuration and signing model * [Signing flow](/docs/architecture/flows/signing-flow) — foundational transaction signing used by feed updates * [Chain indexer](/docs/architecture/components/infrastructure/chain-indexer) — how events reach the off-chain database # Overview Source: https://docs.settlemint.com/docs/architecture/flows Index of the primary operational flows in the Digital Asset Lifecycle Platform, covering platform-level transaction sequences and capability-specific distribution and settlement workflows. This section documents the end-to-end operational flows that orchestrate digital asset lifecycle events across DALP's four platform layers. ## Platform flows [#platform-flows] Platform flows are foundational sequences that underpin all DALP operations. Every capability flow builds on one or more of these. | Flow | Trigger | Outcome | | ------------------------------------------------------------------- | ---------------------------------- | --------------------------------------------------------------------- | | [Signing flow](/docs/architecture/flows/signing-flow) | Any blockchain write operation | Transaction signed by custody provider and broadcast to network | | [Asset issuance](/docs/architecture/flows/asset-issuance) | Issuer creates a new digital asset | Token contract deployed, compliance configured, initial supply minted | | [Compliance transfer](/docs/architecture/flows/compliance-transfer) | Token holder initiates transfer | Transfer validated against compliance rules and executed on-chain | | [Feeds update flow](/docs/architecture/flows/feeds-update-flow) | Issuer publishes signed price data | Value validated, stored, indexed, and available to consumers | ## Capability flows [#capability-flows] Capability flows implement specific business operations. Each composes platform flows with additional domain logic for distribution, settlement, or offering management. | Flow | Trigger | Outcome | | ----------------------------------------------------------------------- | --------------------------------------------- | ---------------------------------------------------------------- | | [DAIO offering](/docs/architecture/flows/daio-offering) | Issuer opens a Digital Asset Initial Offering | Investors subscribe, tokens allocated, lock-up enforced | | [Airdrop distribution](/docs/architecture/flows/airdrop-distribution) | Issuer initiates airdrop to holder list | Tokens distributed to eligible wallets in batch | | [Treasury distribution](/docs/architecture/flows/treasury-distribution) | Scheduled or manual distribution trigger | Settlement currency paid from asset treasury to investors | | [XvP settlement](/docs/architecture/flows/xvp-settlement) | Settlement instruction submitted | Atomic exchange of tokens against payment between counterparties | ## How flows relate to platform layers [#how-flows-relate-to-platform-layers] Each flow traverses multiple layers. The Asset Console or Unified API receives the initial request. The DALP Execution Engine orchestrates the multi-step sequence with durable state. The SMART Protocol enforces on-chain invariants. Custody providers sign and broadcast the final transactions. Flows are the primary unit of reliability analysis. Each flow page documents where failures can occur and how the Execution Engine recovers. ## See also [#see-also] * [DALP Execution Engine](/docs/architecture/components/infrastructure/execution-engine) for the orchestration runtime * [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) for on-chain enforcement * [Signing flow](/docs/architecture/flows/signing-flow) as the foundational transaction sequence * [Unified API](/docs/architecture/components/platform/unified-api) for programmatic flow triggers * [Observability](/docs/architecture/operability/observability) for flow monitoring # Signing Flow Source: https://docs.settlemint.com/docs/architecture/flows/signing-flow End-to-end transaction signing sequence in DALP, from on-chain compliance validation through the unified signer interface to custody providers such as DFNS, Fireblocks, and Luna HSM, including provider policy evaluation and blockchain broadcast. **See also:** [Key Guardian](/docs/architecture/components/infrastructure/key-guardian) | [Transaction Signer](/docs/architecture/components/infrastructure/transaction-signer) | [Identity & compliance](/docs/architecture/security/identity-compliance) *** ## Overview [#overview] Every DALP transaction passes through two independent policy layers before reaching the blockchain: 1. **On-chain compliance** --- the SMART Protocol verifies identity claims, transfer restrictions, and supply limits via simulation. 2. **Custodian policy** --- the configured custody provider applies operational controls such as amount limits, multi-party approval, or HSM quorum before signing. Neither layer can be bypassed independently. A transaction must pass both to complete. ## End-to-end sequence [#end-to-end-sequence] ## Flow steps [#flow-steps] 1. **Initiate transaction** --- The operation enters the Execution Engine, which builds the payload, estimates gas, and assigns a nonce. No signing or state change occurs yet. 2. **On-chain compliance pre-check** --- The engine simulates via `eth_call` against the SMART Protocol's `canTransfer`. This checks identity claims, compliance modules, and amount/volume limits without spending gas. If simulation reverts, the failure surfaces immediately. 3. **Unified signer interface** --- The Transaction Signer delegates to a provider-agnostic layer abstracting over local keys, DFNS, Fireblocks, and Luna HSM. Switching providers requires configuration, not a different transaction flow. 4. **Custody provider signing** --- The active provider evaluates its own policy rules before signing. MPC providers combine key shares. Luna HSM signs inside the hardware partition and can return a pending approval state while quorum approval is completed. 5. **Broadcast and on-chain execution** --- The signed transaction is submitted via `eth_sendRawTransaction`. The compliance engine enforces `canTransfer` again on-chain. If compliance state changed between simulation and broadcast, the transaction reverts. ## Two-layer policy model [#two-layer-policy-model] | Layer | Where enforced | What it controls | Configured by | | ----------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------ | | **On-chain compliance** | SMART Protocol contracts | Identity/KYC claims, country restrictions, blocklists, supply caps, investor counts, time locks, volume modules | Issuer / compliance manager via DALP API | | **Custodian policies** | DFNS policy engine, Fireblocks TAP, or Luna HSM quorum controls | Per-transaction amount limits, rolling spend limits, approver workflows, IP/time restrictions, destination allowlists, HSM quorum approval | Operations team in the provider or HSM control surface | **Key invariants:** * Layer 1 (on-chain) enforces regulatory compliance at protocol level and cannot be bypassed off-chain * Layer 2 (custodian) provides operational controls and approval workflows at infrastructure level * Both layers must pass for a transaction to complete * On-chain amount limits (via custom compliance modules) are auditable on-chain; custodian limits are off-chain operational controls ## Failure modes [#failure-modes] | Failure point | Cause | Resolution | | ------------------------ | ------------------------------------------------------------------------------- | --------------------------------------------------------- | | Simulation revert | On-chain compliance module blocked the transaction | Check compliance status, claims, and module configuration | | Custodian policy block | Transaction exceeds custodian amount limit, provider rule, or HSM quorum policy | Adjust policy thresholds or request approval | | Pending approval timeout | Approvers have not actioned the provider or HSM request | Escalate or configure auto-reject after timeout | | Signing failure | Network, provider, or HSM partition issue | Automatic retry with exponential backoff | | Broadcast failure | Gas underpricing or nonce conflict | Transaction Signer resubmits with increased gas | | On-chain revert | Compliance state changed between simulation and broadcast | Surface revert reason; re-evaluate compliance | ## See also [#see-also] * [Key Guardian](/docs/architecture/components/infrastructure/key-guardian) --- key storage backends including DFNS, Fireblocks, and HSM-backed signing * [Transaction Signer](/docs/architecture/components/infrastructure/transaction-signer) --- gas management, nonce coordination, and retry logic * [Identity & compliance](/docs/architecture/security/identity-compliance) --- on-chain compliance modules including amount and volume controls * [Chain Gateway](/docs/architecture/components/infrastructure/chain-gateway) --- EVM RPC node access and transaction broadcast # Treasury Distribution Source: https://docs.settlemint.com/docs/architecture/flows/treasury-distribution How DALP's treasury payment architecture enables digital assets to act as their own payment source for automated lifecycle distributions to investors. ## Overview [#overview] DALP's treasury payment capability integrates settlement currency management directly into the digital asset. Rather than relying on external treasury contracts or manual payment processes, the asset holds settlement currency and distributes it to investors automatically when lifecycle events trigger a payment. This design makes treasury operations a first-class component of the digital asset lifecycle --- not a separate system bolted on after issuance. ## Architectural model [#architectural-model] The key design elements are: 1. **Asset-integrated treasury** --- The asset contract holds settlement currency directly. There is no external treasury contract to deploy or manage. 2. **Feature-gated payment authorization** --- Only registered lifecycle features can request payment from the treasury. Unauthorized payment requests are rejected. 3. **Per-investor distribution** --- The distribution engine calculates each investor's share based on their current holding and dispatches currency to their custody account. 4. **On-chain record** --- Every distribution event is recorded immutably, providing a complete audit trail. ## Payment authorization [#payment-authorization] The treasury does not accept payment requests from arbitrary sources. Authorization works through a registered feature list: This authorization model ensures that only approved lifecycle operations can trigger distributions, protecting treasury funds from unauthorized depletion. ## Lifecycle features as payment triggers [#lifecycle-features-as-payment-triggers] Payment features are registered on the asset at configuration time. Each feature type encodes the business logic for when and how payments are calculated: | Feature type | Trigger | Amount calculation | | ------------------- | ---------------------------------------------- | ------------------------------------------------- | | Yield distribution | Scheduled interval (daily, monthly, quarterly) | Rate x investor holding x elapsed period | | Maturity redemption | Asset maturity date | Principal amount per unit x investor holding | | Coupon payment | Scheduled coupon date | Fixed coupon rate x face value x investor holding | Features are not activated globally --- each must be explicitly registered on the specific asset where it will operate. This prevents features from being applied to assets they were not designed for. ## Settlement currency model [#settlement-currency-model] The asset treasury holds settlement currency --- a specific payment currency designated at asset configuration time. This is distinct from the digital security itself: * **Digital security** --- The tokenized instrument (bond, equity, fund unit) that investors hold * **Settlement currency** --- The payment medium (typically a regulated stablecoin or fiat-linked currency) used for distributions The treasury balance must be funded by the issuer before distribution events occur. If the treasury balance is insufficient at payment time, the distribution does not execute --- partial payments are not made. This protects investors from receiving only a fraction of their entitled distribution. ## Auto-detection of treasury capability [#auto-detection-of-treasury-capability] DALP automatically detects whether an asset has treasury payment capability or relies on an external custody account for payments. This detection happens at the time of payment initiation, allowing assets to be deployed without treasury capability and upgraded later if needed. ## On-chain auditability [#on-chain-auditability] Every payment event is recorded on-chain, capturing: * Timestamp of distribution * Total amount distributed * Number of recipients * Per-recipient amounts * The lifecycle feature that triggered the payment This record is immutable and cannot be altered after the fact. It is the authoritative source for investor reporting, regulatory submissions, and audit inquiries. ## Integration with DALP lifecycle [#integration-with-dalp-lifecycle] Treasury payment capability is one component of DALP's broader asset lifecycle: * **Before distributions:** Asset issuance, investor onboarding, compliance verification (handled by issuance and compliance modules) * **At distribution:** Eligibility checked against current compliance rules; treasury distributes to eligible holders * **After maturity:** Final redemption distribution triggered; asset moves to retired status The compliance layer that governs transfers also governs distribution eligibility --- the same investor identity and rule framework applies throughout the lifecycle. ## Related [#related] * [Architecture flows](/docs/architecture/flows) * [Vault capability](/docs/architecture/components/capabilities/vault) # XvP Settlement Flow Source: https://docs.settlemint.com/docs/architecture/flows/xvp-settlement How the DALP XvP settlement flow executes atomic multi-party token exchanges -- from settlement creation through approval collection, optional hashlock coordination for cross-chain legs, to all-or-nothing execution. ## Related [#related] * [XvP Settlement](/docs/architecture/components/capabilities/xvp-settlement) -- contracts, roles, and configuration * [Signing Flow](/docs/architecture/flows/signing-flow) -- transaction signing and custody * [Compliance Transfer](/docs/architecture/flows/compliance-transfer) -- transfer compliance checks *** ## Flow overview [#flow-overview] The XvP (Exchange versus Payment) settlement flow coordinates atomic token exchanges between multiple parties. "Atomic" means all token transfers in a settlement succeed together or all revert together -- there is no intermediate state where some parties have received tokens and others have not. For settlements involving external chains, a hashlock mechanism coordinates execution across networks. XVP settlement setup ## Managing settlements [#managing-settlements] Operators can manage XvP settlements through the Asset Console, API, or CLI: * **Asset Console:** list settlements for an XvP add-on factory, open a settlement detail page, review flows and approvals, and run eligible actions such as approve, revoke approval, execute, cancel, withdraw expired settlements, decrypt stored secrets, and reveal secrets. * **API:** use the XvP settlement endpoints to create, list, read, approve, revoke approval, execute, cancel, withdraw cancellation requests, withdraw expired settlements, reveal a secret, or decrypt a stored settlement secret. * **CLI:** use the `xvp-settlements` command group for the same operational surface when automating or testing settlement workflows from a terminal. Use the same business checks before each channel: confirm the settlement address, participants, flow amounts, expiration, approval status, and whether the settlement is local-only or uses hashlock coordination for external-chain legs. ## Happy path -- local settlement [#happy-path----local-settlement] 1. **Initiator creates settlement** -- the settlement contract is deployed with a set of flow definitions. Each flow specifies a sender, receiver, token address, and amount. An expiration timestamp is set at creation and is immutable. 2. **Senders grant ERC20 allowances** -- each sender in a local flow calls `approve()` on the relevant token contract, granting the settlement contract permission to transfer their tokens. 3. **Senders approve the settlement** -- each sender calls `approve()` on the settlement contract itself, signaling readiness to execute. Approvals can be revoked before execution. 4. **Execution gate satisfied** -- once all local senders have approved (and the hashlock is satisfied, if applicable), the settlement is eligible for execution. If auto-execution is enabled, this triggers immediately. 5. **Atomic token transfers** -- all local flows execute in a single transaction. The settlement contract calls `transferFrom` for each flow. If any individual transfer fails (insufficient allowance, compliance revert, or other ERC20 error), the entire transaction reverts. 6. **Settlement marked executed** -- an on-chain record confirms the settlement completed. No further approvals or executions are possible. ## Cross-chain hashlock lifecycle [#cross-chain-hashlock-lifecycle] When a settlement includes flows with `externalChainId != 0`, the hashlock mechanism coordinates execution across chains: 1. **Settlement created with hashlock** -- the initiator provides a `hashlock` (the hash of a secret) at creation time. This is required when any flow targets an external chain. 2. **External HTLC deployment** -- counterparties on external chains deploy Hash Time-Locked Contracts using the same hashlock. These HTLCs lock the external-chain tokens, mirroring the flows defined in the settlement. 3. **Secret revealed on external chain** -- when the external HTLC executes, the secret (preimage) becomes public on-chain. 4. **Secret relayed to settlement** -- anyone calls `revealSecret(bytes secret)` on the settlement contract. This function is permissionless -- no single party can block settlement by withholding the secret once it is public. 5. **Execution proceeds** -- with both the hashlock satisfied and all local approvals collected, the settlement executes atomically as described in the local flow above. For pure local settlements where all flows have `externalChainId = 0`, the hashlock is optional and the execution gate requires only local approvals. ## Approval management [#approval-management] * **Per-sender scope** -- each sender approves the entire settlement, not individual flows. A sender appearing in multiple flows provides one approval. * **Revocation** -- any sender can revoke their approval before execution by calling `revoke()`. This resets their approval status without affecting other senders. * **Auto-execution** -- when the auto-execution flag is set at creation, the settlement executes automatically the moment all gates are satisfied. Without auto-execution, a separate `execute()` call is required after all conditions are met. Active settlement transaction monitoring ## Failure modes [#failure-modes] * **Settlement expired** -- the expiration timestamp has passed. No further approvals or execution attempts are accepted. Funds remain with their original owners. * **Insufficient ERC20 allowance** -- a sender's token allowance is less than the flow amount. The execution transaction reverts atomically; no partial transfers occur. * **Missing local approvals** -- not all senders have approved. Execution is blocked until every local sender has called `approve()`. * **Hashlock not satisfied** -- for cross-chain settlements, the secret has not been revealed on the settlement contract. Execution is blocked until `revealSecret()` is called with the correct preimage. * **Token transfer failure** -- if any individual `transferFrom` call fails (e.g., a compliance-enabled token rejects the transfer), the entire settlement transaction reverts. All tokens remain with their original owners. XvP settlement contracts are available and can be operated through the Asset Console, API, and CLI. Local flows execute atomically on the current chain. External-chain legs use hashlock coordination and require the matching external-chain workflow to reveal the shared secret before local execution can proceed. ## Related resources [#related-resources] * [XvP Settlement](/docs/architecture/components/capabilities/xvp-settlement) -- contracts, roles, and configuration * [Signing Flow](/docs/architecture/flows/signing-flow) -- how transactions are signed and broadcast * [Compliance Transfer](/docs/architecture/flows/compliance-transfer) -- transfer validation for compliance-enabled tokens * [Capabilities overview](/docs/architecture/components/capabilities) -- how capabilities extend the platform # Compliance Providers Source: https://docs.settlemint.com/docs/architecture/integrations/compliance-providers Explanation of DALP compliance-provider intake, including ClaimSource, provider issuer EOAs, webhook verification, and the boundary between persistent claims and per-transaction decisions. ## Purpose [#purpose] DALP compliance providers turn events from external KYC, KYB, and wallet-monitoring systems, including [Sumsub](https://docs.sumsub.com/) and [Elliptic](https://developers.elliptic.co/), into standard on-chain claims. The provider-specific API calls and webhook formats stay behind an intake layer, while the claim that affects compliance remains a normal DALP claim issued by a trusted issuer. For setup steps, see [onboard a compliance provider](/docs/developer-guides/compliance/onboarding-a-provider). For exact endpoints and fields, see [compliance provider API reference](/docs/developer-guides/compliance/compliance-provider-api-reference). ## Current coverage boundary [#current-coverage-boundary] Current DALP compliance-provider adapters cover provider events that can be reduced to durable identity, business, or wallet claims through ClaimSource: * Identity and KYB verdicts from configured KYC/KYB providers * Monitoring alerts from applicant, entity, and wallet-monitoring providers * Wallet-monitoring alerts from Elliptic For the current default DALP integration surface, [Notabene](https://devx.notabene.id/) Travel Rule transfer gating is a separate per-transfer decision surface rather than a compliance-provider adapter. [Chainalysis KYT](https://docs.chainalysis.com/) is also not a current DALP compliance-provider adapter. ## Provider intake model [#provider-intake-model] Compliance integrations split by what the provider produces and how long the subject lives: | Surface | Provider shape | DALP behaviour | | ------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | A | Identity verdicts | Sumsub, Jumio, Middesk, Onfido, Persona, Trulioo, and Veriff terminal verdicts issue or remove on-chain claims for one topic | | B | Monitoring alerts | Sumsub AML watchlist events, Sumsub applicant-on-hold events, ComplyAdvantage entity monitoring, and Elliptic wallet alerts are normalised to severity scores for claim revocation | | C | Per-transaction Travel Rule | Outside the current dapp compliance-provider flow | ClaimSource covers identity or wallet subjects that persist beyond one transaction. Sumsub applicant-review events produce identity verdicts, Sumsub AML watchlist events, Sumsub applicant-on-hold events, and ComplyAdvantage monitored-search events produce monitoring alerts, and Elliptic produces wallet-monitoring alerts. TransferGate is reserved for Travel Rule and similar per-transaction decisions, where the subject is one transfer rather than a durable identity or wallet. ## Provider issuer trust model [#provider-issuer-trust-model] Each integration provisions one tenant-scoped, DALP-custodied EOA for one provider and one claim topic. DALP registers that EOA as a trusted issuer in the tenant's trusted issuer registry for that topic. The integration then uses the EOA to sign claims emitted by provider intake. The on-chain attestor of a compliance-issued claim is the provider integration EOA, not DALP itself and not the tenant compliance officer. An audit can identify which integration asserted the claim, retrieve the original provider payload, and verify the trusted-issuer registration history from on-chain events. This design keeps provider-driven claims inside DALP's existing trusted-claim primitive. There is no separate compliance-provider trust store. ## Webhook intake boundary [#webhook-intake-boundary] Each integration receives one webhook URL that the tenant pastes into the provider dashboard: ```text /api/webhooks/compliance/// ``` The integration identifier scopes the URL to one tenant integration. The URL token is a non-guessable UUID generated when the tenant creates the integration. HMAC remains the primary authentication mechanism; the token limits accidental cross-integration delivery and adds a defence-in-depth layer against URL leaks. DALP verifies each provider's webhook authentication material before processing the event. HMAC providers sign the raw request body; Jumio uses callback Basic Auth plus a configured IP allowlist. DALP records raw provider payloads for audit and extracts decision-driving fields such as subject, topic, severity, applied timestamp, and outcome for query and claim processing. | Provider | Webhook authentication | Header or network source | | --------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ | | Sumsub | HMAC over raw body | `x-payload-digest`, `x-payload-digest-alg` | | Sumsub AML | HMAC over raw body | `x-payload-digest`, `x-payload-digest-alg` | | ComplyAdvantage | HMAC-SHA256 digest | `x-complyadvantage-signature` | | Elliptic | HMAC-SHA256 digest | `x-elliptic-signature` | | Jumio | Basic Auth + IP allowlist | `Authorization: Basic …` and callback source IP | | Middesk | HMAC-SHA256 over raw body | `X-Middesk-Signature-256` | | Onfido | HMAC-SHA256 over raw body | `X-SHA2-Signature` | | Persona | HMAC-SHA256 over `${timestamp}.${rawBody}` | `Persona-Signature` timestamp and hexadecimal signature values (space-separated sets accepted during key rotation) | | Trulioo | HMAC-SHA256 over raw body | `x-trulioo-signature` | | Veriff | HMAC-SHA256 over raw body | `X-HMAC-SIGNATURE` | ## Subject mapping [#subject-mapping] Provider webhooks can only affect a DALP subject after that subject has been mapped to the integration. * Sumsub, Jumio, Onfido, Persona, Trulioo, and Veriff applicant intake, plus Middesk business intake, map the provider subject to a DALP identity address. * ComplyAdvantage search intake maps a monitored person or company search to its DALP identity. * Elliptic wallet intake maps the monitored wallet to its DALP identity. If a webhook arrives for an unmapped subject, DALP records the event for audit but does not produce an on-chain claim effect. Map the subject before provider delivery; the same delivered provider event is not replayed automatically after mapping. ## Why the two intake models stay separate [#why-the-two-intake-models-stay-separate] ClaimSource works with durable subjects: identities and wallets. It receives background provider events and emits standard on-chain claim issue or revoke transactions. TransferGate would work with one transfer at transfer-initiation time. That is a different subject, timing model, and output. Combining both models would make the persistent-claim path harder to reason about and would expose per-transaction decisions inside an event-driven claim model. This boundary keeps the model clear. Current DALP compliance-provider integrations support these event families through ClaimSource: * Sumsub identity verdicts * Sumsub AML watchlist monitoring alerts for existing applicants * Sumsub applicant-on-hold monitoring alerts * ComplyAdvantage entity monitoring alerts * Elliptic wallet monitoring * Jumio identity verdicts * Middesk KYB verdicts, attested to `knowYourCustomer` as DALP's current KYB umbrella topic * Onfido Workflow Studio and classic API identity verdicts, attested to `knowYourCustomer` * Persona inquiry verdicts, attested to `knowYourCustomer` * Trulioo DataVerify identity and business verdicts, attested to `knowYourCustomer` * Veriff hosted identity-verification verdicts, attested to `knowYourCustomer` For the current default DALP integration surface, [Notabene](https://devx.notabene.id/) Travel Rule transfer gating remains a separate per-transfer surface rather than a compliance-provider adapter. A deployment that needs Travel Rule gating should integrate a per-transfer adapter instead of folding that decision into ClaimSource. [Chainalysis KYT](https://docs.chainalysis.com/) is also not a current DALP compliance-provider adapter. ## See also [#see-also] * [Choose a KYC issuance path](/docs/developer-guides/compliance/choose-kyc-issuance-path) * [Onboard a compliance provider](/docs/developer-guides/compliance/onboarding-a-provider) * [Map compliance-provider subjects](/docs/developer-guides/compliance/compliance-provider-subjects) * [Compliance provider API reference](/docs/developer-guides/compliance/compliance-provider-api-reference) * [Provider issuer EOA rotation](/docs/runbooks/rotate-provider-issuer-eoa) * [Identity and compliance](/docs/architecture/security/identity-compliance) # Custody Providers Source: https://docs.settlemint.com/docs/architecture/integrations/custody-providers Architecture overview of DALP's custody provider integrations covering browser-wallet verification boundaries, DFNS and Fireblocks MPC signing, Luna HSM partition signing, policy engines, configuration requirements, and the unified signer boundary that keeps DALP signing workflows consistent while provider-specific approval and wallet behavior stays behind the adapter. DALP separates application-user browser-wallet verification from institutional custody provider integrations. DFNS and Fireblocks provide MPC custody. Luna HSM provides hardware-backed partition signing. Provider approvals stay inside the configured custody or HSM control plane, while DALP uses a common signer boundary for EVM transaction requests. The comparison below separates browser-wallet verification, provider-owned custody policy, and HSM quorum activation so implementation teams can choose the signing model that matches their control requirements. ## Related [#related] * [Key Guardian](/docs/architecture/components/infrastructure/key-guardian) * [Signing Flow](/docs/architecture/flows/signing-flow) * [Transaction Signer](/docs/architecture/components/infrastructure/transaction-signer) * [Supported Networks](/docs/architecture/integrations/supported-networks) * [DFNS developer documentation](https://docs.dfns.co/) * [Fireblocks developer documentation](https://developers.fireblocks.com/) * [Thales Luna HSM documentation](https://cpl.thalesgroup.com/docs) ## Browser-wallet verification [#browser-wallet-verification] Browser wallets serve application-user onboarding and transaction-verification flows. Passkey challenge and verification is controlled by the per-auth-source session state: `walletPasskeyEnabled` is persisted with the authenticated user and the passkey endpoints require it before issuing or verifying transaction-signing challenges. Changing the wallet provider does not by itself enable passkeys. This path is distinct from institutional MPC custody. Browser-wallet verification gives users a platform-managed authorization boundary, while DFNS and Fireblocks provide provider-managed custody, wallet organization, and policy approval flows for higher-control signing setups. ## Provider comparison [#provider-comparison] | Capability | DFNS | Fireblocks | Luna HSM | | ----------------------------- | -------------------------- | --------------------------------------------- | -------------------------------------------- | | Custody model | Threshold MPC | MPC-CMP with continuous key refresh | Thales Luna 7 HSM partition signing | | Policy engine | DFNS Policy Engine | Transaction Authorization Policy (TAP) | HSM quorum controls | | Mobile approval | No | Yes (Fireblocks app) | No DALP-managed mobile approval path | | Provider policy decision flow | Provider-owned status flow | Console / Co-Signer only | Out-of-band quorum activation | | API model | REST API | REST API | PKCS#11 library integration | | Wallet model | Flat wallet list | Vault account hierarchy | HSM key labels scoped by organization/wallet | | DALP signing use | Configured EVM wallets | Fireblocks vault wallets for EVM transactions | Hardware-backed EVM signing | DFNS and Fireblocks use MPC signing so that no single private key ever exists in one place. Luna HSM signs inside a configured hardware partition and can surface pending approval while M-of-N quorum activation completes. Custody provider support does not change DALP network compatibility: DALP signing workflows target the EVM-compatible networks documented in [Supported Networks](/docs/architecture/integrations/supported-networks). The fundamental difference is operational: custody providers own their policy or quorum decision flows. DALP's signer contract uses pending, approved, denied, expired, or blocked status vocabulary, and each provider adapter exposes the subset it can observe. ## DFNS integration [#dfns-integration] DFNS provides delegated MPC custody where key shards distribute across DFNS infrastructure. DALP connects through a service account with the following configuration: | Setting | Description | | --------------- | ----------------------------------------- | | API URL | DFNS service endpoint | | Organization ID | Tenant identifier | | Auth token | Service account authentication | | Credential ID | Identifies the signing credential | | EC private key | Elliptic curve key for API authentication | ### Policy enforcement [#policy-enforcement] The DFNS policy engine evaluates transaction rules before MPC signing proceeds. Policies support auto-sign rules, amount thresholds, IP/time restrictions, and multi-party approval requirements. When a DFNS policy requires approval, DFNS owns the decision flow; DALP signer approval surfaces use the canonical pending, approved, denied, or expired statuses, while provider-native broadcast failure, provider denial, or polling timeout are treated as errors rather than extra status strings. ### Audit integration [#audit-integration] DFNS audit logs synchronize with DALP audit records, providing unified compliance reporting across both the platform and custody layers. ## Fireblocks integration [#fireblocks-integration] Fireblocks provides institutional MPC-CMP custody through vault accounts. Key material distributes across Fireblocks infrastructure and the customer's co-signer node, with continuous key refresh eliminating static key shares. | Setting | Description | | --------------- | ----------------------------------------- | | API key | From the Fireblocks Console | | RSA private key | PEM format, for API authentication | | API endpoint | Production or sandbox URL (auto-detected) | ### Vault-based wallet model [#vault-based-wallet-model] Fireblocks organizes keys into vault accounts, each containing one or more asset wallets. DALP supports creating vault accounts, activating asset wallets, and querying vaults across the organization. ### Transaction authorization policy (TAP) [#transaction-authorization-policy-tap] Fireblocks enforces custodian-level policies through TAP rules that evaluate before signing. Rules cover transaction amount thresholds, whitelisted destination addresses, velocity limits, and multi-approver requirements. When a TAP rule blocks a transaction, DALP can reflect a pending or blocked provider status while polling Fireblocks for the provider outcome. Fireblocks does not support programmatic approval resolution through external APIs, so blocked transactions must be approved through the Fireblocks Console or a Co-Signer appliance; DALP then reflects approved or denied provider results, while provider expiry and polling timeout are treated as error paths instead of extra status literals. ## Luna HSM integration [#luna-hsm-integration] Luna HSM support uses a configured Thales Luna 7 partition for hardware-backed EVM signing. DALP loads the Luna signer through the same signer configuration surface as the other providers, but the Luna adapter requires a PKCS#11 library path, a non-empty token label, and a secret reference for the partition PIN. Optional client certificate and key references can also be supplied through the secrets backend. | Setting | Description | | ----------------- | ------------------------------------------------------------- | | PKCS#11 library | Local library path used to communicate with the Luna module | | Token label | HSM partition label; blank labels are rejected at config load | | PIN reference | Secret reference for the partition Crypto User PIN | | Client cert/key | Optional secret references for client certificate material | | Quorum retry | Window used while waiting for M-of-N quorum activation | | Operation timeout | Per-operation timeout for blocking PKCS#11 calls | ### Quorum and pending approval [#quorum-and-pending-approval] Luna signing runs in sign-only approval mode. When the partition is waiting for M-of-N quorum activation, DALP records a provider pending state and keeps polling until the signing attempt succeeds, expires, or is classified as blocked. The approval is completed out of band through the Luna control process; DALP does not provide a browser or API approval button for the HSM quorum itself. ## Signer approval and polling behavior [#signer-approval-and-polling-behavior] Custody-provider approval is separate from DALP smart-wallet multisig approval. If a provider-native broadcast returns `pending`, DALP keeps the transaction in the broadcast workflow while polling the active signer adapter for provider status; it does not persist the smart-wallet `pending approval` transaction state for DFNS or Fireblocks custody-policy decisions. DFNS broadcast polling treats broadcast or confirmation as approval; provider failure, denial, and polling timeout are treated as errors rather than extra status strings. Fireblocks polling checks pending or blocked approval status until the provider reports approval or denial; provider expiry and polling timeout are treated as error paths rather than recorded as status literals. Luna polling tracks HSM quorum activation until the partition signs, the request expires, or DALP classifies the pending state as blocked. Smart-wallet multisig approvals use DALP's smart-wallet approval endpoints for UserOperation approval collection. Those endpoints do not approve or reject custody-provider policy decisions or Luna HSM quorum activation. ### System setup and batch requests [#system-setup-and-batch-requests] For DFNS sign-only or nonce-reservation paths, approval-pending conflicts can pause setup operations that register token factories or add-on factories. DALP surfaces those DFNS-shaped `CUSTODY_APPROVAL_PENDING` cases as retryable HTTP 409 conflicts for the setup request instead of converting the batch into a permanent validation failure. Fireblocks setup requests do not use the DFNS `CUSTODY_APPROVAL_PENDING` marker. Fireblocks follows its own `pending_approval` status-polling contract through `getPendingApproval`, so clients should not expect a retryable HTTP 409 setup-request conflict for Fireblocks TAP approval. Safe setup retries require the client to send an `Idempotency-Key` header on the original request and reuse the same value on retry. If the client omits the header, DALP generates a per-request key for server-side tracking, but that generated value is not returned to the caller for later reuse. After the DFNS approval or nonce reservation clears, retry the same request with the original client-provided key so DALP resumes from the tracked transaction state rather than creating a duplicate setup operation. ## Unified signer abstraction [#unified-signer-abstraction] The unified signer interface abstracts over the configured custody backend. For DALP consumers, signing requests follow the same platform workflow while provider-specific approval and wallet behavior remains behind the signer adapter. This abstraction boundary means: * **Platform workflows** call the signer interface without knowing which provider handles the request * **Adding a new provider** requires implementing the signer adapter, not changing platform flows * **Provider-specific behavior** (approval model differences, vault hierarchies) is encapsulated behind the adapter See the [Signing Flow](/docs/architecture/flows/signing-flow) for the complete end-to-end sequence showing how the signer interface delegates to the active provider. ## Storage tiers [#storage-tiers] Custody providers represent the highest tier in DALP's key storage hierarchy. Organizations select the appropriate tier based on asset value and regulatory requirements: | Tier | Protection | Use case | | --------------------------------------- | ---------------------------- | -------------------------------- | | Encrypted database | Application-level encryption | Development and low-value assets | | Cloud secret manager | Platform-managed encryption | Standard production deployments | | Hardware security module | FIPS 140-2 Level 3 | Regulated financial services | | Third-party custody (DFNS / Fireblocks) | Delegated institutional MPC | Highest security requirements | Each tier escalates protection. The Key Guardian routes signing requests to the appropriate backend based on key metadata. See [Key Guardian](/docs/architecture/components/infrastructure/key-guardian) for the full storage hierarchy and routing architecture. ## See also [#see-also] * [Key Guardian](/docs/architecture/components/infrastructure/key-guardian) for the complete key storage and routing architecture * [Signing Flow](/docs/architecture/flows/signing-flow) for the end-to-end transaction signing sequence * [Authentication](/docs/architecture/security/authentication) for API-level security controls # Overview Source: https://docs.settlemint.com/docs/architecture/integrations Overview of DALP's integration architecture covering custody provider connectivity, supported blockchain networks, and the platform's approach to external system boundaries. DALP connects to custody providers, blockchain networks, compliance providers, and market data sources through defined integration boundaries. This section describes those boundaries and the pages that document each integration surface. ## Integration posture [#integration-posture] DALP isolates external dependencies behind internal abstraction boundaries. The Key Guardian abstracts custody providers. The Chain Gateway abstracts blockchain networks. The Feeds system abstracts market data sources. This design allows providers and networks to change without affecting platform workflows or API contracts. Each integration has a defined contract surface: * **Inbound contracts** specify what DALP expects from the external system (API versions, authentication, response formats) * **Outbound contracts** specify what DALP exposes to external systems, including API response formats and transaction status formats. * **Stability promises** define versioning, deprecation windows, and backward compatibility guarantees ## Section pages [#section-pages] | Page | Description | | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [Custody providers](/docs/architecture/integrations/custody-providers) | Supported MPC custody solutions ([DFNS](https://docs.dfns.co/), [Fireblocks](https://developers.fireblocks.com/)), integration architecture, and policy evaluation | | [Compliance providers](/docs/architecture/integrations/compliance-providers) | [Sumsub](https://docs.sumsub.com/) and [Elliptic](https://developers.elliptic.co/) ClaimSource intake, provider-as-issuer trust, and webhook contracts | | [Supported networks](/docs/architecture/integrations/supported-networks) | EVM-compatible blockchain networks, RPC configuration, and network-specific considerations | API integration monitoring and observability ## Key design decisions [#key-design-decisions] * **Provider-agnostic signing**: The unified signer interface accepts signing requests without coupling to a specific custody provider. Adding a new provider requires implementing the signer adapter, not changing platform flows. * **Network abstraction**: Blockchain-specific details (gas pricing, finality, block times) are encapsulated in the Chain Gateway. Platform workflows operate against a normalized transaction interface. * **No direct external calls from business logic**: All external system interactions route through dedicated gateway components with retry handling, circuit breaking, and observability. ## See also [#see-also] * [Key Guardian](/docs/architecture/components/infrastructure/key-guardian) for custody abstraction internals * [Signing flow](/docs/architecture/flows/signing-flow) for end-to-end custody interaction * [Chain Gateway](/docs/architecture/components/infrastructure/chain-gateway) for network abstraction * [EVM RPC Node](/docs/architecture/components/infrastructure/evm-rpc-node) for blockchain connectivity * [Feeds system](/docs/architecture/components/infrastructure/feeds-system) for market data integration # Supported Networks Source: https://docs.settlemint.com/docs/architecture/integrations/supported-networks Reference for blockchain networks supported by the DALP platform, covering EVM-compatible Layer 1 mainnets, Layer 2 rollups, testnets, and private consortium networks with their configuration differences. ## Purpose [#purpose] Reference for blockchain networks that DALP supports, their configuration requirements, and compatibility constraints. * **Doc type:** Reference * **What you'll find here:** * EVM compatibility requirements * Supported network categories (L1, L2, testnet, private) * Network-specific configuration differences * Private network deployment considerations * **Related:** * [EVM RPC Node](/docs/architecture/components/infrastructure/evm-rpc-node) — blockchain access point * [Chain Gateway](/docs/architecture/components/infrastructure/chain-gateway) — multi-node connectivity * [Deployment Topology](/docs/architecture/overview/deployment-topology) — environment architecture *** ## Compatibility requirement [#compatibility-requirement] DALP operates on any blockchain that implements the **Ethereum JSON-RPC specification**. No application changes are required when switching networks. Configuration handles consensus differences, gas models, and confirmation requirements. *** ## Network categories [#network-categories] ### Layer 1 mainnets [#layer-1-mainnets] | Network | Gas model | Block time | Notes | | ----------------- | ------------------------------ | ------------ | --------------------------------------------- | | Ethereum | EIP-1559 (base + priority fee) | \~12 seconds | Primary target, full feature support | | Polygon PoS | EIP-1559 variant | \~2 seconds | Lower gas costs, faster finality | | Avalanche C-Chain | EIP-1559 | \~2 seconds | Sub-second finality with 3-block confirmation | | BNB Smart Chain | Legacy gas pricing | \~3 seconds | Higher throughput, centralized validator set | ### Layer 2 rollups [#layer-2-rollups] | Network | Type | Settlement | Notes | | ------------- | ----------------- | ----------- | ----------------------------------------- | | Arbitrum One | Optimistic rollup | Ethereum L1 | 7-day challenge period for withdrawals | | Optimism | Optimistic rollup | Ethereum L1 | Bedrock architecture | | Base | Optimistic rollup | Ethereum L1 | Coinbase-incubated, OP Stack | | zkSync Era | ZK rollup | Ethereum L1 | Validity proofs, different gas accounting | | Polygon zkEVM | ZK rollup | Ethereum L1 | EVM-equivalent ZK proofs | ### Testnets [#testnets] | Network | Corresponds to | Faucet availability | | ------- | ----------------- | ------------------- | | Sepolia | Ethereum mainnet | Public faucets | | Amoy | Polygon PoS | Public faucets | | Fuji | Avalanche C-Chain | Public faucets | ### Private / consortium networks [#private--consortium-networks] DALP supports private EVM deployments: * **Hyperledger Besu** — Enterprise Ethereum with IBFT 2.0 or QBFT consensus * **Go-Ethereum (Geth)** — private PoA (Clique) or PoS configurations * **SettleMint networks** — Managed private networks with genesis-allocated DALP contracts (predeployed infrastructure, deployment starts at Phase 2) *** ## Network-specific configuration [#network-specific-configuration] | Parameter | What varies | Impact | | ------------------------ | ------------------------------------- | ----------------------------------- | | Block confirmation count | 1 (private) to 12+ (Ethereum mainnet) | Finality confidence before indexing | | Gas price strategy | Legacy, EIP-1559, or custom | Transaction cost estimation | | RPC batch limits | Varies by provider | Indexer chunk size and concurrency | | Chain ID | Unique per network | Multi-chain identity registry | Configuration is environment-variable driven. No code changes required for network switching. *** ## Multi-chain considerations [#multi-chain-considerations] DALP supports simultaneous operation across multiple chains. Key architectural implications: * **Identity isolation**: Each chain has its own identity registry. An investor's OnchainID is chain-specific. * **Compliance isolation**: Compliance module configurations are per-chain, per-token. * **Indexer per chain**: One `ChainIndexer` Restate Virtual Object per chain ID. * **Custody per chain**: Fireblocks and DFNS support multi-chain asset wallets through the same vault/wallet. *** ## See also [#see-also] * [EVM RPC Node](/docs/architecture/components/infrastructure/evm-rpc-node) for blockchain connectivity * [Chain Gateway](/docs/architecture/components/infrastructure/chain-gateway) for multi-node failover * [Deployment Topology](/docs/architecture/overview/deployment-topology) for environment architecture # Database Source: https://docs.settlemint.com/docs/architecture/operability/database PostgreSQL is the authoritative store for application data, providing ACID guarantees, mature replication capabilities, and enterprise-proven reliability for mission-critical digital asset operations. ## Overview [#overview] PostgreSQL provides persistent storage for all DALP application data. This includes user accounts, asset configurations, indexed blockchain state, workflow state, and audit records. PostgreSQL's maturity, reliability, and extensive ecosystem make it the foundation for data persistence. Enterprise deployments require proven database technology. PostgreSQL represents decades of production hardening at scale. Major financial institutions trust PostgreSQL for mission-critical workloads. DALP benefits from this maturity and the extensive expertise available in the market. ## Data domains [#data-domains] | Domain | Content | Characteristics | | ------------- | ---------------------------------- | --------------------------------- | | Identity | Users, roles, sessions | High read, moderate write | | Configuration | Asset definitions, system settings | Low volume, high importance | | Indexed state | Blockchain-derived data | High volume, append-heavy | | Workflow | Execution engine state | Moderate volume, frequent updates | | Audit | Activity logs, compliance records | High volume, append-only | ## Schema organization [#schema-organization] Schemas partition data by domain for organizational clarity and access control: ``` dalp_identity - Authentication and authorization dalp_assets - Asset configuration and metadata dalp_indexed - Chain indexer output dalp_workflows - Execution engine state dalp_audit - Audit and compliance logs ``` Schema-level permissions enable principle of least privilege. Components access only schemas required for their function. ## High availability [#high-availability] Production deployments implement high availability through PostgreSQL replication: **Synchronous replication**: Write operations confirm only after replica acknowledgment. Zero data loss during primary failure. **Read replicas**: Query workloads distribute across replicas. Primary handles writes exclusively. **Automatic failover**: Patroni or cloud-managed failover promotes replica to primary during outages. ## Backup strategy [#backup-strategy] Data protection implements multiple backup layers: | Method | Frequency | Retention | Recovery time | | ---------------------- | ---------- | --------- | ------------- | | Streaming replication | Continuous | Real-time | Minutes | | Point-in-time recovery | Continuous | 30 days | Hours | | Full backups | Daily | 90 days | Hours | | Archive storage | Weekly | 7 years | Days | Point-in-time recovery enables restoration to any moment within the retention window. This capability supports compliance investigations and operational recovery scenarios. ## Performance optimization [#performance-optimization] ### Connection pooling [#connection-pooling] PgBouncer pools database connections, reducing connection overhead and enabling higher concurrency than direct connections support. ### Query optimization [#query-optimization] Schema design anticipates query patterns. Indexes cover common filter and join operations. Query analysis guides ongoing optimization. ### Partitioning [#partitioning] High-volume tables partition by time or tenant. Partition pruning reduces scan scope for bounded queries. Partition management automates retention enforcement. ### Caching [#caching] Application-level caching reduces database load for repeated queries. Cache invalidation coordinates with database transactions for consistency. ## Security [#security] ### Encryption [#encryption] **At rest**: Storage encryption protects data on disk. Managed deployments use cloud provider encryption with customer-managed keys. **In transit**: TLS encrypts all database connections. Certificate verification prevents connection interception. ### Access control [#access-control] Role-based access restricts database operations to authorized components. Service accounts receive minimum necessary privileges. Administrative access requires multi-factor authentication. ### Audit logging [#audit-logging] PostgreSQL audit logs can capture data access for deployments that enable the required database auditing. Retention, export, and SIEM routing depend on the deployment's logging storage, backup, and compliance configuration; see [Observability](/docs/architecture/operability/observability#audit-logging) for the shared audit logging boundary. ## Managed deployment options [#managed-deployment-options] | Provider | Service | Key features | | ------------ | --------------- | ------------------------------------------ | | AWS | RDS PostgreSQL | Multi-AZ, automated backups | | GCP | Cloud SQL | High availability, private networking | | Azure | Azure Database | Geo-replication, compliance certifications | | Self-managed | Patroni cluster | Full control, any infrastructure | ## See also [#see-also] * [Chain Indexer](/docs/architecture/components/infrastructure/chain-indexer) for indexed data * [Observability](/docs/architecture/operability/observability) for database monitoring # Failure Modes Source: https://docs.settlemint.com/docs/architecture/operability/failure-modes Catalog of architecture-level failure modes across DALP components, documenting degradation behavior, detection mechanisms, and recovery strategies for each failure scenario. ## Purpose [#purpose] Catalogs architecture-level failure modes, their impact, and how DALP components degrade and recover. * **Doc type:** Reference * **What you'll find here:** * Failure modes per component layer * Degradation behavior (fail-open vs fail-closed) * Detection and recovery mechanisms * Impact on user-facing operations * **Related:** * [Observability](/docs/architecture/operability/observability) — monitoring and alerting * [Database](/docs/architecture/operability/database) — data layer resilience * [Signing Flow](/docs/architecture/flows/signing-flow) — transaction durability *** ## Failure mode catalog [#failure-mode-catalog] ### Blockchain layer [#blockchain-layer] | Failure | Impact | Behavior | Recovery | | -------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------- | ---------------------------------------- | | RPC node unreachable | No blockchain reads or writes | Chain Gateway fails over to next node in pool | Automatic (health check + failover) | | All RPC nodes down | Complete blockchain outage | Transactions queue in Restate; reads return stale data | Manual: restore node connectivity | | Block reorg | Indexed data may reflect reverted transactions | Reorg detection via block hash comparison (infrastructure in place, not yet active) | Future: automatic rollback and reprocess | | Gas price spike | Transaction submission may fail or be slow | Automatic gas estimation; transactions retry with updated gas | Automatic retry via nonce manager | | Nonce conflict | Transaction rejected by network | Nonce manager queues and reorders; Restate retries | Automatic | ### Execution engine layer [#execution-engine-layer] | Failure | Impact | Behavior | Recovery | | ------------------------ | ---------------------------------------- | ------------------------------------------------------ | --------------------------------------------------------- | | Restate server crash | In-flight workflows pause | Journaled steps preserved; automatic resume on restart | Automatic (Restate journal replay) | | Workflow step failure | Single step in multi-step workflow fails | Restate retries with configurable backoff | Automatic retry; manual intervention if retries exhausted | | Database connection lost | Cannot checkpoint or read state | Restate retries database operations | Automatic retry; manual if persistent | ### Indexer layer [#indexer-layer] | Failure | Impact | Behavior | Recovery | | ------------------------------------- | ------------------------------- | -------------------------------------------------------------- | ---------------------------- | | Indexer crash during block processing | Gap in indexed data | Resume from last checkpoint; idempotent event processing | Automatic (checkpoint-based) | | Event handler failure | Single event type not processed | `processedEvents` table prevents duplicate processing on retry | Automatic retry | | RPC rate limit exceeded | Indexer sync slows | Batch size and concurrency respect configured limits | Automatic backoff | ### API layer [#api-layer] | Failure | Impact | Behavior | Recovery | | --------------------------- | ------------------ | ---------------------------------------------------- | ------------------------------ | | API server crash | Requests fail | Load balancer routes to healthy instances | Automatic (horizontal scaling) | | Authentication service down | No new sessions | Existing sessions continue (cached); new logins fail | Restart auth service | | Database unreachable | API returns errors | Fail-closed: operations that require data return 503 | Restore database connection | ### Custody layer [#custody-layer] | Failure | Impact | Behavior | Recovery | | -------------------------------- | ----------------------------- | ---------------------------------------------------- | ------------------------------------ | | Custody provider unreachable | Cannot sign transactions | Transactions queue in Restate; retry on availability | Automatic retry | | Policy engine blocks transaction | Transaction held for approval | DALP surfaces pending approval in operator interface | Manual approval or policy adjustment | | MPC signing timeout | Transaction delayed | Restate retries signing request | Automatic retry | *** ## Degradation philosophy [#degradation-philosophy] DALP follows **fail-closed** for security-sensitive operations: * Compliance checks that cannot complete → transfer blocked (not allowed by default) * Authentication failures → access denied * Signing failures → transaction queued, not skipped Read-only operations degrade gracefully: * Stale indexed data is served with freshness indicators * Cached API responses continue serving during database outages * Dashboard shows last-known state with staleness warnings *** ## See also [#see-also] * [Observability](/docs/architecture/operability/observability) for detection and alerting * [Signing Flow](/docs/architecture/flows/signing-flow) for transaction durability guarantees * [Chain Gateway](/docs/architecture/components/infrastructure/chain-gateway) for blockchain failover # Overview Source: https://docs.settlemint.com/docs/architecture/operability Architecture-level operational posture for the DALP platform covering observability infrastructure, database architecture, and failure mode analysis for enterprise deployments. ## Purpose [#purpose] This section documents DALP's operational architecture -- the infrastructure, patterns, and design decisions that determine how the platform behaves under normal operations and during failures. * **Doc type:** Reference ## What you'll find here [#what-youll-find-here] * Observability stack architecture (metrics, logs, traces, dashboards) * Database design and data persistence strategy * Failure mode catalog with expected system behavior * Architecture-level reliability characteristics ## Operational posture [#operational-posture] DALP is designed for continuous operation in regulated environments. The operational architecture addresses three concerns: 1. **Visibility** -- operators must see what the platform is doing at all times. The observability stack provides metrics, structured logs, and distributed traces across all components. 2. **Persistence** -- application state must survive failures. PostgreSQL provides ACID guarantees for all critical data. The Execution Engine persists workflow state at every step boundary. 3. **Resilience** -- failures must not corrupt state or lose transactions. The platform degrades gracefully, retries transient failures automatically, and routes permanent failures to dead letter queues for manual resolution. This section describes architecture-level operational characteristics. For deployment procedures and runbooks, see the [self-hosting documentation](/docs/architecture/self-hosting). ## Section pages [#section-pages] | Page | Description | | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | | [Observability](/docs/architecture/operability/observability) | Metrics collection, log aggregation, distributed tracing, alerting rules, and pre-built dashboards | | [Database](/docs/architecture/operability/database) | PostgreSQL architecture, data domains, replication strategy, and backup posture | | [Failure modes](/docs/architecture/operability/failure-modes) | Catalog of expected failure scenarios, system behavior under each, and recovery characteristics | ## Key reliability characteristics [#key-reliability-characteristics] * **Workflow durability**: The DALP Execution Engine persists state before each step. Process restarts resume from the last checkpoint without data loss or duplicate operations. * **Transaction atomicity**: Blockchain transactions use idempotency keys and nonce management to prevent duplicate submissions under retry conditions. * **Graceful degradation**: Component failures isolate to affected workflows. Unrelated operations continue processing. ## See also [#see-also] * [DALP Execution Engine](/docs/architecture/components/infrastructure/execution-engine) for workflow reliability patterns * [Self-hosting](/docs/architecture/self-hosting) for deployment and infrastructure requirements * [High availability](/docs/architecture/self-hosting/high-availability) for HA and DR configurations * [Security](/docs/architecture/security) for security-related operational controls # Observability Source: https://docs.settlemint.com/docs/architecture/operability/observability The observability stack provides platform visibility through metrics collection, log aggregation, distributed tracing, and Grafana dashboards for deployments that enable the observability chart. ## Overview [#overview] The observability stack provides visibility into DALP platform operations when the observability chart is enabled for a deployment. Metrics, logs, and traces collect centrally so operators can inspect platform health and investigate incidents from one telemetry surface. Enterprise platforms require operational visibility. Operators need system health signals, security teams need audit trails, and developers need debugging context. DALP supports those workflows through deployable telemetry components rather than by making the application itself the system of record for monitoring policy. ## Three pillars [#three-pillars] API monitoring overview ### Metrics [#metrics] Time-series metrics capture quantitative measurements over time. Counters, gauges, and histograms represent request counts, resource utilization, and latency distributions. | Metric category | Examples | Use case | | ---------------- | ------------------------------------ | ---------------------- | | Request metrics | Rate, latency, errors | Performance monitoring | | Resource metrics | CPU, memory, connections | Capacity planning | | Business metrics | Transactions, assets, users | Operational reporting | | Chain metrics | Block lag, gas prices, confirmations | Blockchain health | ### Logs [#logs] Structured logs capture discrete events with rich context. JSON formatting enables efficient parsing and querying. Correlation identifiers link related log entries across components. DALP redacts common credential and token shapes before log records are written to configured sinks. Covered values include SettleMint access tokens, bearer tokens, private keys, provider access keys, webhook or integration tokens, RPC URLs that contain embedded keys, and email addresses. Redaction applies to log messages and structured properties, so operators can use logs for debugging without intentionally storing those values. ### Traces [#traces] Distributed traces follow operations across component boundaries. Spans capture timing and metadata for each step. Trace visualization reveals bottlenecks and failure points in complex operations. ## Dashboard areas [#dashboard-areas] Grafana dashboards can cover these monitoring areas when the observability stack and relevant exporters are enabled: | Dashboard area | Audience | Example signals | | --------------------- | ------------------- | ---------------------------------------------- | | Operations overview | Platform operators | Request rates, error rates, latency | | Transaction monitor | Operations team | Pending transactions, gas usage, confirmations | | Compliance activity | Compliance officers | Verification volumes, approval rates | | Security overview | Security team | Authentication events, access patterns | | Infrastructure health | DevOps | Resource utilization, node health | Detailed API request logs ## Deployable components [#deployable-components] The observability Helm chart can deploy the telemetry components used by self-hosted environments. The chart includes VictoriaMetrics for metrics storage, Grafana Alloy for telemetry collection, metrics-server and kube-state-metrics for Kubernetes resource and object metrics, Grafana for dashboards, Loki for logs, Prometheus node exporter for host metrics, and Tempo for traces. Local development values enable the observability stack by default. Staging values disable the observability chart by default, and other deployment values may also disable the stack, so treat observability as a deployment option that must be enabled and configured for the target environment. ### Grafana access on OpenShift [#grafana-access-on-openshift] On OpenShift clusters, the observability chart can expose Grafana through an OpenShift Route when the Route API is available and the Grafana Route option is enabled. The Route targets the Grafana service, uses the configured host and path, and can include TLS settings such as edge, passthrough, or re-encrypt termination with the deployment's insecure-traffic policy. This Route is disabled by default. Enable it only when the cluster should expose Grafana through the OpenShift Router instead of another ingress pattern, and keep the hostname, TLS policy, and access controls aligned with the organization's observability access model. ## Alerting [#alerting] Alert rules can notify operators when metrics exceed thresholds or exhibit anomalous patterns. | Alert category | Example condition | Severity | | ------------------- | ----------------------------------------------------- | -------- | | Error rate spike | Elevated error rate over a sustained window | Critical | | Latency degradation | P99 latency materially above baseline | Warning | | Resource exhaustion | High memory or CPU utilization | Warning | | Chain connectivity | Sustained block production or RPC connectivity issues | Critical | | Transaction failure | Elevated transaction failure rate | Warning | Alert routing depends on the deployment's notification configuration. Alert labels include the originating cluster name from the deployment telemetry configuration. Multi-cluster operators can identify the affected environment before they inspect dashboards or logs. ### DIDX live indexing alerts [#didx-live-indexing-alerts] When the observability chart is enabled, DALP can alert on DIDX live indexing health. Each DIDX alert includes the affected Kubernetes namespace, chain ID, and cluster name. Operators can triage one chain in one cluster without masking it behind healthy chains elsewhere. | Alert signal | What it indicates | Triage hint | | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | Live indexing lag | DIDX is more than 1,000 blocks behind chain head, has not reduced that lag over a 30-minute lookback, and remains in that condition for 15 minutes. | Check whether indexer lag is rising, flat, or recovering, then compare DIDX and RPC health. | | Sync or handler errors | DIDX recorded sync failures or event-handler failures in the recent alert window. | Query the handler-error metric by event and contract type to find the failing handler. | | Backfill not progressing | DIDX has pending backfill work and the pending count has not decreased over the monitored window. | Check DIDX pod health, RPC latency, and whether the pending queue is growing or flat. | | Native-balance collection | Operator-wallet balances, balance fetches, or refresh-queue depth need attention. | Use the affected chain ID and wallet address to confirm the balance or collector backlog. | For live-indexing alerts, start from the alert labels, then inspect the DIDX dashboard and logs for the same cluster, namespace, and chain ID. Handler-error alerts page at the chain level. Query the underlying handler-error metric by event and contract type to distinguish one failing handler from a chain-wide indexing outage. ### DIDX meta-transaction metrics [#didx-meta-transaction-metrics] DIDX records signer attribution for indexed domain events. For each event, the resolver checks the same transaction for the next `ExecutedForwardRequest` marker from a registered forwarder. Forwarder-marked events use the marker's signer. Direct and unmarked events use the transaction sender. Forwarder attribution follows the forwarder's active window. When a deployment rotates to a new forwarder, DIDX keeps the previous forwarder available for historical event blocks. DIDX does not trust the previous forwarder for later events. Historical backfills use the markers that were active for the backfilled block range, so reindexing can rebuild signer attribution for older forwarded transactions. Operators can use these counters to inspect ERC-2771 signer attribution: * `dalp.didx.meta_tx.signer_resolved`: Counts each event with resolved signer attribution. Use the `resolver_source`, `event`, and `contract_type` labels to separate forwarder-attributed events from transaction-sender attribution. * `dalp.didx.meta_tx.signer_caller_divergence`: Counts events where the resolved signer differs from an on-chain `caller` field. DALP emits this counter for event families that still carry that field, including `TokenBound` and `TokenUnbound`. Use `signer_resolved` as the baseline for attribution volume. Use `signer_caller_divergence` to investigate signer and caller mismatches on the event families that emit it. ## Application logging configuration [#application-logging-configuration] Application logging can be configured through the `config.yml` file. | Setting | Environment variable | Default | Description | | --------------------- | ------------------------------------- | ------- | ----------------------------------------------------------------------- | | `app.logLevel` | `LOG_LEVEL` or `SETTLEMINT_LOG_LEVEL` | `info` | Minimum log level: `debug`, `info`, `warn`, `warning`, `error`, `fatal` | | `app.logOrpcRequests` | `LOG_ORPC_REQUESTS` | `false` | Enable verbose ORPC request/response logging | > **Note**: `LOG_LEVEL` takes precedence during auto-configuration. Invalid values are silently ignored and fall back to environment defaults (debug for development, info for production, warning for test). ### ORPC request logging [#orpc-request-logging] When `app.logOrpcRequests` is enabled, the platform logs detailed information for each API request: * Request ID and URL * HTTP method and elapsed time * Response status codes * Procedure execution paths This setting is disabled by default to keep logs clean in development and production. Enable it for debugging API issues: ```yaml # config.yml app: logOrpcRequests: true ``` Or via environment variable: ```bash LOG_ORPC_REQUESTS=true ``` On-chain transaction monitoring ## Audit logging [#audit-logging] Observability data can support audit investigations by preserving operational events and correlation context. Typical events include: * Authentication events with outcome and context * Authorization decisions with resource and action * Data access with query details and results * Configuration changes with before and after state * Administrative actions with operator identity Retention, export, and tamper-evidence requirements depend on the deployment's logging storage, backup, and compliance configuration. ## Incident response [#incident-response] Observability tooling supports rapid incident response: **Correlation**: Trace IDs link logs, metrics, and traces for affected operations. **Timeline reconstruction**: Log search with time filters reveals event sequences. **Impact assessment**: Metrics dashboards quantify affected users and operations. **Root cause analysis**: Trace visualization identifies failing components. ## Deployment integration [#deployment-integration] Self-hosted deployments can use the DALP observability chart for in-cluster telemetry collection, storage, and dashboards. If an organization already operates a monitoring platform, use the deployment configuration to decide which telemetry components to enable and how to route metrics, logs, traces, and alerts. When enabled, the observability chart includes Grafana dashboard configuration for common self-hosted deployments. ## See also [#see-also] * [Database](/docs/architecture/operability/database) for database monitoring * [Chain Gateway](/docs/architecture/components/infrastructure/chain-gateway) for network metrics # Data Domains Source: https://docs.settlemint.com/docs/architecture/overview/data-domains Data domain ownership map for the DALP platform showing sources of truth, data lifecycle stages, and governance boundaries across on-chain and off-chain storage. ## Purpose [#purpose] Maps data ownership across DALP, identifying sources of truth, lifecycle stages, and governance boundaries. * **Doc type:** Reference * **What you'll find here:** * Data domain ownership table * Source of truth for each domain * On-chain vs off-chain data split * Data lifecycle and retention * **Related:** * [Database](/docs/architecture/operability/database) — database schema and migrations * [Chain Indexer](/docs/architecture/components/infrastructure/chain-indexer) — blockchain-to-database sync * [Identity & Compliance](/docs/architecture/security/identity-compliance) — identity data model *** ## Domain ownership [#domain-ownership] | Domain | Source of truth | Storage | Owner | | ---------------- | ---------------------------------- | ------------------- | ------------------------- | | Token balances | Blockchain (ERC-20 state) | On-chain | SMART Protocol | | Token metadata | Blockchain (contract storage) | On-chain | SMART Protocol | | Identity claims | Blockchain (OnchainID contracts) | On-chain | Identity system | | Compliance rules | Blockchain (module contracts) | On-chain | Compliance engine | | User accounts | PostgreSQL (`auth_*` tables) | Off-chain | Unified API (Better Auth) | | Organizations | PostgreSQL (`organisation` table) | Off-chain | Unified API | | Wallet bindings | PostgreSQL (`wallet` table) | Off-chain | Unified API | | API keys | PostgreSQL (`api_key` table) | Off-chain | Unified API | | Signing keys | External (HSM / DFNS / Fireblocks) | External | Key Guardian | | Indexed events | PostgreSQL (`idxr_*` tables) | Off-chain (derived) | Chain Indexer | | Feed data | PostgreSQL (`feed_*` tables) | Off-chain | Feeds system | | Audit log | PostgreSQL (`audit_log` table) | Off-chain | Unified API | *** ## On-chain vs off-chain split [#on-chain-vs-off-chain-split] | Characteristic | On-chain | Off-chain | | ----------------- | ------------------------------------------- | ------------------------------------- | | Authoritative for | Asset state, compliance, identity | User accounts, UI state, analytics | | Mutability | Append-only (blockchain) | Mutable (PostgreSQL) | | Latency | Block time (2-15 seconds) | Sub-second | | Consistency | Eventually consistent (block confirmations) | Strongly consistent (PostgreSQL ACID) | | Retention | Permanent (blockchain) | Configurable (7+ years for financial) | The Chain Indexer bridges these worlds: it reads blockchain events and writes derived state to PostgreSQL for fast querying. The indexed data is always reconstructible from on-chain events. *** ## Data lifecycle [#data-lifecycle] | Stage | On-chain data | Off-chain data | | ---------- | --------------------------------- | --------------------------------------------- | | Creation | Transaction execution | API request → database write | | Validation | Smart contract logic + compliance | Application validation + database constraints | | Storage | Block inclusion | PostgreSQL commit | | Retrieval | RPC query or indexed copy | Database query | | Archival | Permanent on-chain | Backup + retention policy | | Deletion | Not possible (immutable) | GDPR-compliant deletion for PII | *** ## See also [#see-also] * [Database](/docs/architecture/operability/database) for schema details and migration workflow * [Chain Indexer](/docs/architecture/components/infrastructure/chain-indexer) for blockchain-to-database sync * [Identity & Compliance](/docs/architecture/security/identity-compliance) for identity data architecture # Deployment Topology Source: https://docs.settlemint.com/docs/architecture/overview/deployment-topology Architecture-level view of DALP deployment topology covering environments, runtime zones, network boundaries, and scaling constraints. Links to self-hosting guides for installation procedures. DALP deployments separate the public console, backend services, and persistent data stores into distinct runtime zones. The tables below map each component to its deployment boundary for self-hosting, operability, and network isolation planning. Related architecture references: * [Component Catalog](/docs/architecture/components) for the full component inventory * [Self-Hosting](/docs/architecture/self-hosting) for installation procedures * [Operability](/docs/architecture/operability) for observability and database architecture ## Runtime zones [#runtime-zones] DALP components deploy across three runtime zones: | Zone | Components | Network exposure | | ------------ | ------------------------------------------------------------ | ----------------------------------------- | | **Frontend** | Asset Console (React SPA) | Public internet via CDN/reverse proxy | | **Backend** | Unified API, Execution Engine, Restate, Chain Indexer, Feeds | Internal network, API exposed via ingress | | **Data** | PostgreSQL, Restate state store, blockchain nodes | Internal only, no public exposure | *** ## Environments [#environments] | Environment | Purpose | Blockchain | | ----------- | --------------------------------------- | ----------------------------- | | Development | Local development with Docker Compose | Anvil (local EVM) | | Staging | Integration testing with realistic data | Testnet (Sepolia, Amoy, etc.) | | Production | Live operations | Mainnet or private network | All environments use identical container images. Configuration differences are environment variables only. *** ## Component deployment model [#component-deployment-model] | Component | Runtime | Scaling model | State | | ---------------- | ----------------------- | -------------------------- | ---------------------- | | Asset Console | Container (Node.js SSR) | Horizontal (stateless) | None | | Unified API | Container (Node.js) | Horizontal (stateless) | Database sessions | | Execution Engine | Restate runtime | Single-writer per workflow | Restate journal | | Chain Indexer | Restate service | Single instance per chain | PostgreSQL checkpoints | | Key Guardian | Container (Node.js) | Horizontal | External (HSM/custody) | | Chain Gateway | Container | Horizontal | Health check state | | PostgreSQL | Managed database | Vertical + read replicas | Persistent | | Blockchain Node | Container or managed | Per-network | Chain state | *** ## Network boundaries [#network-boundaries] Three boundaries separate trust zones: 1. **Internet → Frontend zone**: TLS termination, rate limiting, WAF 2. **Frontend → Backend zone**: API authentication, session validation 3. **Backend → Data zone**: Database credentials, RPC node access Blockchain RPC traffic exits the backend zone to external or co-located nodes through the Chain Gateway. *** Real-time blockchain transaction monitoring and contract interaction tracking ## See also [#see-also] * [Self-Hosting](/docs/architecture/self-hosting) for installation procedures * [Failure Modes](/docs/architecture/operability/failure-modes) for degradation behavior * [Database](/docs/architecture/operability/database) for data architecture # Overview Source: https://docs.settlemint.com/docs/architecture/overview Landing page for DALP architecture fundamentals: design principles, quality attributes, deployment topology, and data domain ownership. Provides the conceptual foundation before exploring components and flows. DALP's architecture starts with four boundaries: the operator console, the programmatic API, the execution engine, and the SMART Protocol contracts. These pages define the principles, quality attributes, deployment topology, and data ownership model behind those boundaries. ## Architecture philosophy [#architecture-philosophy] DALP separates concerns into four distinct layers. The **Asset Console** provides the operator interface. The **Unified API** exposes every capability programmatically with OpenAPI documentation. The **DALP Execution Engine** orchestrates multi-step blockchain workflows with guaranteed delivery. The **SMART Protocol** enforces compliance at the smart-contract level through ERC-3643. DALP platform overview showing total AUM, active assets, and key statistics Each layer owns its boundaries, contracts, and failure modes. Layers communicate through stable interfaces, not shared internal state. This separation allows independent scaling, deployment, and evolution of each layer. ## Sections [#sections] | Page | What it covers | | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | | [Principles and non-goals](/docs/architecture/overview/principles-and-non-goals) | What we optimize for, what we deliberately exclude, and why | | [Quality attributes](/docs/architecture/overview/quality-attributes) | Security, reliability, performance, and compliance commitments with tradeoffs | | [Deployment topology](/docs/architecture/overview/deployment-topology) | Environments, runtime zones, network boundaries, and scaling constraints | | [Data domains](/docs/architecture/overview/data-domains) | Sources of truth, data ownership, lifecycle stages, and governance model | ## Related [#related] * [Component catalog](/docs/architecture/components) for the full inventory of platform services * [Key flows](/docs/architecture/flows) for step-by-step operation sequences * [Security](/docs/architecture/security) for threat model and controls mapping * [Start here](/docs/architecture/start-here) for the architecture navigation map # Principles & Non-Goals Source: https://docs.settlemint.com/docs/architecture/overview/principles-and-non-goals Design principles that guide DALP's architecture decisions and explicit non-goals that define what the platform deliberately does not attempt. ## Purpose [#purpose] Defines the design principles that drive every architectural decision in DALP, and the explicit non-goals that keep the platform focused. * **Doc type:** Explanation * **What you'll find here:** * Core design principles with rationale * Explicit non-goals and why they are excluded * Tradeoffs between competing concerns * **Related:** * [Quality Attributes](/docs/architecture/overview/quality-attributes) — measurable quality commitments * [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) — protocol-level design decisions *** ## Design principles [#design-principles] | Principle | Rationale | | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Compliance at the protocol layer** | Transfer restrictions, identity verification, and regulatory rules enforce in smart contracts — not in application middleware. On-chain enforcement is auditable and tamper-proof. | | **Separate contract per instrument** | Each financial instrument (bond, equity, fund) deploys as its own token contract. Independent lifecycles, compliance rules, and upgrades. | | **Durable orchestration** | Multi-step blockchain workflows (deploy, configure, mint) execute through Restate with guaranteed delivery and exactly-once semantics. No lost transactions. | | **Layer separation** | Four layers (Console, API, Execution Engine, Protocol) communicate through stable interfaces. No shared internal state. Independent scaling and deployment. | | **Identity-bound tokens** | Every token holder must have an on-chain identity (OnchainID). Claims-based verification replaces centralized allowlists. | | **Factory-deployed upgradeable contracts** | All token and capability contracts deploy through factory proxies. Upgrades without migration. Consistent deployment patterns. | | **API-first** | Every capability available through the Asset Console is also available through the Unified API. Enables automation and third-party integration. | | **Multi-tenant by default** | Organization-level isolation for platform, system, and per-asset roles. No single-tenant assumptions in the architecture. | *** ## Explicit non-goals [#explicit-non-goals] | Non-goal | Rationale | | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Custom blockchain consensus** | DALP runs on existing EVM-compatible chains. Building or modifying consensus is out of scope. | | **Retail wallet UX** | The Asset Console targets institutional operators, not retail investors. Retail-facing interfaces are expected from integrators. | | **Cross-chain atomic swaps** | XvP settlement supports single-chain atomic delivery. Cross-chain coordination uses hashlock patterns but does not implement bridge protocols. | | **Real-time market data feeds** | The Feeds system provides curated data for on-chain operations (pricing, NAV). DALP is not a real-time market data platform. | | **Smart contract formal verification** | Testing covers correctness. Formal verification is not part of the standard development workflow, though the contract architecture supports third-party audits. | *** ## Key tradeoffs [#key-tradeoffs] | Tradeoff | Chosen direction | Cost | | ---------------------------------------- | --------------------- | --------------------------------------------------- | | On-chain compliance vs transaction speed | On-chain enforcement | Higher gas costs, slower transfers | | Separate tokens vs partitioned balances | Separate tokens | More deployment overhead, more contracts to manage | | Durable orchestration vs direct RPC | Restate orchestration | Additional infrastructure component, learning curve | | Factory proxies vs direct deployment | Factory proxies | Proxy indirection, storage layout constraints | *** ## See also [#see-also] * [Quality Attributes](/docs/architecture/overview/quality-attributes) for measurable quality targets * [SMART Protocol integration](/docs/architecture/components/asset-contracts/smart-protocol-integration) for protocol-level design decisions # Quality Attributes Source: https://docs.settlemint.com/docs/architecture/overview/quality-attributes Quality attributes treated as first-class architectural requirements in DALP, with explicit tradeoffs and measurable commitments for security, reliability, performance, and regulatory compliance. ## Purpose [#purpose] Documents the quality attributes that DALP treats as first-class architectural requirements, with explicit tradeoffs and measurable targets. * **Doc type:** Reference * **What you'll find here:** * Security posture and trust boundaries * Reliability model and failure recovery * Performance characteristics and bottlenecks * Compliance alignment with regulatory frameworks * **Related:** * [Security](/docs/architecture/security) — detailed security architecture * [Operability](/docs/architecture/operability) — observability and failure modes * [Principles & Non-Goals](/docs/architecture/overview/principles-and-non-goals) — design drivers *** ## Security [#security] | Attribute | Commitment | | -------------------- | ---------------------------------------------------------------- | | Authentication | Multi-factor: session + wallet PIN/TOTP for blockchain writes | | Authorization | 26 roles across 4 layers (platform, system, per-asset, module) | | On-chain enforcement | ERC-3643 compliance modules validate every token transfer | | Key protection | MPC custody (DFNS, Fireblocks) or HSM for signing keys | | Audit trail | Every state change logged with actor, timestamp, and correlation | Trust boundaries: platform boundary (authentication), execution boundary (authorization), chain boundary (on-chain compliance + custody policies). See [Security](/docs/architecture/security). *** ## Reliability [#reliability] | Attribute | Commitment | | -------------------- | -------------------------------------------------------------------- | | Workflow durability | Restate journals every step; automatic retry on failure | | Transaction delivery | Nonce management with queue-based ordering; no lost transactions | | Data consistency | PostgreSQL with Drizzle ORM; migrations generated, never hand-edited | | Indexer recovery | Idempotent event processing; checkpoint-based resume after crashes | | Blockchain failover | Chain Gateway load-balances across multiple RPC endpoints | Failure modes and degradation behavior documented in [Failure Modes](/docs/architecture/operability/failure-modes). *** ## Performance [#performance] | Component | Characteristic | Bottleneck | | ---------------- | -------------------------------------------------- | --------------------------------------------- | | Token transfers | Bounded by block time + compliance module gas cost | Number of compliance modules per token | | Indexer sync | \~2000 blocks per batch, converging discovery loop | RPC rate limits, database write throughput | | API response | Sub-second for cached queries | Database query complexity for aggregate views | | Asset deployment | Multi-transaction workflow (5-15 TXs) | Block confirmation time × transaction count | *** ## Compliance [#compliance] | Framework | DALP support | | ----------------- | ------------------------------------------------------------------ | | ERC-3643 | Full implementation via SMART Protocol | | MiCA (EU) | Country allow-list + supply cap modules | | Regulation D (US) | Accredited investor verification + investor count + holding period | | KYC/AML | OnchainID claim-based verification via trusted issuers | | SOC 2 / PCI DSS | Key Guardian supports HSM storage tiers | See [Compliance Modules](/docs/architecture/security/compliance) for the full module catalog. *** Platform scale: 39 managed identities, 4 trusted issuers, 384 active verifications ## See also [#see-also] * [Security](/docs/architecture/security) for defense-in-depth details * [Operability](/docs/architecture/operability) for observability and failure modes * [Compliance Modules](/docs/architecture/security/compliance) for regulatory module catalog # SMART Protocol Source: https://docs.settlemint.com/docs/architecture/overview/smart-protocol Redirect notice for the DALP SMART Protocol architecture page. The SMART Protocol architecture content now lives under the asset contract component catalogue. Use the new page for the current ERC-3643 implementation details, compliance hooks, identity registry integration, and transfer validation flow. See: [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) # Authentication Source: https://docs.settlemint.com/docs/architecture/security/authentication DALP implements multi-method authentication with passkeys, API keys, session cookies, and wallet verification. Enterprise SSO protocols are supported through optional authentication extensions but are not active by default. DALP verifies users, services, and browser sessions before they can access protected platform areas. Browser operators authenticate with session cookies. Integrations use scoped API keys. Deployments can enable passkeys and two-factor authentication for stronger account protection. Related pages: * [Authorization](/docs/architecture/security/authorization) * [Wallet Verification](/docs/architecture/security/wallet-verification) * [Identity and Compliance](/docs/architecture/security/identity-compliance) * [Signing Flow](/docs/architecture/flows/signing-flow) ## Authentication methods [#authentication-methods] The table below shows each supported method and whether it is active by default. | Method | Use case | Status | | ----------------------- | ---------------------------------------------------------------------------------- | ---------------------- | | Email / password | Standard operator and user access | Active | | Passkeys (WebAuthn) | Hardware security keys, platform authenticators (Face ID, Touch ID, Windows Hello) | Active | | LDAP / Active Directory | Corporate directory integration | Available when enabled | | OAuth 2.0 / OIDC | Okta, Auth0, Azure AD integration | Available when enabled | | SAML 2.0 | Legacy enterprise SSO | Available when enabled | LDAP, OAuth 2.0/OIDC, and SAML 2.0 require the corresponding authentication extension. Enabling them is not a configuration-only change. ## Session authentication [#session-authentication] Browser clients authenticate with HTTP-only session cookies. Authentication checks validate the current session before routing users into protected console areas. If the session is missing or expired, DALP redirects the user to sign in. If the deployment enforces two-factor authentication and the user has not completed setup, DALP routes the user to the two-factor setup flow before they continue. Split onboarding defers that redirect until organization deployment can continue. Key properties: * **HTTP-only cookies**: prevent client-side script access to session tokens * **Secure flag**: cookies transmit only over HTTPS * **SameSite attribute**: protects against cross-site request forgery * **Expiration**: sessions expire after 7 days and refresh after 24 hours when the session remains active * **Fresh session validation**: protected console routes validate the current session before making routing decisions * **Session binding**: each session associates with a user identity and active organization for complete audit trails Signing out clears the browser session cookies and returns the operator to the sign-in flow. If an authenticated user has no active organization, DALP routes them to the next eligible onboarding or invitation step. In single-tenant deployments with an existing organization, DALP signs the user out and returns them to sign in. Every authentication event is logged with timestamp, method, and result. Multi-factor authentication with passkey and email/password credentials ## Passkey authentication [#passkey-authentication] Passkeys provide passwordless authentication using the WebAuthn standard. Hardware security keys and platform authenticators (Face ID, Touch ID, Windows Hello) eliminate password-related vulnerabilities. Benefits: * **Phishing resistance** -- passkeys are cryptographically bound to the origin domain * **No shared secrets** -- nothing to steal from the server * **Biometric verification** -- on supported devices, authentication requires a biometric or device PIN ## Two-factor authentication [#two-factor-authentication] User accounts can add TOTP-based two-factor authentication from the account security card when the platform has 2FA enforcement enabled. If enrollment is mandatory and a user has not enabled 2FA, the app routes them to the two-factor setup page before they continue, except during split onboarding where 2FA is deferred until after deployment. The setup flow asks the user to confirm their password, then displays an `otpauth://` QR code for scanning with an authenticator app. After the user verifies a one-time code, the platform records 2FA as enabled and displays backup codes for the user to store separately. The QR code is rendered locally in the browser. The TOTP URI is passed to DALP's in-app QR-code component instead of to a third-party QR-code image service, so the shared secret does not leave the browser during enrollment. ## API key authentication [#api-key-authentication] Machine-to-machine integrations authenticate with API keys. Keys follow a predictable format for easy identification in logs and configuration. | Aspect | Implementation | | ---------- | ----------------------------------------------------------------- | | Format | `sm_atk_` prefix + 16 random characters | | Storage | Hashed in database; cleartext shown once at creation | | Metadata | Custom key-value pairs for tracking and organization | | Scoping | Per-key permissions limit access to specific procedure namespaces | | Rate limit | 10,000 requests per 60-second window per key | ### Key scoping [#key-scoping] API keys support scoped permissions that limit access to specific procedure namespaces. A key created for reporting cannot execute token operations. This follows the principle of least privilege -- each integration receives only the permissions it requires. Permissions inherit from the creating user's role (admin, owner, or member). ### Key lifecycle [#key-lifecycle] Keys can be created, rotated, and revoked through the console or API. Revoked keys immediately lose access. Optional expiration can be configured per key with no minimum constraint. ## Key management architecture [#key-management-architecture] The platform implements a three-tier key management system for blockchain signing keys: ### Tier 1: Database-managed keys [#tier-1-database-managed-keys] Keys are encrypted and stored in the platform database. Fastest onboarding with minimal infrastructure. Suitable for development, proof-of-concept, and non-custodial scenarios. ### Tier 2: Hardware security modules [#tier-2-hardware-security-modules] Production deployments integrate with HSMs for HSM-backed key protection. Keys never leave the HSM boundary -- all signing operations execute within the secure enclave. Satisfies regulatory requirements for financial institutions. ### Tier 3: External custody integration [#tier-3-external-custody-integration] For institutions with existing custody infrastructure, the platform delegates key management to external providers (Fireblocks, DFNS). The platform initiates signing requests; the custody solution handles key storage, access controls, and audit trails. Organizations select their tier based on security requirements and regulatory obligations. Mixed deployments -- HSM for treasury operations, database keys for automated processes -- are supported. ## Security design principles [#security-design-principles] * **Layered verification**: Identity authentication plus wallet verification ensure compromised credentials alone cannot execute transactions. API key sessions skip wallet verification by design because the key itself is a scoped, rate-limited credential and the authorization factor for machine-to-machine use * **Fail secure**: Invalid authentication, expired sessions, and missing permissions result in denied access * **Audit completeness**: Every authentication event logs with timestamp, method, and result; failed attempts trigger configurable alerting * **Standard protocols**: TOTP (RFC 6238), WebAuthn, and HTTP-only session cookies provide well-understood, audited primitives # Authorization Source: https://docs.settlemint.com/docs/architecture/security/authorization DALP enforces authorization through a dual-layer model: off-chain platform roles via Better Auth control API and console access, while on-chain roles in Solidity contracts govern blockchain operations. 26 distinct roles span platform, people, asset, and system module layers. **Purpose:** How the platform decides what authenticated users and contracts are allowed to do. * **Doc type:** Reference * **What you'll find here:** * Dual-layer permissions model (off-chain + on-chain) * 26 roles across 4 layers * Per-asset permission matrix * Multi-tenant architecture * **Related:** * [Authentication](/docs/architecture/security/authentication) * [Wallet Verification](/docs/architecture/security/wallet-verification) * [Asset Contracts](/docs/architecture/components/asset-contracts) *** ## Authorization architecture [#authorization-architecture] The on-chain `AccessManager` contract is the **authoritative source for all role assignments** in DALP. This is a key architectural invariant: roles granted or revoked on-chain are immediately reflected in the UI — there is no separate off-chain permission database. **How the authority chain works:** 1. Role assignments are made directly on-chain via the `AccessManager` contract 2. Role events (`RoleGranted`, `RoleRevoked`) are emitted and indexed by DALP's chain indexer 3. The UI queries indexed on-chain state — never a separate database — to determine what features and actions to render 4. Write operations require: (1) an active platform session (Better Auth) AND (2) the appropriate on-chain role — neither alone is sufficient 5. Role granted on-chain → indexer processes the event → UI reflects the change immediately **Key invariant:** The chain is the source of truth. If a role is revoked on-chain, the UI hides or disables the corresponding operation without any manual synchronization step. ## Dual-layer permissions [#dual-layer-permissions] Every blockchain operation requires both off-chain and on-chain authorization. Missing either layer results in denial. **Key invariant:** Read operations require only a valid session. Write operations require both the platform permission and the on-chain role. ## Role taxonomy [#role-taxonomy] 26 distinct roles organized into four layers (excluding one deprecated role): | Layer | Scope | Count | Roles | | --------------------- | -------------------------------- | ----- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **1. Platform** | Off-chain (Better Auth) | 3 | owner, admin, member | | **2. System People** | On-chain (`DALPPeopleRoles.sol`) | 9 | systemManager, identityManager, tokenManager, complianceManager, claimPolicyManager, organisationIdentityManager, claimIssuer, auditor, feedsManager | | **3. Per-Asset** | On-chain (`DALPAssetRoles.sol`) | 7 | admin (DEFAULT\_ADMIN\_ROLE), governance, supplyManagement, custodian, emergency, saleAdmin, fundsManager | | **4. System Modules** | On-chain (`DALPSystemRoles.sol`) | 7 | systemModule, identityRegistryModule, tokenFactoryRegistryModule, tokenFactoryModule, addonFactoryRegistryModule, addonFactoryModule, trustedIssuersMetaRegistryModule | ### Layer 1: Platform roles [#layer-1-platform-roles] Organization-scoped, managed by Better Auth. | Role | Capabilities | | ---------- | ----------------------------------------------------------------------- | | **owner** | Full administrative access, role assignment, organization configuration | | **admin** | User management, platform configuration | | **member** | Standard operations based on assigned permissions | ### Layer 2: System people roles [#layer-2-system-people-roles] Assigned to human operators. Defined in `DALPPeopleRoles.sol`. | Role | Responsibilities | | ------------------------------- | --------------------------------------------------------------------- | | **systemManager** | Bootstrap system, manage upgrades, register factories/addons/modules | | **identityManager** | Register and recover identities, manage user onboarding | | **tokenManager** | Deploy and configure tokens | | **complianceManager** | Register compliance modules, configure global settings, manage bypass | | **claimPolicyManager** | Manage trusted issuers and claim topics | | **organisationIdentityManager** | Manage claims for the organisation identity | | **claimIssuer** | Create and attach claims to identity contracts | | **auditor** | View-only: permissions, identities, audit logs, system state | | **feedsManager** | Register, replace, and remove feeds in the FeedsDirectory | > **Note:** `addonManager` from v1 is still defined in `DALPPeopleRoles.sol` for backward compatibility but is deprecated and excluded from the count above. ### Layer 3: Per-asset roles [#layer-3-per-asset-roles] Scoped per token contract. Defined in `DALPAssetRoles.sol`. | Role | Responsibilities | | -------------------------------- | ------------------------------------------------------------------- | | **admin** (DEFAULT\_ADMIN\_ROLE) | Grant and revoke all other per-asset roles | | **governance** | Configure identity registry, compliance modules, features, metadata | | **supplyManagement** | Mint, burn, batch operations, set supply cap | | **custodian** | Freeze/unfreeze, forced transfers, wallet recovery | | **emergency** | Pause/unpause operations, recover stuck ERC20 tokens | | **saleAdmin** | Manage token sale configuration and lifecycle | | **fundsManager** | Withdraw funds from token sales | ### Layer 4: System module roles [#layer-4-system-module-roles] Assigned to contract addresses. Defined in `DALPSystemRoles.sol`. | Role | Responsibilities | | ------------------------------------ | ----------------------------------------------------- | | **systemModule** | Manage system modules, register compliance modules | | **identityRegistryModule** | Modify identity registry storage | | **tokenFactoryRegistryModule** | roleAdmin of tokenFactoryModule | | **tokenFactoryModule** | Add token contracts to compliance allow list | | **addonFactoryRegistryModule** | roleAdmin of addonFactoryModule | | **addonFactoryModule** | Add addon instance contracts to compliance allow list | | **trustedIssuersMetaRegistryModule** | Add trusted issuers to the registry | ## Per-asset permission matrix (summary) [#per-asset-permission-matrix-summary] | Action | Required role | Asset-specific notes | | ---------------------------------------------- | ---------------- | --------------------------------------- | | Set OnchainID / identity registry / compliance | governance | All asset types | | Set features / metadata | governance | DALPAsset only (Configurable extension) | | Set yield schedule / mature bond | governance | Bond only | | Mint / burn / batch operations | supplyManagement | RealEstate and PreciousMetal: no burn | | Set supply cap | supplyManagement | Bond and RealEstate only | | Freeze / forced transfer / recovery | custodian | All asset types | | Pause / unpause / recover ERC20 | emergency | All asset types | | Configure / manage token sale | saleAdmin | Assets with sale addon | | Withdraw sale funds | fundsManager | Assets with sale addon | See [Legacy-Equivalent Presets](/docs/architecture/components/asset-contracts/instrument-profiles) for per-instrument feature and compliance configuration across all seven asset types. Asset-level role assignment for mint, burn, and transfer permissions ## Multi-tenant architecture [#multi-tenant-architecture] The platform supports configurable multi-tenancy through Better Auth's organization system. * **Single-tenant**: All users in one organization. Creation blocked after the first exists. * **Multi-tenant**: Separate organizations with isolated membership, roles, assets, compliance records, and audit trails. **Tenant boundaries:** Isolation enforced at the database query level on every API request. Cross-tenant operations are not possible. # Capital Raise Limit Source: https://docs.settlemint.com/docs/architecture/security/compliance/capital-raise-limit CapitalRaiseLimit module for fiat-denominated gross capital raise caps, fixed or rolling accounting windows, and PriceResolver-backed mint valuation. The CapitalRaiseLimit module caps the gross fiat value raised through minting during a configured accounting window. DALP converts each minted amount through the installed PriceResolver addon before it checks the cap. Related compliance references: * [Compliance overview](/docs/architecture/security/compliance) * [Supply and investor limits](/docs/architecture/security/compliance/supply-investor-limits) * [Issuance volume limit](/docs/architecture/security/compliance/issuance-volume-limit) ## Where this module applies [#where-this-module-applies] | Concern | CapitalRaiseLimit | | --------- | -------------------------------------------------------------- | | Minting | Enforces gross raised fiat cap in the active window | | Transfers | Pass through; transfers do not change the tracker | | Burns | Pass through; burns do not release capacity | | Pricing | Converts mint amount through the installed PriceResolver addon | ## When to use this module [#when-to-use-this-module] Use `capital-raise-limit` when a token needs a periodic fundraising cap expressed in fiat value instead of token units. The module measures gross capital raised through issuance in a fixed or rolling window. It is not: * A token-unit issuance quota. Use [Issuance volume limit](/docs/architecture/security/compliance/issuance-volume-limit). * An absolute outstanding-supply cap. Use [Supply cap](/docs/architecture/security/compliance/supply-cap-collateral). * A net-outstanding-value control; burns do not release capacity. ## Configuration [#configuration] ```solidity struct RaiseLimitConfig { uint256 maxSupply; // Maximum gross raised fiat in the window, normalized to 18 decimals. uint256 periodLength; // Window length in days. Must be 1..730. bool rolling; // true = rolling window, false = fixed window. address priceResolver; // Injected by dapi from the installed PriceResolver addon. uint256 priceTopicId; // Injected by dapi; defaults to keccak256("basePrice"). } ``` | Field | Client supplied? | Mutable after install? | Notes | | --------------- | ---------------- | ---------------------- | --------------------------------------------------------------------------- | | `maxSupply` | Yes | No | Maximum gross raised fiat in the window, stored as 18-decimal fiat. | | `periodLength` | Yes | No | Window length in days. The module rejects `0` and values above `730`. | | `rolling` | Yes | No | Chooses rolling-window or fixed-window accounting. | | `priceResolver` | No | No | Resolved and injected by the DALP API from the indexed PriceResolver addon. | | `priceTopicId` | No | No | Resolved and injected by the DALP API, defaulting to the `basePrice` topic. | Installed configuration is immutable. The dapp disables the form once the module is enabled and shows a notice that a different configuration requires deploying a new module instance. ## PriceResolver prerequisite [#priceresolver-prerequisite] `capital-raise-limit` requires a `PriceResolver` addon on the same system. The dapp checks the addon factory list for `typeId: "price-resolver"` and shows a warning only when the lookup succeeds and confirms the addon is missing. Clients do not provide a resolver address. During current compliance install flows, the DALP API resolves the PriceResolver addon address for the token's system. It then merges the address into the compliance parameters and injects the default price topic ID `keccak256("basePrice")` before ABI encoding. If the DALP API cannot find a PriceResolver addon for the system, the install request fails with `TOKEN_COMPLIANCE_PRICE_RESOLVER_ADDON_NOT_INSTALLED` (`DALP-0474`). The error maps to HTTP 409 (`CONFLICT`) and gives SDK and frontend clients a stable typed error to branch on. This prevents deployment with an absent resolver. The contract also rejects zero `priceResolver` and zero `priceTopicId` during config validation. ## Accounting [#accounting] The module enforces only on mints (`from == address(0)`). For each mint, it calls `PriceResolver.getBasePrice(token, priceTopicId)` and converts the raw token amount into 18-decimal fiat using the token decimals. The mint is allowed only when the active-window gross total plus the new fiat amount stays within `maxSupply`. | Mode | Tracker | Rollover behavior | | ------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | | Fixed | Single current-period total with `periodStart`. | When the period elapses, the current raised fiat reads as `0`; the next mint starts a fresh period. | | Rolling | 730-slot circular daily buffer keyed by day, summed over the configured window. | Daily slots age out as time advances. | Burns and regular transfers do not change the raised-fiat tracker. A burn therefore cannot make room for additional issuance in the same window. ## Failure behavior [#failure-behavior] The module fails closed when pricing is not usable. A mint check fails if the resolver address is zero, if the resolver returns a zero or negative price, or if the resolver call itself reverts. A mint that would exceed the configured raise cap reverts with the module's raise-limit compliance reason. # Country Restrictions Source: https://docs.settlemint.com/docs/architecture/security/compliance/country CountryAllowList and CountryBlockList modules for geographic transfer restrictions. Covers sanctions/OFAC use cases, EU MiCA jurisdiction selection, and ISO 3166-1 country codes. **Purpose:** Reference for country-based compliance modules. * **Doc type:** Reference * **What you'll find here:** * CountryAllowList and CountryBlockList — geographic transfer restrictions * Interface tables showing on-chain capabilities * ISO 3166-1 numeric codes reference * Key invariants, operational signals, and failure modes * **Related:** [Compliance Overview](/docs/architecture/security/compliance), [Identity Verification](/docs/architecture/security/compliance/identity-verification), [Identity Lists](/docs/architecture/security/compliance/identity-lists), [Supply & Investor Limits](/docs/architecture/security/compliance/supply-investor-limits), [Asset Contracts](/docs/architecture/components/asset-contracts) *** ## Where these modules apply [#where-these-modules-apply] | Concern | CountryAllowList | CountryBlockList | | ---------------- | ------------------------ | ------------------------ | | Minting | Checks recipient country | Checks recipient country | | Transfers | Checks recipient country | Checks recipient country | | Burns | — | — | | Forced transfers | — | — | ## Modules [#modules] | Module | Purpose | Configuration | | -------------------- | --------------------------------------------------------- | ----------------------------------------- | | **CountryAllowList** | Only allow transfers to investors from approved countries | Array of ISO 3166-1 numeric country codes | | **CountryBlockList** | Block transfers to investors from prohibited countries | Array of blocked country codes | Country modules check the **recipient's** country code from the identity registry storage. This value is set by KYC providers during identity verification and stored as a claim on the investor's OnchainID identity contract. ### Interface (capabilities) [#interface-capabilities] **CountryAllowList** | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | --------------------- | ---------------------------- | ----------------------------------------- | ---------------------------------------------------- | ----- | ------------------------------------------------ | | `setModuleParameters` | Token admin (via compliance) | Array of ISO 3166-1 numeric country codes | Stores allowed country list | — | Empty list blocks all transfers | | `canTransfer` | Compliance engine | Sender, recipient, amount | Checks recipient's country code against allowed list | — | Country code read from identity registry storage | **CountryBlockList** | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | --------------------- | ---------------------------- | ------------------------------ | ---------------------------------------------------- | ----- | ------------------------------------------------ | | `setModuleParameters` | Token admin (via compliance) | Array of blocked country codes | Stores blocked country list | — | Empty list blocks none | | `canTransfer` | Compliance engine | Sender, recipient, amount | Checks recipient's country code against blocked list | — | Country code read from identity registry storage | Country allowlist configuration for geographic transfer restrictions ## Use cases [#use-cases] | Use case | Module | Example | | ------------------------ | ---------------- | ----------------------------- | | MiCA EU compliance | CountryAllowList | 27 EU member state codes | | OFAC sanctions screening | CountryBlockList | Sanctioned jurisdiction codes | | Reg D (US only) | CountryAllowList | `[840]` (United States) | | Singapore MAS | CountryAllowList | `[702]` (Singapore) | | Japan FSA | CountryAllowList | `[392]` (Japan) | | UK FCA | CountryAllowList | `[826]` (United Kingdom) | ## ISO 3166-1 numeric codes [#iso-3166-1-numeric-codes] Selected common codes: | Country | Code | | -------------- | ---- | | United States | 840 | | United Kingdom | 826 | | Japan | 392 | | Singapore | 702 | | Germany | 276 | | France | 250 | Full EU 27 member state codes are included in the MiCA EU Standard template. Use the full ISO 3166-1 numeric code list for other jurisdictions. ## Key invariants [#key-invariants] * Country check applies to the **recipient** (buyer/transferee), not the sender * Country code must be set in the identity registry before any transfer is attempted * Using CountryAllowList and CountryBlockList together creates a combination restriction (both must pass) * An empty allow list blocks all transfers; an empty block list blocks none ## Operational signals [#operational-signals] No events emitted by these modules. Monitor for `ComplianceCheckFailed` revert errors in failed transactions when transfers violate country restrictions. ## Failure modes & edge cases [#failure-modes--edge-cases] * Country code not set in identity registry — transfer reverts (recipient cannot be checked) * Sanctioned country added to block list after tokens already transferred — existing holders are not affected until they attempt a new transfer * Using both CountryAllowList and CountryBlockList creates a combination restriction — both must pass independently ## See also [#see-also] * [Compliance Overview](/docs/architecture/security/compliance) — module architecture and regulatory templates * [Identity Verification](/docs/architecture/security/compliance/identity-verification) — verifying that country claims are properly attested * [Identity Lists](/docs/architecture/security/compliance/identity-lists) — granular identity and address-level access control * [Supply & Investor Limits](/docs/architecture/security/compliance/supply-investor-limits) — InvestorCount enforces per-country investor limits # Identity Lists Source: https://docs.settlemint.com/docs/architecture/security/compliance/identity-lists IdentityAllowList, IdentityBlockList, and AddressBlockList modules for granular access control. Covers private placement whitelists, sanctions screening, and the distinction between identity-level and address-level blocking. **Purpose:** Reference for identity-based list compliance modules. * **Doc type:** Reference * **What you'll find here:** * IdentityAllowList, IdentityBlockList, and AddressBlockList — granular access control * Interface tables showing on-chain capabilities * Identity-level vs address-level blocking comparison * Key invariants, operational signals, and failure modes * **Related:** [Compliance Overview](/docs/architecture/security/compliance), [Identity Verification](/docs/architecture/security/compliance/identity-verification), [Country Restrictions](/docs/architecture/security/compliance/country), [Supply & Investor Limits](/docs/architecture/security/compliance/supply-investor-limits), [TimeLock](/docs/architecture/security/compliance/timelock), [Asset Contracts](/docs/architecture/components/asset-contracts) *** ## Where these modules apply [#where-these-modules-apply] | Concern | IdentityAllowList | IdentityBlockList | AddressBlockList | | ---------------- | ------------------------- | ------------------------- | ------------------------ | | Minting | Checks recipient identity | Checks recipient identity | Checks recipient address | | Transfers | Checks recipient identity | Checks recipient identity | Checks recipient address | | Burns | — | — | — | | Forced transfers | — | — | — | ## Modules [#modules] | Module | Granularity | Purpose | | --------------------- | ----------------- | ------------------------------------------------------------ | | **IdentityAllowList** | Identity contract | Whitelist specific investors by OnchainID contract address | | **IdentityBlockList** | Identity contract | Block specific investors by OnchainID contract address | | **AddressBlockList** | Wallet address | Block specific wallet addresses (without requiring identity) | ## Identity-level vs address-level [#identity-level-vs-address-level] | Aspect | IdentityAllowList / IdentityBlockList | AddressBlockList | | ---------------------- | -------------------------------------------- | -------------------------------------------- | | Granularity | Per identity (investor) | Per wallet address | | Covers all wallets | Yes — one identity may have multiple wallets | No — wallet-specific | | Requires identity | Yes | No | | Bypassed by new wallet | No — identity persists across wallets | Yes — new wallet address bypasses | | Primary use case | Private placements, institutional-only | Sanctions lists (OFAC SDN), fraud prevention | **When to block at identity vs address:** * **Identity-level blocking** fits compliance violations or legal disputes because it follows the investor across all their registered wallets * **Address-level blocking** is appropriate for immediate sanctions screening where speed matters and the specific wallet address is the sanctioned entity ### Interface (capabilities) [#interface-capabilities] **IdentityAllowList** | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | --------------------- | ---------------------------- | ------------------------------------- | ------------------------------------------------ | ----- | ------------------------------- | | `setModuleParameters` | Token admin (via compliance) | Array of OnchainID contract addresses | Stores allowed identity list | — | Empty list blocks all transfers | | `canTransfer` | Compliance engine | Sender, recipient, amount | Checks recipient's identity against allowed list | — | Identity resolved from registry | **IdentityBlockList** | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | --------------------- | ---------------------------- | ------------------------------------- | ------------------------------------------------ | ----- | ------------------------------- | | `setModuleParameters` | Token admin (via compliance) | Array of OnchainID contract addresses | Stores blocked identity list | — | Empty list blocks none | | `canTransfer` | Compliance engine | Sender, recipient, amount | Checks recipient's identity against blocked list | — | Identity resolved from registry | **AddressBlockList** | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | --------------------- | ---------------------------- | ------------------------- | --------------------------------------------------------- | ----- | --------------------------------------- | | `setModuleParameters` | Token admin (via compliance) | Array of wallet addresses | Stores blocked address list | — | No identity lookup required | | `canTransfer` | Compliance engine | Sender, recipient, amount | Checks **recipient** (`_to`) address against blocked list | — | Reverts with "Receiver address blocked" | ## Use cases [#use-cases] ### IdentityAllowList — private placement [#identityallowlist--private-placement] Restrict a token to pre-approved investors by OnchainID address. Used for Reg D 506(b) private placements, institutional-only offerings, and friends-and-family rounds. ### IdentityBlockList — compliance violations [#identityblocklist--compliance-violations] Block a specific investor across all their wallets. Used for compliance alerts, failed re-KYC, and court orders. ### AddressBlockList — sanctions screening [#addressblocklist--sanctions-screening] Block specific wallet addresses matching OFAC SDN or similar sanctions lists, known fraud addresses, or mixer/tumbler addresses flagged by on-chain analytics. ## Key invariants [#key-invariants] * IdentityAllowList with an empty list blocks ALL transfers * IdentityBlockList with an empty list blocks NONE * AddressBlockList checks the **recipient** wallet address directly, without identity lookup * These modules can be combined: e.g., IdentityAllowList for private placement + AddressBlockList for OFAC screening ## Operational signals [#operational-signals] No events emitted by these modules. Monitor for `ComplianceCheckFailed` revert errors in failed transactions when transfers violate list restrictions. ## Failure modes & edge cases [#failure-modes--edge-cases] * IdentityAllowList with an empty list silently blocks all transfers — ensure the list is populated before activating * Adding an investor to IdentityBlockList does not freeze their existing balance — they cannot receive new tokens but can still transfer out (unless other modules block) * AddressBlockList can be bypassed by the blocked investor using a different wallet address — use IdentityBlockList for persistent blocking ## See also [#see-also] * [Compliance Overview](/docs/architecture/security/compliance) — module architecture and regulatory templates * [Identity Verification](/docs/architecture/security/compliance/identity-verification) — claim-based verification alternative to explicit lists * [Country Restrictions](/docs/architecture/security/compliance/country) — jurisdiction-level blocking * [Supply & Investor Limits](/docs/architecture/security/compliance/supply-investor-limits) — InvestorCount limits complement identity lists * [TimeLock](/docs/architecture/security/compliance/timelock) — holding period enforcement # Identity Verification Source: https://docs.settlemint.com/docs/architecture/security/compliance/identity-verification SMARTIdentityVerification module with full RPN expression system. Covers KYC/AML/ACCREDITED/CONTRACT claim topics, AND/OR/NOT operators, exemption support, and real-world expression examples. **Purpose:** Reference for the SMARTIdentityVerification module and RPN expression system. * **Doc type:** Reference * **What you'll find here:** * SMARTIdentityVerification — claim-based transfer verification * Interface table showing on-chain capabilities * Full RPN expression system with regulatory examples * Key invariants, operational signals, and failure modes * **Related:** [Compliance Overview](/docs/architecture/security/compliance), [Identity Lists](/docs/architecture/security/compliance/identity-lists), [Supply & Investor Limits](/docs/architecture/security/compliance/supply-investor-limits), [Transfer Approval](/docs/architecture/security/compliance/transfer-approval), [TimeLock](/docs/architecture/security/compliance/timelock) *** ## SMARTIdentityVerification [#smartidentityverification] The most expressive compliance module in DALP. Instead of simple allow/block lists, it evaluates **logical expressions over identity claims** to determine whether a transfer is permitted. A claim is an on-chain attestation issued by a trusted issuer (KYC provider, compliance officer) and stored on the investor's OnchainID identity contract. The module checks whether the investor's identity holds valid (non-expired) claims matching the configured expression. ### Interface (capabilities) [#interface-capabilities] | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | --------------------- | ---------------------------- | ---------------------------------------------------- | -------------------------------------------------------- | ----- | ------------------------------------------------------------ | | `setModuleParameters` | Token admin (via compliance) | RPN expression (array of claim topics and operators) | Stores verification expression | — | Expression validated at configuration time | | `canTransfer` | Compliance engine | Sender, recipient, amount | Evaluates expression against recipient's identity claims | — | Checks both sender and recipient; claims must be non-expired | Verification topics for identity-based compliance checks ## Claim topics [#claim-topics] Standard claim topics used across DALP compliance configurations: | Topic | Meaning | Typical issuer | | ---------------- | --------------------------------------------------------- | ------------------- | | **KYC** | Know Your Customer identity verification completed | KYC provider | | **AML** | Anti-Money Laundering screening passed | Compliance provider | | **ACCREDITED** | Investor meets accredited investor criteria (e.g., Reg D) | Compliance officer | | **CONTRACT** | Investor is a legal entity (not a natural person) | KYC provider | | **JURISDICTION** | Investor is resident in a qualifying jurisdiction | KYC provider | Custom claim topics can be defined by the compliance team and registered in the Topic Scheme Registry. ## RPN expression system [#rpn-expression-system] The module evaluates expressions in **Reverse Polish Notation (postfix)**. This allows arbitrary logical combinations of claim checks without parentheses. ### Expression components [#expression-components] | Node type | Behavior | | --------- | ----------------------------------------------------------------------------- | | **TOPIC** | Push `true` if identity holds a valid claim for that topic; `false` otherwise | | **AND** | Pop two values, push logical AND | | **OR** | Pop two values, push logical OR | | **NOT** | Pop one value, push logical inverse | ### Expression examples [#expression-examples] | Requirement | Postfix expression | | ------------------------------------------ | ---------------------------------------------------- | | KYC AND AML | `[KYC, AML, AND]` | | ACCREDITED investors only | `[ACCREDITED]` | | Corporate entities OR verified individuals | `[CONTRACT, KYC, AML, AND, OR]` | | Full KYC stack | `[ACCREDITED, KYC, AML, AND, JURISDICTION, AND, OR]` | | KYC but NOT sanctioned | `[KYC, SANCTIONED, NOT, AND]` | ### Real-world regulatory configurations [#real-world-regulatory-configurations] | Regulation | Expression | Meaning | | ---------------- | --------------------------------- | ---------------------------------------------------------------------------------------------- | | MiCA EU Standard | `[KYC, AML, AND]` | Both KYC and AML claims required | | Reg D 506(b) | `[ACCREDITED, KYC, AML, AND, OR]` | Accredited investors OR (KYC AND AML) — allows up to 35 non-accredited sophisticated investors | | Reg D 506(c) | `[ACCREDITED]` | Only accredited investors (strict) | | Japan FSA | `[CONTRACT, KYC, AML, AND, OR]` | QII (corporate) or (KYC AND AML) — Qualified Institutional Investor exemption | ## Exemption expressions [#exemption-expressions] The same RPN system is used by other modules to configure **exemptions** — investors that bypass a module's restriction: * **TimeLock**: investors matching the exemption expression skip the holding period * **TransferApproval**: investors matching the exemption expression skip the pre-approval requirement * **InvestorCount**: the `topicFilter` expression determines which investors are **counted** (not which are blocked) This allows sophisticated exemption logic: e.g., "QII investors are exempt from the 180-day lock, but retail investors must hold for the full period." ## Key invariants [#key-invariants] * Both sender and recipient must hold valid claims matching the configured expression * Claims are checked for expiry — expired claims fail verification even if the topic matches * The expression is evaluated as a stack machine — malformed expressions revert at configuration time via `validateParameters` * Trusted issuers are configured globally, not per-token — a claim from any registered trusted issuer is accepted ## Operational signals [#operational-signals] No events emitted by this module. Monitor for `ComplianceCheckFailed` revert errors in failed transactions when identity verification fails. ## Failure modes & edge cases [#failure-modes--edge-cases] * Investor's claim expires between verification check and transfer execution — transfer reverts * Trusted issuer removed after claims were issued — existing claims from that issuer are no longer accepted * Empty expression (no claim topics) — all transfers pass verification (effectively disables the module) ## See also [#see-also] * [Compliance Overview](/docs/architecture/security/compliance) — module architecture and regulatory templates * [Supply & Investor Limits](/docs/architecture/security/compliance/supply-investor-limits) — InvestorCount topicFilter uses the same expression system * [Transfer Approval](/docs/architecture/security/compliance/transfer-approval) — exemption expressions for transfer approval * [TimeLock](/docs/architecture/security/compliance/timelock) — identity-based exemptions from holding periods # Overview Source: https://docs.settlemint.com/docs/architecture/security/compliance Architecture of DALP's modular compliance engine, system-seeded regulatory templates, and compliance modules covering geographic, identity, supply, collateral, pricing, and time-lock controls. **Purpose:** Explain the compliance module architecture and provide the primary on-ramp for architects choosing a regulatory framework. * **Doc type:** Explanation * **What you'll find here:** * Compliance architecture (token → compliance → modules) * 7 system-seeded regulatory templates — start here * Module catalog index with links to details * **Related:** * [Identity & Compliance](/docs/architecture/security/identity-compliance) * [Compliance Transfer Flow](/docs/architecture/flows/compliance-transfer) * [Country Restrictions](/docs/architecture/security/compliance/country) * [Identity Lists](/docs/architecture/security/compliance/identity-lists) * [Identity Verification](/docs/architecture/security/compliance/identity-verification) * [Supply & Investor Limits](/docs/architecture/security/compliance/supply-investor-limits) * [Capital Raise Limit](/docs/architecture/security/compliance/capital-raise-limit) * [Transfer Approval](/docs/architecture/security/compliance/transfer-approval) * [Supply Cap & Collateral](/docs/architecture/security/compliance/supply-cap-collateral) * [TimeLock](/docs/architecture/security/compliance/timelock) *** > **Reading guide:** This page is the compliance module **catalog** (Reference). For the identity system architecture, trust model, and enforcement lifecycle, start at [Identity & Compliance](/docs/architecture/security/identity-compliance). ## Architecture [#architecture] Compliance modules form the enforcement layer of the SMART Protocol. Every token transfer calls the compliance engine, which evaluates all configured modules in sequence. A single module veto blocks the transfer. ``` Token transfer request │ ▼ Compliance Engine (per-token orchestrator) │ ├── Module 1: Country Allow List ──► PASS / FAIL ├── Module 2: Identity Verification ──► PASS / FAIL ├── Module 3: Token Supply Limit ──► PASS / FAIL └── Module N: ... ──► PASS / FAIL │ ▼ Transfer proceeds only if ALL modules pass ``` ### Configuration model [#configuration-model] | Layer | Scope | Examples | | --------- | --------------------- | ---------------------------------------------------------------------- | | Global | Shared infrastructure | Module deployment, trusted issuer registry, topic schemes | | Per-token | Business rules | Which modules to activate, country codes, investor limits, expressions | A single deployed module instance serves all tokens. Each token configures its own parameters encoded as ABI-encoded bytes. Modules validate parameters via `validateParameters` before accepting configuration. *** Compliance scale metrics ## Regulatory templates (start here) [#regulatory-templates-start-here] DALP ships 7 system-seeded compliance templates. These are read-only and available to all organizations. Admins apply a template as a starting point, then customize for their specific needs. **How to use templates:** Select the jurisdiction that matches your offering, apply the template to your DALPAsset token, then review and adjust the module parameters for your specific case. | Template | Jurisdiction | Modules | Key constraints | | ------------------------------------ | ----------------- | ---------------------------------------------------------------------- | ------------------------------------------------------ | | **MiCA EU Standard** | EU (27 countries) | Identity verification + Country allow list + Token supply limit | 8M EUR cap, 365-day rolling window | | **Reg D 506(b) — Private Placement** | United States | Identity verification + Country allow list + Investor count | Max 2,000 investors, US only | | **Reg D 506(c) — Accredited Only** | United States | Identity verification + Country allow list + Transfer approval | All purchasers must be accredited, 24h approval expiry | | **MAS Singapore — Capital Markets** | Singapore | Identity verification + Country allow list + Investor count + TimeLock | Max 50 investors, 180-day holding period | | **UK FCA — Securities** | United Kingdom | Identity verification + Country allow list + Investor count | Max 150 investors, UK only | | **Japan FSA — Crypto Assets** | Japan | Identity verification + Country allow list + Transfer approval | Japan only, 7-day approval expiry | | **Reg CF — Crowdfunding** | United States | Identity verification + Country allow list + Token supply limit | $5M cap, 365-day rolling window | Templates are system-wide and read-only. Organizations can create custom templates for non-standard jurisdictions or novel instrument structures. *** Compliance module configuration during asset creation ## Module index [#module-index] | Module | Category | Details | | ------------------------------------------------------ | --------------------------------------- | ----------------------------------------------------------------------------------------- | | CountryAllowList, CountryBlockList | Geographic restrictions | [Country restrictions](/docs/architecture/security/compliance/country) | | IdentityAllowList, IdentityBlockList, AddressBlockList | Identity-based access | [Identity lists](/docs/architecture/security/compliance/identity-lists) | | SMARTIdentityVerification | Claim verification with RPN expressions | [Identity verification](/docs/architecture/security/compliance/identity-verification) | | TokenSupplyLimit, InvestorCount | Supply and investor limits | [Supply & investor limits](/docs/architecture/security/compliance/supply-investor-limits) | | CapitalRaiseLimit | Fiat-denominated gross raise limit | [Capital raise limit](/docs/architecture/security/compliance/capital-raise-limit) | | TransferApproval | Transfer approval | [Transfer approval](/docs/architecture/security/compliance/transfer-approval) | | CappedComplianceModule, CollateralComplianceModule | Supply cap and collateral enforcement | [Supply cap & collateral](/docs/architecture/security/compliance/supply-cap-collateral) | | TimeLock | Holding period enforcement | [TimeLock](/docs/architecture/security/compliance/timelock) | *** ## Where modules apply [#where-modules-apply] | Module | Transfer | Mint | Burn | Pre-check (`canTransfer`) | Post-hook (`transferred`) | | --------------------- | -------- | ---- | ---- | --------------------------------- | ----------------------------- | | Country restrictions | ✓ | ✓ | — | Validates country codes | — | | Identity verification | ✓ | ✓ | — | Evaluates RPN claim expression | — | | Identity lists | ✓ | ✓ | — | Checks allow/block lists | — | | Supply limits | ✓ | ✓ | — | Checks accumulation against cap | Updates rolling window (mint) | | Capital raise limit | — | ✓ | — | Checks gross raised fiat cap | Updates fixed/rolling tracker | | Investor count | ✓ | ✓ | — | Checks holder count limit | Increments/decrements count | | Transfer approval | ✓ | — | — | Checks pre-authorization exists | Consumes approval | | TimeLock | ✓ | — | — | Checks holding period (FIFO) | Records acquisition timestamp | | Supply cap | ✓ | ✓ | — | Checks circulating supply cap | — | | Collateral | ✓ | ✓ | — | Verifies ERC-735 collateral proof | — | # Issuance Volume Limit Source: https://docs.settlemint.com/docs/architecture/security/compliance/issuance-volume-limit IssuanceVolumeLimit module for periodic token-unit issuance quotas, newest-first burn attribution, and period-mode immutability. The IssuanceVolumeLimit module caps how many token units may be minted in a fixed or rolling time window. Use it for issuance quotas, tranche releases, vesting schedules, or operational throttles where the limit is expressed in token units rather than fiat value. Related compliance references: * [Compliance overview](/docs/architecture/security/compliance) * [Supply and investor limits](/docs/architecture/security/compliance/supply-investor-limits) * [Supply cap and collateral](/docs/architecture/security/compliance/supply-cap-collateral) ## Where this module applies [#where-this-module-applies] | Concern | IssuanceVolumeLimit | | ---------------- | --------------------------------------------------------- | | Minting | Enforces periodic issuance cap (rolling or fixed window) | | Transfers | Not enforced | | Burns | Releases capacity within the window (newest-first / LIFO) | | Forced transfers | Not enforced | ## When to use this module [#when-to-use-this-module] Use `IssuanceVolumeLimit` when you need to answer *"how many token units may be issued in a given window?"*. For example: * Dilution control (no more than 100k tokens issued per quarter) * Vesting / distribution rate limits * Tranche release programs * Operational throttling It is **not**: * An absolute outstanding-supply cap. Use [Capped](/docs/architecture/security/compliance/supply-cap-collateral) * A fiat-denominated fundraising cap. Use [Capital Raise Limit](/docs/architecture/security/compliance/capital-raise-limit) * A mark-to-market outstanding value control * A backing/collateral enforcement. Use [Collateral](/docs/architecture/security/compliance/supply-cap-collateral) ## Interface (capabilities) [#interface-capabilities] | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | ------------------------- | -------------------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------- | | `initialize` | Compliance module registry | engine, `(maxSupply, periodLength, rolling)` | Stores config; rejects `maxSupply == 0`, `periodLength == 0`, or `rolling=true` with `periodLength > 730` | None | One-shot; `Initializable` guard prevents re-initialization | | `updateConfig` | Compliance engine / admin | `(maxSupply, periodLength, rolling)` | Updates only `maxSupply`; reverts if the new config changes `periodLength` or `rolling` from init values | Engine `ModuleConfigUpdated` | `periodLength` and `rolling` are immutable after init. See [Configuration updates](#configuration-updates) | | `canTransfer` (mint path) | Compliance engine | token, from, to, amount | Reverts with `COMPLIANCE_CHECK_REASON_EXCEEDS_QUOTA` if current in-window supply + `amount` > `maxSupply` | None | Only enforced on mints (`from == address(0)`); transfers and burns pass through | | `created` | Compliance engine | token, to, amount | Adds `amount` to today's bucket (rolling) or active period total (fixed) | None | Rolling mode uses a fixed 730-slot circular day buffer | | `destroyed` | Compliance engine | token, from, amount | Releases capacity; unattributed remainder increments `absorbedBurnExcess` | `BurnExceededWindow` (when remainder > 0) | Rolling burns walk newest-first (LIFO). See [Burn attribution](#burn-attribution) | ## Configuration [#configuration] ```solidity struct IssuanceVolumeLimitConfig { uint256 maxSupply; // Per-period cap in raw token units (with token decimals). Must be > 0. uint256 periodLength; // Window length in days. Must be > 0. Locked after init. bool rolling; // true = rolling window, false = fixed window. Locked after init. } ``` | Field | Mutable after init? | Notes | | -------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `maxSupply` | Yes | Can be raised or tightened in place via `updateConfig`. Tightening does not retroactively revert past mints; it constrains future mints only. | | `periodLength` | **No** | In fixed mode there is no per-day detail to reinterpret against a new boundary. In rolling mode an expand can instantly block the next mint. To run with a different period, deploy a new module instance. | | `rolling` | **No** | Different storage paths; flipping it would invalidate the tracker. | ## Mint accounting [#mint-accounting] | Mode | Tracker | Rollover behavior | | ------------------------ | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | Rolling (`rolling=true`) | Fixed 730-slot circular daily buffer. Mints accumulate into today's slot. | Slots older than `periodLength` days naturally age out of the read window as time advances. | | Fixed (`rolling=false`) | Single `(periodStart, totalSupply)` pair. Mints accumulate into the active period. | When `block.timestamp >= periodStart + periodLength * 1 days`, the period has rolled over; the next mint starts a new period. | ### Active period precise definition (fixed mode) [#active-period-precise-definition-fixed-mode] The active period is the half-open interval `[periodStart, periodStart + periodLength * 1 days)`. At exactly `t == periodStart + periodLength * 1 days`, the old period is **no longer active**, and a new period has not yet been initialized. `periodStart` is only updated on the next mint that triggers the tracker update. See the NatSpec on `_isFixedPeriodActive` for the exact semantics. ## Burn attribution [#burn-attribution] In rolling mode, burns release capacity using **newest-first (LIFO) attribution**. The algorithm walks days backwards from `currentDay`, consuming the newest non-empty daily slot first until the burn amount is fully attributed. Rationale (captured in the contract NatSpec): * Deterministic and easy to explain to auditors. * Fulfills the *"burns release capacity"* promise across the whole active window. * Recent issuance is the most flexible part of the budget: it has had the least time to settle into downstream accounting, distribution, or regulatory reporting, so it is the most defensible to retract. * FIFO (oldest-first) would be counter-productive. Oldest in-window slots are about to roll out of the window naturally on the next aging tick, so subtracting from them yields zero practical headroom. ### Overflow behavior [#overflow-behavior] When a burn cannot be fully attributed (tracker empty, burn exceeds all in-window supply, or fixed mode outside an active period), the unattributed remainder is **silently dropped from tracker accounting**. The burn itself succeeds at the token layer; the module simply cannot release capacity it never tracked (e.g., tokens minted before the current rolling window). Two operational signals expose this: | Signal | Shape | Use | | -------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | | `BurnExceededWindow` event | `(uint256 indexed attempted, uint256 indexed absorbed, uint256 indexed windowSpan)` | Indexed off-chain; trip alerts when the absorbed fraction is non-trivial | | `absorbedBurnExcess()` | `uint256` view, monotonic | Cumulative on-chain counter of all absorbed remainders; never decremented | ## Configuration updates [#configuration-updates] Only `maxSupply` may be updated in place after initialization. Any attempt to change `periodLength` or `rolling` via `updateConfig` reverts with `InvalidConfig("... cannot be changed; redeploy module")`. The compliance engine's standard `ModuleConfigUpdated` event still fires. The module does not suppress it, but the module itself does not mutate its tracker. Re-submitting an unchanged config (or a same-value `maxSupply`) is accepted as a no-op: no tracker mutation, no module-level event. The dapp UI mirrors this: when the module is already enabled, `periodLength` and `rolling` inputs are disabled with helper copy explaining that the operator must remove and re-add the module to change them. ## Availability and type ID [#availability-and-type-id] Operators see this module as **Issuance Volume Limit**. Deployments must enable the module before operators can add it to a token or template. On chain, the current module hash is derived from `issuance-volume-limit__experimental__`. # Supply Cap & Collateral Source: https://docs.settlemint.com/docs/architecture/security/compliance/supply-cap-collateral CappedComplianceModule and CollateralComplianceModule — simple circulating supply cap enforcement and ERC-735 claim-based collateral requirements for minting. **Purpose:** Reference for supply cap and collateral enforcement compliance modules. * **Doc type:** Reference * **What you'll find here:** * CappedComplianceModule — circulating supply cap for minting * CollateralComplianceModule — ERC-735 claim-based collateral enforcement * Interface tables showing on-chain capabilities * Key invariants and operational signals for both modules * **Related:** [Compliance Overview](/docs/architecture/security/compliance), [Supply & Investor Limits](/docs/architecture/security/compliance/supply-investor-limits), [Transfer Approval](/docs/architecture/security/compliance/transfer-approval), [Asset Contracts](/docs/architecture/components/asset-contracts) *** ## Where these modules apply [#where-these-modules-apply] | Concern | CappedComplianceModule | CollateralComplianceModule | | ---------------- | --------------------------------------------- | -------------------------- | | Minting | Enforces circulating supply cap | Checks collateral ratio | | Transfers | — | — | | Burns | Frees up capacity (live `totalSupply()` read) | — | | Forced transfers | — | — | ## CappedComplianceModule [#cappedcompliancemodule] Enforces a maximum circulating supply cap for minting operations. ### Interface (capabilities) [#interface-capabilities] | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | ------------------------- | ---------------------------- | ------------------------- | ------------------------------------------------------------------ | ----- | ---------------------------------------------------------------------------- | | `setModuleParameters` | Token admin (via compliance) | `maxSupply` (`uint256`) | Stores supply cap; `validateParameters` reverts if `maxSupply = 0` | — | Cap must be a positive value | | `canTransfer` (mint path) | Compliance engine | Sender, recipient, amount | Checks `totalSupply() + mintAmount <= maxSupply` | — | Only enforced on mints; reads live `totalSupply()` so burns free up capacity | ### Configuration [#configuration] | Parameter | Type | Description | | ----------- | --------- | --------------------------------------------- | | `maxSupply` | `uint256` | Maximum circulating supply in raw token units | ### Use cases [#use-cases] * **Bond issuance caps** — limit total outstanding bonds to a fixed amount * **Fixed supply instruments** — real estate tokens representing a property valuation * **Regulatory caps** — jurisdiction-specific maximum issuance limits ### Key invariants [#key-invariants] * Burns free up capacity — the cap tracks live circulating supply via `totalSupply()`, not lifetime minted * Calling `setModuleParameters` with `maxSupply = 0` reverts; the cap must be a positive value * Mint-only enforcement: transfers between existing holders are unaffected * Forced transfers do not affect the cap (supply unchanged) ### Operational signals [#operational-signals] No events emitted by this module. Monitor for `ComplianceCheckFailed` revert errors in failed transactions when minting exceeds the cap. ### Failure modes & edge cases [#failure-modes--edge-cases] * Concurrent mint transactions may both pass the `totalSupply()` check if submitted in the same block — final state depends on execution order * Reducing `maxSupply` below current `totalSupply()` does not burn tokens — it prevents further minting until supply decreases via burns *** Supply cap and collateral requirement configuration ## CollateralComplianceModule [#collateralcompliancemodule] Enforces collateral requirements for minting via on-chain identity claims (ERC-735). ### Interface (capabilities) [#interface-capabilities-1] | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | ------------------------- | ---------------------------- | -------------------------------------------- | --------------------------------------------------------------- | ----- | ------------------------------------------------------------------------ | | `setModuleParameters` | Token admin (via compliance) | `proofTopic`, `ratioBps`, `trustedIssuers[]` | Stores collateral config; `ratioBps = 0` disables enforcement | — | `proofTopic` is an ERC-735 claim topic | | `canTransfer` (mint path) | Compliance engine | Sender, recipient, amount | Checks that post-mint supply does not exceed collateral x ratio | — | Collateral amount read from identity claims; expired claims are rejected | ### Configuration [#configuration-1] | Parameter | Type | Description | | ---------------- | ----------- | ---------------------------------------------------------------------------- | | `proofTopic` | `uint256` | ERC-735 claim topic representing collateral proof | | `ratioBps` | `uint16` | Ratio in basis points (10,000 = 100%, 20,000 = 200%; 0 disables enforcement) | | `trustedIssuers` | `address[]` | Optional additional trusted issuers for collateral claims | ### Use cases [#use-cases-1] * **StableCoin collateral backing** — ensure minting cannot exceed on-chain proof of reserves (100% collateral ratio) * **Over-collateralized tokens** — set ratio above 10,000 (e.g., 15,000 = 150% collateral requirement) * **Deposit tokens** — collateral-backed tokenized deposits with proof-of-reserves ### Key invariants [#key-invariants-1] * Collateral claims carry an expiry timestamp — claims at or past expiry are rejected * Issuers must renew claims proactively or minting halts * Mint-only enforcement: transfers between existing holders are unaffected * Setting `ratioBps = 0` disables collateral checking entirely ### Operational signals [#operational-signals-1] No events emitted by this module. Monitor for `ComplianceCheckFailed` revert errors in failed transactions when minting exceeds the collateral ratio. Monitor claim expiry timestamps — approaching expiry requires issuer renewal to avoid minting disruption. ### Failure modes & edge cases [#failure-modes--edge-cases-1] * Collateral claim expires without renewal — all minting halts until a new claim is issued by a trusted issuer * Multiple trusted issuers with conflicting collateral amounts — the module uses the first valid (non-expired) claim found ## See also [#see-also] * [Compliance Overview](/docs/architecture/security/compliance) — module architecture and regulatory templates * [Identity Verification](/docs/architecture/security/compliance/identity-verification) — ERC-735 claim system also used for on-chain proof-of-reserves claims * [Legacy-Equivalent Presets](/docs/architecture/components/asset-contracts/instrument-profiles) — Bond and StableCoin presets reference these modules # Supply & Investor Limits Source: https://docs.settlemint.com/docs/architecture/security/compliance/supply-investor-limits TokenSupplyLimit and InvestorCount modules — time-based and rolling supply caps with currency conversion, and unique holder limits with per-country granularity. **Purpose:** Reference for supply and investor limit compliance modules. * **Doc type:** Reference * **What you'll find here:** * TokenSupplyLimit — time-based and rolling supply caps with currency conversion * InvestorCount — unique holder limits with per-country granularity * Interface tables showing on-chain capabilities * Key invariants and operational signals for both modules * **Related:** [Compliance Overview](/docs/architecture/security/compliance), [Transfer Approval](/docs/architecture/security/compliance/transfer-approval), [Supply Cap & Collateral](/docs/architecture/security/compliance/supply-cap-collateral), [Identity Verification](/docs/architecture/security/compliance/identity-verification), [Asset Contracts](/docs/architecture/components/asset-contracts) *** ## Where these modules apply [#where-these-modules-apply] | Concern | TokenSupplyLimit | InvestorCount | | ---------------- | ----------------------------------------- | ------------------ | | Minting | Enforces cap (lifetime / fixed / rolling) | Tracks new holders | | Transfers | — | Tracks new holders | | Burns | — | Decrements count | | Forced transfers | — | Tracks new holders | ## TokenSupplyLimit [#tokensupplylimit] Enforces maximum token supply limits, with optional base-price conversion for EUR/USD-denominated caps. ### Interface (capabilities) [#interface-capabilities] | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | ------------------------- | ---------------------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------- | ----- | ---------------------------------------------------------------------- | | `setModuleParameters` | Token admin (via compliance) | `maxSupply`, `limitType`, `period`, `useBasePrice` | Stores supply-limit config; `validateParameters` reverts if `maxSupply = 0` | — | Calling with `maxSupply = 0` reverts; the cap must be a positive value | | `canTransfer` (mint path) | Compliance engine | Sender, recipient, amount | Checks cumulative supply against limit (converts via price claim if `useBasePrice`) | — | Only enforced on mints (`from == address(0)`) | ### Supply limit types [#supply-limit-types] | Type | Behavior | Use case | | --------------- | -------------------------------------- | ----------------------------------------------- | | LIFETIME | Total cap across the token's lifetime | MiCA EUR 8M asset-referenced token limit | | FIXED\_PERIOD | Cap resets at the start of each period | Quarterly fundraising caps | | ROLLING\_PERIOD | Sliding window of last N days | Continuous monitoring (rolling 12-month limits) | ### Base currency conversion [#base-currency-conversion] When `useBasePrice` is enabled, the module converts token amounts using on-chain price claims before checking against the supply limit. This enables EUR- or USD-denominated caps (e.g., MiCA 8M EUR lifetime limit) on tokens priced in native units. ### Key invariants [#key-invariants] * Supply limit applies to **minting** operations only; transfers and burns are not checked * ROLLING\_PERIOD uses a sliding window — older mints fall off as time passes * FIXED\_PERIOD resets at the start of each new period, regardless of previous minting * Calling `setModuleParameters` with `maxSupply = 0` reverts; the cap must be a positive value ### Operational signals [#operational-signals] No events emitted by this module. Monitor for `ComplianceCheckFailed` revert errors in failed transactions when minting exceeds the configured limit. ### Failure modes & edge cases [#failure-modes--edge-cases] * Price feed stale or missing when `useBasePrice` is enabled — minting reverts until a valid price claim exists * ROLLING\_PERIOD window boundary — mints near the window edge may succeed or fail depending on when older mints age out *** ## InvestorCount [#investorcount] Restricts the number of unique token holders. The `topicFilter` determines which investors are **counted**, not which are **blocked**. ### Interface (capabilities) [#interface-capabilities-1] | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | --------------------- | ---------------------------- | -------------------------------------------------------------- | --------------------------------------------------------------- | ----- | -------------------------------------------------------------------------- | | `setModuleParameters` | Token admin (via compliance) | `globalLimit`, `countryLimits[]`, `topicFilter`, `global` flag | Stores investor-count config | — | `topicFilter` uses the same RPN expression system as identity verification | | `canTransfer` | Compliance engine | Sender, recipient, amount | Checks if adding recipient would exceed global or country limit | — | Skips burns (`to == address(0)`); applies to mints and transfers | ### How it works [#how-it-works] | Investor state | Identity module result | InvestorCount result | Transfer outcome | | ------------------------------------- | -------------------------- | ------------------------------- | ---------------- | | No KYC/AML claims | Blocked by identity module | N/A (never reaches count check) | Transfer blocked | | Has qualifying claims, count \< limit | Allowed | Counted | Transfer allowed | | Has qualifying claims, count = limit | Allowed | Blocked (over limit) | Transfer blocked | ### topicFilter and limits [#topicfilter-and-limits] The `topicFilter` is a claim expression (same [RPN system](/docs/architecture/security/compliance/identity-verification) as SMARTIdentityVerification) that determines which investors count toward the limit. InvestorCount can enforce a **global limit** across all investors and **per-country limits** for jurisdiction-specific restrictions (e.g., max 50 Singapore residents). ### Key invariants [#key-invariants-1] * `canTransfer` skips burns (`to == address(0)`) but applies to both mints and transfers * The global tracker accumulates regardless of the `global` flag setting — the flag controls enforcement, not tracking * Per-country limits are checked independently of the global limit; both must pass * `topicFilter` determines who is counted, not who is blocked — blocking is handled by identity verification ### Operational signals [#operational-signals-1] No events emitted by this module. Monitor for `ComplianceCheckFailed` revert errors in failed transactions when investor count exceeds limits. ### Failure modes & edge cases [#failure-modes--edge-cases-1] * Investor count may temporarily exceed the limit after forced transfers bypass the check * Country code missing from identity registry — investor is not counted toward per-country limits but may still be counted globally ## See also [#see-also] * [Compliance Overview](/docs/architecture/security/compliance) — module architecture and regulatory templates * [Identity Verification](/docs/architecture/security/compliance/identity-verification) — `topicFilter` uses the same RPN expression system * [Transfer Approval](/docs/architecture/security/compliance/transfer-approval) — pre-approval controls complement investor count limits * [Supply Cap & Collateral](/docs/architecture/security/compliance/supply-cap-collateral) — CappedComplianceModule for hard circulating supply caps # TimeLock Source: https://docs.settlemint.com/docs/architecture/security/compliance/timelock TimeLock module enforces minimum holding periods using FIFO batch tracking. Covers configuration, identity-based exemptions, and integration with regulatory templates like MAS Singapore. **Purpose:** Reference for the TimeLock compliance module. * **Doc type:** Reference * **What you'll find here:** * TimeLock — minimum holding period enforcement with FIFO batch tracking * Interface table showing on-chain capabilities * Identity-based exemptions and regulatory use cases * Key invariants, operational signals, and failure modes * **Related:** [Compliance Overview](/docs/architecture/security/compliance), [Identity Verification](/docs/architecture/security/compliance/identity-verification), [Supply & Investor Limits](/docs/architecture/security/compliance/supply-investor-limits), [Transfer Approval](/docs/architecture/security/compliance/transfer-approval) *** ## TimeLock [#timelock] Enforces minimum holding periods — tokens received must be held for at least `holdPeriod` seconds before they can be transferred. ### Interface (capabilities) [#interface-capabilities] | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | --------------------- | -------------------------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------- | --------------------- | ----------------------------------------------------------------------- | | `setModuleParameters` | Token admin (via compliance) | `holdPeriod`, `allowExemptions`, `exemptionExpression` | Stores holding-period config | — | `exemptionExpression` uses the same RPN system as identity verification | | `canTransfer` | Compliance engine | Sender, recipient, amount | Walks FIFO queue; checks if enough unlocked balance exists for the transfer amount | — | Exempted investors bypass the check entirely | | `recordAcquisition` | Compliance engine (post-transfer hook) | Investor identity, amount, timestamp | Creates a new FIFO batch entry with expiry = timestamp + holdPeriod | `AcquisitionRecorded` | Called after every successful incoming transfer | | `unlockTokens` | Token holder (or agent) | Investor identity | Releases expired batches from the FIFO queue | `TokensUnlocked` | Optional — `canTransfer` also processes expired batches inline | ## How it works [#how-it-works] TimeLock uses **FIFO (First In, First Out) batch tracking**. Each incoming transfer creates a batch with: * Amount received * Timestamp received * Expiry (received + holdPeriod) On transfer, the module walks the FIFO queue and checks whether the oldest batches have cleared their holding period. If the requested transfer amount exceeds the unlocked balance, the transfer is blocked. **Example:** An investor receives 100 tokens on day 1 and 50 tokens on day 90, with a 180-day hold. On day 185, they can transfer up to 100 tokens (day 1 batch unlocked). The day 90 batch unlocks on day 270. ## Configuration [#configuration] | Parameter | Description | | --------------------- | -------------------------------------------------------------------- | | `holdPeriod` | Minimum holding period in seconds (e.g., `15552000` = 180 days) | | `allowExemptions` | If true, investors matching the exemption expression bypass the lock | | `exemptionExpression` | RPN expression identifying exempt investors | ## Identity-based exemptions [#identity-based-exemptions] The exemption expression uses the same [RPN system](/docs/architecture/security/compliance/identity-verification) as SMARTIdentityVerification. Common exemption patterns: | Exemption | Expression | Use case | | --------------------------- | -------------- | --------------------------------------------------- | | QII investors exempt | `[CONTRACT]` | Japan FSA: institutional investors skip lockup | | Accredited investors exempt | `[ACCREDITED]` | Reg D: accredited investors can trade freely | | No exemptions | Empty | MAS Singapore: all investors subject to hold period | ## Regulatory use cases [#regulatory-use-cases] | Regulation | Hold period | Exemption | | ------------------------------- | ----------------------------- | -------------------------- | | MAS Singapore — Capital Markets | 180 days (15,552,000 seconds) | None | | US Reg D 506(b) — typical | 1 year (31,536,000 seconds) | `[ACCREDITED]` | | EU MiCA — some asset types | Varies | Depends on instrument type | ## Key invariants [#key-invariants] * TimeLock applies to **received** tokens, not the sender's full balance * Forced transfers (custodian) bypass the TimeLock check * Burns are not blocked by TimeLock — the FIFO queue walks batches oldest-first and skips lock checks for burn operations * The FIFO queue is maintained per investor identity (not per wallet address) ## Operational signals [#operational-signals] * `AcquisitionRecorded` — emitted after each incoming transfer creates a new FIFO batch * `TokensUnlocked` — emitted when expired batches are released from the FIFO queue * Monitor for `ComplianceCheckFailed` revert errors in failed transactions when transfers exceed unlocked balance ## Failure modes & edge cases [#failure-modes--edge-cases] * Hold period changed after tokens acquired — existing batches keep their original expiry; only new acquisitions use the updated period * Exemption expression changed — investors who previously bypassed the lock are now subject to it (and vice versa), but only for future transfers * Large number of FIFO batches from frequent small transfers — `canTransfer` gas cost increases linearly with batch count ## See also [#see-also] * [Compliance Overview](/docs/architecture/security/compliance) — module architecture and regulatory templates * [Identity Verification](/docs/architecture/security/compliance/identity-verification) — RPN expression system for exemption expressions * [Supply & Investor Limits](/docs/architecture/security/compliance/supply-investor-limits) — InvestorCount often paired with TimeLock * [Transfer Approval](/docs/architecture/security/compliance/transfer-approval) — complementary pre-approval controls # Transfer Approval Source: https://docs.settlemint.com/docs/architecture/security/compliance/transfer-approval TransferApproval module for pre-authorization workflows with expiry, exact amount, single-use, and cumulative approval modes for regulated transfer control. TransferApproval adds identity-bound pre-authorization to regulated transfers. Each approval is tied to a sender identity, recipient identity, approved value, expiry window, and configured consumption mode. Approval authorities can grant or revoke approvals. The compliance engine consumes them according to the configured approval mode after a successful transfer. Related pages: [Compliance Overview](/docs/architecture/security/compliance), [Supply & Investor Limits](/docs/architecture/security/compliance/supply-investor-limits), [Supply Cap & Collateral](/docs/architecture/security/compliance/supply-cap-collateral), [Identity Verification](/docs/architecture/security/compliance/identity-verification), [Asset Contracts](/docs/architecture/components/asset-contracts). ## TransferApproval [#transferapproval] TransferApproval requires pre-authorization before transfers. Approvals expire after a configured duration. The configured approval mode determines whether an approval must be consumed by an exact transfer amount, by one transfer up to the approved amount, or by multiple transfers up to the approved total. ### Interface (capabilities) [#interface-capabilities] | Capability | Who can call | Inputs | On-chain effect | Emits | Notes | | --------------------- | ---------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | -------------------------- | ----------------------------------------------------------------------- | | `setModuleParameters` | Token admin (via compliance) | Approval authorities, expiry, and the installed module's parameter schema | Stores approval config; expiry is a config-time duration applied to all approvals | None | Use approval-mode settings or exemption/one-time-use settings, not both | | `approveTransfer` | Approval authority | Token, sender identity, recipient identity, value | Records approval for the sender, recipient, and approved amount with expiry timestamp | `TransferApproved` | Only addresses in `approvalAuthorities` can issue approvals | | `revokeApproval` | Approval authority | Token, sender identity, recipient identity, value | Removes the active approval record | `TransferApprovalRevoked` | Any configured approval authority can revoke a pending approval | | `canTransfer` | Compliance engine | Sender, recipient, amount | Checks the approval record and applies the configured approval mode | None | Validation only; consumption happens after a successful transfer | | `transferred` | Compliance engine | Token, sender, recipient, amount | Marks consumed approval value after the transfer completes | `TransferApprovalConsumed` | Called by the engine after transfer execution | ### How pre-approval works [#how-pre-approval-works] 1. A compliance manager configures the approval authorities, expiry, and the parameter model used by the installed module. 2. An approval authority issues an approval for a sender identity, recipient identity, and approved value. 3. The holder attempts a transfer between those identities. 4. The module checks that the approval is still active, not expired, and valid for the requested amount under the configured consumption rule. 5. After a successful transfer, the engine calls the transfer hook and the module records consumed approval value, emitting `TransferApprovalConsumed` where applicable. 6. If validation fails, the transfer is blocked before consumption. ### Approval modes [#approval-modes] Use the approval mode to choose how narrowly each approval can be consumed. | Mode | Value | Consumption behavior | Typical use | | ------------ | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | Exact amount | `0` | One transfer must match the approved value exactly. After that transfer succeeds, the approval is consumed and cannot be reused. | Strict regulated transfers where the approval record must match one settlement instruction. | | Up to once | `1` | One transfer can use any amount up to the approved value. Transferring less than the approved value still consumes the approval. | Workflows where the final settled amount may be lower than the pre-approved cap. | | Up to total | `2` | Multiple transfers can spend against the approval until the approved total is exhausted. Further transfers require a new approval or higher amount. | Programmatic or staged settlement where one approval covers several partial transfers. | Approval modes do not remove the expiry check during normal transfer validation. Once the configured approval window has passed, validation rejects the approval even if unused value remains. If a module is configured for accounting-only scope, validation can be skipped while post-transfer accounting hooks still run. ### Configuration [#configuration] Choose the parameter schema that matches the installed module. For the approval-mode model, configure: | Option | Description | | --------------------- | --------------------------------------------------------------------------------------------------------- | | `approvalAuthorities` | Addresses authorized to issue or revoke approvals | | `approvalMode` | Consumption rule for approvals: exact amount, up to once, or cumulative up to total | | `approvalExpiry` | Duration in seconds, from 1 to 31,536,000 seconds; set at configuration time and applies to all approvals | For the exemption model, configure: | Option | Description | | --------------------- | --------------------------------------------------------------------------------------------------------- | | `approvalAuthorities` | Addresses authorized to issue or revoke approvals | | `oneTimeUse` | If true, a matching approval is consumed after one successful transfer | | `approvalExpiry` | Duration in seconds, from 1 to 31,536,000 seconds; set at configuration time and applies to all approvals | | `allowExemptions` | If true, recipients matching the exemption expression bypass the approval requirement | | `exemptionExpression` | RPN expression identifying exempt investors | Extra keys that do not belong to the installed module's schema are ignored by validation, so keep configuration payloads schema-specific instead of mixing exemption settings with approval modes. After configuration, approval-mode behavior is fixed for that module instance. To change from one approval mode to another, deploy a new TransferApproval module with the desired mode. ### Operating patterns [#operating-patterns] | Pattern | Approval mode | Why it fits | | ------------------------- | ------------- | -------------------------------------------------------------------------- | | Exact settlement ticket | Exact amount | The approval record must match one transfer instruction. | | Capped single transfer | Up to once | The final transfer may settle below the approved cap, but only once. | | Staged partial settlement | Up to total | Several partial transfers may spend down one approved total before expiry. | ### Key invariants [#key-invariants] * Approval is scoped to the sender identity, recipient identity, approved value, expiry, and configured approval mode. It is not a blanket approval for any transfer. * An investor with multiple wallets uses the same identity, so the approval can apply across wallets tied to that identity. * `approvalExpiry` is a config-time duration. All approvals use the same expiry window, not per-approval overrides. * Exact amount mode is the strictest approval-mode pattern; up-to modes are useful only when the operating process allows partial consumption. * `approvalMode` is fixed after configuration. Redeploy the module to change approval-mode behavior. * Expired approvals are rejected during normal transfer validation; the approval authority must reissue. Accounting-only scoped modules can still run post-transfer accounting hooks after validation was skipped. ### Operational signals [#operational-signals] * `TransferApproved`: emitted when an approval authority grants a transfer approval * `TransferApprovalConsumed`: emitted when an approval is fully consumed. Exact amount and up-to-once approvals emit after the successful transfer that uses them. Up-to-total approvals emit only when the remaining approved amount reaches zero, not on every partial transfer. * `TransferApprovalRevoked`: emitted when an approval authority revokes an approval * Monitor for `ComplianceCheckFailed` revert errors in failed transactions when transfers lack valid approvals ### Failure modes & edge cases [#failure-modes--edge-cases] * Any configured approval authority can revoke a pending approval. Revoked approvals cannot be consumed. * Reusing an exact amount or up-to-once approval after a successful transfer is rejected because the approval has already been used * Spending more than the remaining value of an up-to-total approval is rejected during normal transfer validation. In accounting-only scope, validation is skipped and the post-transfer hook clamps remaining approval value to zero for accounting. ## See also [#see-also] * [Compliance Overview](/docs/architecture/security/compliance): module architecture and regulatory templates * [Identity Verification](/docs/architecture/security/compliance/identity-verification): identity controls that can combine with transfer pre-approval * [TimeLock](/docs/architecture/security/compliance/timelock): time-based restrictions that complement pre-approval patterns # Identity & Compliance Source: https://docs.settlemint.com/docs/architecture/security/identity-compliance Architecture of the DALP identity and compliance system built on ERC-3643 and OnchainID (ERC-734/735). Covers on-chain identity binding, claim-based permissions, compliance engine orchestration, and the two-layer policy model. DALP binds identities to wallets and evaluates compliance rules before regulated token operations execute. The model combines ERC-3643 token controls, OnchainID claims, trusted issuers, and custody policy checks. Related pages: * [Compliance Modules](/docs/architecture/security/compliance) * [Compliance Transfer Flow](/docs/architecture/flows/compliance-transfer) * [Public chain privacy boundaries](/docs/architecture/security/public-chain-privacy) * [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) * [Signing Flow](/docs/architecture/flows/signing-flow) * [Authentication](/docs/architecture/security/authentication) * [Authorization](/docs/architecture/security/authorization) *** > **Reading guide:** Use this architecture hub for enforcement flow. For **what each rule does**, see the [Compliance Modules catalog](/docs/architecture/security/compliance). ## The ERC-3643 standard [#the-erc-3643-standard] ERC-3643 is the security token standard behind the SMART Protocol: * **Identity binding**: Every holder needs an OnchainID registered in the token's identity registry * **Compliance validation**: Transfers execute only when all configured modules return true * **Claim-based permissions**: Identity contracts hold claims from trusted authorities * **Modular architecture**: Compliance rules live in separate module contracts DALP extends ERC-3643 via [SMART Protocol integration](/docs/architecture/components/asset-contracts/smart-protocol-integration) including logical claim expressions and cross-token investor counting. *** Identity management integrated into operator workflow ## Core on-chain components [#core-on-chain-components] | Component | Scope | Responsibility | Where documented | | ---------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ | | **Identity Registry** | Per token | Maps wallet addresses to OnchainID contracts. Each token maintains its own registry. | [Compliance Transfer Flow](/docs/architecture/flows/compliance-transfer) | | **Trusted Issuers Registry** | Global | Defines which claim issuers are authorized for which claim topics. Shared across all tokens. | [OnchainID protocol](#onchainid-protocol) (this page) | | **OnchainID** (ERC-734/735) | Per identity | Holds cryptographic keys and verifiable claims for a single identity. | [OnchainID protocol](#onchainid-protocol) (this page) | | **SMARTCompliance** | Per token | Orchestrates compliance modules for a token. Queries all configured modules during `canTransfer`. | [Compliance Modules catalog](/docs/architecture/security/compliance) | | **Compliance Modules** | Global instances, per-token config | Individual rule contracts (country, identity, supply, etc.). Single deployed instance, per-token parameters. | [Compliance Modules catalog](/docs/architecture/security/compliance) | *** ## Where compliance is enforced [#where-compliance-is-enforced] | Operation | Identity lookup | Compliance check | Result if failing | | --------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------- | | **Transfer** | Both sender and recipient resolved via Identity Registry | All configured modules evaluated sequentially | Transaction reverts with module reason | | **Mint** | Recipient resolved via Identity Registry | All configured modules evaluated | Mint reverts: no tokens created | | **Burn / Redemption** | Sender resolved via Identity Registry | Modules evaluated; recipient checks (country, identity) skip for `address(0)` | Burn reverts: tokens remain | | **Forced transfer** | Bypasses identity and compliance | Compliance skipped; state-tracking hooks (e.g., InvestorCount) may still apply | Executes without compliance checks (ERC-3643 `forcedTransfer`) | *** ## OnchainID protocol [#onchainid-protocol] Built on ERC-734 (key management) and ERC-735 (claim management). ### Key types (ERC-734) [#key-types-erc-734] | Purpose | Name | Controls | | ------- | ---------- | ----------------------------- | | 1 | Management | Add/remove other keys | | 2 | Action | Execute on behalf of identity | | 3 | Claim | Sign and approve claims | | 4 | Encryption | Decrypt data sent to identity | ### Inspecting identity keys [#inspecting-identity-keys] Operators and integrations can review the keys indexed for an identity address through the v2 system identity API. Use `GET /api/v2/system/identities/{identityAddress}/keys` when you need to inspect which ERC-734 keys are associated with an organisation identity, contract identity, or a user's OnchainID contract on the active system chain. `{identityAddress}` must be the indexed identity-contract or OnchainID address, not a wallet EOA. The keys endpoint does not resolve a wallet to its identity contract; wallet-based callers should first resolve the identity address with `GET /api/system/identity/by-wallet/{wallet}`, then call the keys endpoint with that resolved address. The response lists key hashes with decoded key purpose and key type labels. The collection supports pagination, sorting by indexer ingestion time, and filters for purpose and key type. ERC-734 `Action` keys are exposed with the `deposit` purpose value, so use `?purpose=deposit`; `?purpose=action` will not match returned key records. Use the [API Reference](/docs/developer-guides/api-integration/api-reference) to check the exact request and response schema before integrating. `createdAt` is the UTC timestamp when the indexer ingested the key record, not the on-chain event time. Consumers that need chain-event ordering or provenance should use `createdAtBlock` and `createdAtTxHash`, especially when comparing data across reindex or backfill windows. Identity keys are on-chain identity data surfaced through an authenticated, role-gated API. Treat the existence of identity addresses and key relationships as public-chain information, and keep identity documents and verification evidence off-chain. ### Claim structure (ERC-735) [#claim-structure-erc-735] | Field | Description | | --------- | --------------------------------------------- | | Topic | What the claim certifies (`keccak256("KYC")`) | | Issuer | Trusted authority address | | Signature | Cryptographic proof from issuer | | Data | Verification results (arbitrary bytes) | | URI | Pointer to off-chain proof | *** ## Identity verification lifecycle [#identity-verification-lifecycle] **Question answered:** How does a user go from unverified to transfer-ready? **Key takeaways:** * Claims are issued off-chain (KYC provider) and stored on-chain (OnchainID contract) * The Trusted Issuers Registry gates which authorities can issue claims for which topics * Every transfer re-validates claims at execution time: expired or revoked claims block transfers 1. **Off-chain verification** -- KYC provider verifies identity, AML, jurisdiction 2. **On-chain claim issuance** -- Trusted issuer calls `addClaim` on user's OnchainID 3. **Identity registration** -- Wallet-to-identity mapping in identity registry 4. **Runtime validation** -- Transfers query compliance, resolve identities, evaluate rules *** Verification topic registry for compliance attestations ## Compliance engine architecture [#compliance-engine-architecture] Orchestration is separated from rule enforcement: **Question answered:** How do tokens, the compliance engine, and modules relate? **Key takeaways:** * One SMARTCompliance contract per token orchestrates all compliance checks * Modules are global singletons: each token configures its own parameters * A single module veto blocks the entire transfer (fail-fast) During transfers, the compliance contract iterates configured modules. If any returns false, the transaction reverts. See [Compliance Transfer Flow](/docs/architecture/flows/compliance-transfer) for the full sequence. *** ## Two-layer policy model [#two-layer-policy-model] Two independent policy layers: **Question answered:** What are the two independent policy layers a transaction passes through? **Key takeaways:** * Layer 1 (on-chain) enforces regulatory compliance and is immutable/auditable * Layer 2 (custodian) enforces operational controls and approval workflows * For standard transfers, both layers must pass in sequence: Layer 1 failure prevents Layer 2 from being reached | Aspect | Layer 1: On-chain | Layer 2: Custodian | | ------------- | ------------------------------------------ | ---------------------------------------------- | | Enforced by | DALP compliance modules (EVM) | DFNS / Fireblocks policy engine | | Controls | Identity, country, supply, holding periods | Thresholds, multi-party approval, spend limits | | Visibility | On-chain, auditable in tx trace | Provider dashboard, audit logs | | Configured by | Token issuer / compliance manager | Operations team | Layer 1 is authoritative for regulatory enforcement. Layer 2 protects signing infrastructure. See [Signing Flow](/docs/architecture/flows/signing-flow). *** ## Trust boundaries & assumptions [#trust-boundaries--assumptions] * **Trusted Issuers Registry** is the root of trust: only issuers listed here can issue claims that the system accepts. Compromise of this registry compromises all identity verification. * **Claims have expiry**: expired claims are rejected at transfer time. The system fails closed: missing or invalid claims block operations, they do not silently pass. * **Off-chain verification, on-chain proof**: KYC/AML checks happen off-chain; the on-chain claim is the proof artifact. The system trusts the claim signature, not the quality of the underlying verification: a compromised or negligent trusted issuer can issue fraudulent claims that the protocol will accept. * **Identity Registry binding is per-token**: a wallet registered for Token A is not automatically registered for Token B. Each token maintains its own wallet→identity mapping. * **Layer 2 is independent**: custodian policy (DFNS/Fireblocks) operates outside the protocol. On-chain compliance cannot enforce or verify custodian-layer decisions. See [Signing Flow](/docs/architecture/flows/signing-flow). * **Forced transfers bypass compliance**: `forcedTransfer` (ERC-3643) skips all compliance checks. Treat it as a custodian-controlled exceptional servicing operation. See [Authorization](/docs/architecture/security/authorization) for role controls. *** ## See also [#see-also] * [Compliance Modules](/docs/architecture/security/compliance) -- full module catalog * [Compliance Transfer Flow](/docs/architecture/flows/compliance-transfer) -- transfer validation sequence * [Authentication](/docs/architecture/security/authentication) -- platform identity * [Authorization](/docs/architecture/security/authorization) -- role-based access control * [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) -- core protocol # Overview Source: https://docs.settlemint.com/docs/architecture/security Overview of DALP's security architecture covering authentication, authorization, identity and compliance, on-chain compliance modules, and wallet verification as layered defenses for digital asset operations. ## Purpose [#purpose] This section documents DALP's security architecture as a set of layered controls that protect digital asset operations from identity verification through on-chain enforcement. * **Doc type:** Reference ## What you'll find here [#what-youll-find-here] * Authentication mechanisms across console and API access * Authorization model with role-based and resource-level controls * On-chain identity and compliance enforcement via ERC-3643 * Modular compliance rules that govern token transfers * Wallet verification gates for blockchain write operations ## Defense-in-depth model [#defense-in-depth-model] DALP enforces security at every platform layer. No single control failure grants unauthorized access to digital assets. | Layer | Control | Enforced by | | ----------- | --------------------------------------------- | ------------------------------------ | | Identity | Authentication (session, API key, SSO) | Asset Console, Unified API | | Access | Role-based and resource-level authorization | Unified API middleware | | Transaction | Wallet verification (PIN, TOTP, backup codes) | Unified API before blockchain writes | | On-chain | Identity claims and compliance modules | SMART Protocol (ERC-3643) | | Custody | Provider policy evaluation and MPC signing | Key Guardian, custody providers | Each layer operates independently. A compromised session token is blocked by wallet verification. A bypassed API authorization check is blocked by on-chain compliance. Custody provider policies provide the final gate before any transaction reaches the blockchain. ## Section pages [#section-pages] | Page | Description | | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | [Authentication](/docs/architecture/security/authentication) | Identity provider options, session management, passkeys, and enterprise SSO | | [Authorization](/docs/architecture/security/authorization) | Role-based access control, resource-level permissions, and permission inheritance | | [Identity and compliance](/docs/architecture/security/identity-compliance) | On-chain identity (ERC-734/735 OnchainID), KYC/AML claims, and verification lifecycle | | [Compliance modules](/docs/architecture/security/compliance) | Modular transfer rules, country restrictions, investor limits, and time-based locks | | [Wallet verification](/docs/architecture/security/wallet-verification) | PIN, TOTP, and backup code verification for blockchain write operations | Secure authentication with passwordless passkey support ## Trust boundaries [#trust-boundaries] Three trust boundaries define the security perimeter: 1. **Platform boundary** -- between external users/systems and DALP's API surface. Controlled by authentication and rate limiting. 2. **Execution boundary** -- between the API layer and the Execution Engine. Controlled by authorization and input validation. 3. **Chain boundary** -- between the Execution Engine and the blockchain. Controlled by wallet verification, on-chain compliance, and custody provider policies. ## See also [#see-also] * [Signing flow](/docs/architecture/flows/signing-flow) for the complete transaction security sequence * [Key Guardian](/docs/architecture/components/infrastructure/key-guardian) for cryptographic key protection * [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) for on-chain security enforcement * [Authorization](/docs/architecture/security/authorization) for role-based access control * [Custody Providers](/docs/architecture/integrations/custody-providers) for MPC custody integration # Public chain privacy boundaries Source: https://docs.settlemint.com/docs/architecture/security/public-chain-privacy How DALP separates public EVM chain data from off-chain identity and compliance records. Covers wallet visibility, identity claims, personal-data boundaries, and operator controls. ## Purpose [#purpose] Use this page before designing an asset, identity, or compliance flow on a public EVM chain. * **Doc type:** Explanation * **What you'll find here:** * Which DALP records become public blockchain data * Which identity and KYC records stay off-chain * How wallet addresses, identities, claims, and compliance checks relate * What not to put on-chain * **Related:** * [Identity & Compliance](/docs/architecture/security/identity-compliance) * [Compliance Modules](/docs/architecture/security/compliance) * [Wallet Verification](/docs/architecture/security/wallet-verification) * [API Reference](/docs/developer-guides/api-integration/api-reference) * [CLI Command Reference](/docs/developer-guides/cli/command-reference) * [Privacy Policy](/docs/legal/privacy-policy) * [Terms of Service](/docs/legal/terms-of-service) *** ## Public-chain baseline [#public-chain-baseline] DALP operates on EVM networks. On public EVM chains, blockchain transactions and contract state can be read by anyone with access to that network. That includes token contract addresses, wallet addresses, transaction hashes, transfer events, and data written into smart contracts. Use the [API Reference](/docs/developer-guides/api-integration/api-reference) and [CLI Command Reference](/docs/developer-guides/cli/command-reference) to check the exact identity, claim, trusted-issuer, and token operations your flow uses. Public-chain data has a different privacy profile from off-chain platform data: | Data surface | Visibility boundary | Operator guidance | | ---------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | Wallet addresses | Visible on the chain and linkable through transactions | Treat addresses as persistent public identifiers once used on a public network. | | Token and compliance contract state | Visible according to the network and contract interfaces | Configure only the data required for issuance, transfers, and enforcement. | | Transaction metadata | Visible through transaction hashes, logs, and event history | Assume mint, transfer, burn, and administrative actions can be correlated on-chain. | | KYC/KYB documents and verification files | Processed off-chain through platform and verification workflows | Keep personal documents and detailed evidence off-chain. | | Platform account data | Stored and processed by the platform according to the legal privacy terms | Manage retention and access through platform controls and your data-processing obligations. | ## Identity and compliance boundary [#identity-and-compliance-boundary] DALP's identity and compliance model separates verification evidence from the on-chain enforcement records that tokens need for ERC-3643 style controls. The public [API Reference](/docs/developer-guides/api-integration/api-reference), [CLI Command Reference](/docs/developer-guides/cli/command-reference), and [Identity & Compliance](/docs/architecture/security/identity-compliance) pages show the user-facing identity, claim, trusted-issuer, and compliance operations behind this boundary. * Identity documents and detailed KYC/KYB evidence are processed off-chain. * Wallets are connected to on-chain identities so token contracts can evaluate eligibility. * Identity records can be read by wallet address or by identity address, and can be checked with or without token context. * Trusted issuers and claim topics define which authorities and claim types can support transfer eligibility. * Claims can be issued to an identity, revoked from an identity, and reviewed through claim-history views. * Compliance modules evaluate configured rules during token operations such as minting, transfers, and burns. This means DALP can enforce eligibility on-chain without requiring operators to publish identity documents on-chain. It does not make public-chain activity private. Wallets, token operations, identity addresses, trusted-issuer relationships, claim topics, issued or revoked claim references, and claim event history remain visible according to the network and registry surfaces used in the flow. ## Current identity and claim surfaces [#current-identity-and-claim-surfaces] Use these surfaces when reviewing what a public-chain identity flow exposes. Use the public API reference to verify request and response fields, and use the CLI command reference to find the matching commands and flags before launch: | Surface | What operators configure or review | Privacy boundary | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | | Identity registry | Creates, registers, reads, lists, searches, updates, and removes wallet-linked identity records. | Wallet and identity addresses are identifiers. Treat them as public once they are registered or used in public-chain flows. | | Identity lookups | Reads an identity by wallet address, identity address, or current user, with optional token-specific claim validation. | Token-specific validation can reveal which token context is being checked against the identity. | | Claim issuance | Issues a signed claim to a target identity for topics such as KYC, AML, accreditation, investor type, or asset classification. | Claim topics and on-chain claim references support enforcement; detailed evidence should remain off-chain. | | Claim revocation/history | Revokes an existing claim and exposes chronological claim history for audit and operations. | Revocation and history help operate compliance, but they do not hide prior public-chain activity. | | Claim topics | Lists, creates, updates, reads, and deletes the topic schemes that describe supported identity claims. | Topic names and signatures should describe eligibility states, not personal documents or raw verification evidence. | | Trusted issuers | Lists, reads, creates, updates, upserts, and deletes issuers and the topics each issuer is trusted to attest. | Issuer identities and topic assignments can link an issuer to eligibility decisions. | ## What not to put on-chain [#what-not-to-put-on-chain] Do not place personal data, identity documents, raw KYC evidence, confidential commercial terms, or sensitive investor files into blockchain fields, contract metadata, claim payloads, claim references, token metadata, transaction notes, or uploaded documents that are intended to be publicly referenced. Use neutral identifiers and off-chain records where possible. For example, a compliance workflow can record that a wallet is eligible for a claim topic without publishing the underlying passport, proof of address, sanctions report, or beneficial-ownership file on-chain. ## Current controls and limits [#current-controls-and-limits] DALP provides controls that help separate public enforcement from private evidence: | Control | What it helps with | Limit | | -------------------------------- | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- | | Off-chain verification workflows | Keeps supporting KYC/KYB evidence out of token contracts | Operators still control what they submit to on-chain fields and public metadata. | | Trusted issuers and claim topics | Lets approved authorities support eligibility without publishing full evidence | The existence of an identity, wallet, claim topic, or claim reference can still be visible on-chain. | | Compliance modules | Enforces transfer, mint, and burn rules through contract checks | Enforcement does not hide transaction history or wallet activity on public chains. | | Platform access controls | Limits who can view and manage off-chain operational records in DALP | Access control cannot remove data already written to a public blockchain. | The legal privacy pages state the key operational boundary: data written to a public blockchain can be immutable and publicly accessible, and SettleMint cannot delete or modify on-chain data. Operators are responsible for ensuring personal data is not recorded on-chain in violation of applicable law. ## Design checklist [#design-checklist] Before launching on a public EVM network: 1. Identify every field that will be written on-chain or referenced from on-chain records. 2. Remove personal data and confidential files from token metadata, claim data, claim references, and transaction inputs. 3. Keep detailed verification evidence in approved off-chain systems. 4. Use claim topics and eligibility flags for enforcement instead of raw identity evidence. 5. Confirm that issuer, custodian, registry, and operator addresses can be publicly associated with their actions. 6. Review the [Privacy Policy](/docs/legal/privacy-policy) and [Terms of Service](/docs/legal/terms-of-service) for the legal treatment of blockchain data. # Wallet Verification Source: https://docs.settlemint.com/docs/architecture/security/wallet-verification Wallet verification gates blockchain write operations as a second security layer beyond session authentication, using PIN, TOTP, or backup-code checks. Wallet verification is a dedicated second factor for blockchain write operations. Even with a valid authenticated session, a signing request must include a supported wallet verification method before DALP submits the transaction. This page is a reference for the verification methods accepted by transaction-signing APIs and the request shape those APIs expect. ## Related [#related] * [Authentication](/docs/architecture/security/authentication) * [Authorization](/docs/architecture/security/authorization) * [Signing Flow](/docs/architecture/flows/signing-flow) * [Key Guardian](/docs/architecture/components/infrastructure/key-guardian) ## Why wallet verification exists [#why-wallet-verification-exists] Session authentication proves identity. Wallet verification proves intent and control. Separating these concerns means a compromised session token alone cannot trigger asset transfers, minting, burning, or any other state-changing blockchain operation. The attacker must also possess the user's wallet verification credential, a value that never travels in the session token and rotates independently. This separation aligns with defense-in-depth: the platform boundary (authentication) and the chain boundary (wallet verification) use independent control mechanisms. See the [security overview](/docs/architecture/security) for how these layers combine. ## Verification methods [#verification-methods] Transaction-signing APIs accept these wallet verification methods. Organizations can enable one or more based on their security posture. ### PINCODE [#pincode] A 6-digit PIN set during initial wallet setup. PIN verification provides quick confirmation for routine transactions where the user is already authenticated and working within the platform. * **Setup:** User defines PIN during wallet creation * **Usage:** Enter 6-digit code alongside the transaction request * **Trade-off:** Fastest verification, lower resistance to shoulder-surfing compared to TOTP ### Two-factor authentication (TOTP) [#two-factor-authentication-totp] Time-based one-time passwords generated by an authenticator app. Codes rotate every 30 seconds following RFC 6238. * **Setup:** User scans a QR code into their authenticator app during wallet setup * **Usage:** Enter the current 6-digit code from the authenticator app * **Trade-off:** Stronger than PIN (codes expire), requires the user's authenticator device ### Backup codes [#backup-codes] One-time recovery codes generated during wallet setup. Each code works exactly once and is consumed on use. * **Setup:** Platform generates a set of codes during wallet configuration; user stores them securely offline * **Usage:** Enter any unused code when other methods are unavailable * **Trade-off:** Last-resort recovery mechanism; limited supply, no expiration until used ## Passkeys and wallet verification [#passkeys-and-wallet-verification] Passkeys can be used for account authentication when enabled for the deployment. They are documented separately on the [Authentication](/docs/architecture/security/authentication) page. For blockchain write requests, the wallet verification middleware accepts PINCODE, OTP, or SECRET\_CODES. Do not send PASSKEY as the `walletVerification.verificationType` for transaction-signing API calls; the request will be rejected. ## How verification works in requests [#how-verification-works-in-requests] API procedures that trigger blockchain write operations accept a `walletVerification` object in the request body: | Field | Value | Description | | ------------------------------------------- | ----------------------------------------- | --------------------------------------------------- | | `walletVerification.verificationType` | `PINCODE`, `OTP`, or `SECRET_CODES` | Which wallet verification method the user presents | | `walletVerification.secretVerificationCode` | 6-digit string, TOTP code, or backup code | The credential for the selected verification method | The Unified API validates the credential before forwarding the operation to the Execution Engine. If verification fails, the request is rejected immediately. No gas is consumed, no custody provider interaction occurs, and no on-chain state changes. ### API key sessions bypass verification [#api-key-sessions-bypass-verification] When a request is authenticated via API key, wallet verification is not required. The `walletVerification` field can be omitted from the request body. API keys are scoped, rate-limited credentials intended for machine-to-machine use. The API key is the authorization factor, so a second interactive challenge is unnecessary and impractical for automated workflows. ## Security considerations [#security-considerations] * **Independent rotation:** Wallet verification credentials rotate on their own schedule, independent of session tokens or passwords * **Rate limiting:** Failed verification attempts trigger progressive lockout to prevent brute-force attacks * **Audit trail:** Every verification attempt (success and failure) logs with user identity and timestamp * **No fallback bypass:** There is no administrative override that skips wallet verification; recovery requires backup codes or credential re-enrolment ## See also [#see-also] * [Security overview](/docs/architecture/security) for the full defense-in-depth model * [Signing Flow](/docs/architecture/flows/signing-flow) for how wallet verification fits into the transaction lifecycle * [Authentication](/docs/architecture/security/authentication) for API-level rate limiting and RBAC # Backup & Recovery Source: https://docs.settlemint.com/docs/architecture/self-hosting/high-availability/backup-recovery Backup strategy, tiered schedules, PostgreSQL PITR, DR testing requirements, and cloud provider-specific HA configurations for self-hosted DALP deployments. **Purpose:** Document the backup strategy, tiered schedules, PITR configuration, and DR testing requirements. * **Doc type:** Reference * **Related:** [HA Overview](/docs/architecture/self-hosting/high-availability), [Cloud-native](/docs/architecture/self-hosting/high-availability/cloud-native), [Observability](/docs/architecture/operability/observability) *** ## What gets backed up [#what-gets-backed-up] | Component | Backup method | Frequency | Retention | | -------------------- | --------------------------------------------------- | ------------------- | ---------- | | PostgreSQL data | Managed PITR or CNPG WAL shipping to object storage | Continuous | 30 days | | Kubernetes resources | Velero backups (file-level, snapshots optional) | Hourly/Daily/Weekly | 48h/7d/30d | | Object storage | Bucket versioning | Automatic | 90 days | | Observability data | Velero backups when self-hosted | Daily | 3 days | | Configuration | Helm values in Git | Every change | Indefinite | ## Tiered backup schedule [#tiered-backup-schedule] Default Velero schedules: | Schedule | Timing | Retention | Contents | | -------- | ----------- | --------- | ------------------------ | | Hourly | Every hour | 48 hours | ConfigMaps, Secrets | | Daily | 3 AM daily | 7 days | Full namespace snapshot | | Weekly | 4 AM Sunday | 30 days | Full namespace + volumes | ## PostgreSQL PITR [#postgresql-pitr] For CloudNativePG deployments: * WAL shipping to object storage (continuous) * Base backups daily * Point-in-time recovery to any moment within retention window Velero can optionally use CSI snapshots when a compatible CSI driver and VolumeSnapshot CRDs are installed. If not available, Velero performs file-level backups. ## Monitoring [#monitoring] ### Key metrics to monitor [#key-metrics-to-monitor] | Metric | Purpose | | -------------------------- | ------------------- | | Pod availability | Application health | | Pod restart counts | Stability indicator | | PostgreSQL replication lag | Data consistency | | Backup success/failure | Recovery capability | | Certificate expiration | TLS continuity | ### Recommended alerts [#recommended-alerts] | Alert | Threshold | Severity | | -------------------- | --------------- | -------- | | Backup failed | Any failure | Critical | | Replication lag | Over 60 seconds | Warning | | Pod restart | Over 5 per hour | Warning | | Certificate expiring | Under 14 days | Warning | | Disk usage | Over 80% | Warning | ## DR testing requirements [#dr-testing-requirements] ### Quarterly DR drills [#quarterly-dr-drills] 1. Restore from backup to test environment 2. Verify data integrity 3. Test application functionality 4. Document recovery time 5. Update runbooks if needed ### Annual full DR test [#annual-full-dr-test] 1. Simulate cluster failure 2. Execute full recovery procedure 3. Measure actual RTO/RPO vs targets 4. Report to stakeholders 5. Update SLA if needed ## See also [#see-also] * [Prerequisites](/docs/architecture/self-hosting/prerequisites) for infrastructure requirements * [Installation process](/docs/architecture/self-hosting/installation-process) for deployment steps * [DALP Execution Engine](/docs/architecture/components/infrastructure/execution-engine) for component architecture # Cloud-Native (Recommended) Source: https://docs.settlemint.com/docs/architecture/self-hosting/high-availability/cloud-native Single-region multi-AZ deployment using managed Kubernetes services, managed PostgreSQL, and Velero backups. The recommended HA approach for most self-hosted DALP deployments. **Purpose:** Describe the recommended cloud-native HA deployment pattern. * **Doc type:** Reference * **Related:** [HA Overview](/docs/architecture/self-hosting/high-availability), [Hot-warm](/docs/architecture/self-hosting/high-availability/hot-warm), [Backup & Recovery](/docs/architecture/self-hosting/high-availability/backup-recovery) *** Single-region, multi-AZ deployment using managed services and Velero backups. This is the recommended approach for most deployments. ## Architecture [#architecture] ## Recovery metrics [#recovery-metrics] | Metric | Target | Notes | | ------ | ---------------- | ------------------------------------ | | RTO | 2–15 minutes | Automatic failover for most failures | | RPO | Seconds–1 minute | Synchronous replication | | RTT | 15–60 minutes | Including verification | ## Setup and maintenance [#setup-and-maintenance] | Task | Time estimate | Client role | | ------------------------------- | ------------- | ------------------------ | | Kubernetes cluster provisioning | 2–4 hours | Client platform engineer | | Managed PostgreSQL setup | 1–2 hours | Client platform engineer | | Velero installation and config | 2–4 hours | Client platform engineer | | Backup verification | 2–4 hours | Client platform engineer | | Monitoring and alerting | 2–4 hours | Client platform engineer | | Documentation and runbooks | 4–8 hours | Client platform engineer | | **Total initial setup** | **2–3 days** | 1 client engineer | | Activity | Frequency | Time per cycle | | ----------------------- | --------- | -------------- | | Backup verification | Weekly | 30 minutes | | Helm chart updates | Monthly | 1–2 hours | | DR drill / restore test | Quarterly | 4–8 hours | | Security patching | Monthly | 2–4 hours | | Capacity review | Quarterly | 2–4 hours | | **Monthly effort** | | **8–16 hours** | ## Team requirements [#team-requirements] * **Minimum:** Part of platform team responsibilities (\~0.25 FTE) * **Recommended:** Dedicated on-call rotation for production incidents **Required skills:** Kubernetes/OpenShift administration (intermediate), Helm chart management (basic), cloud provider managed services, basic PostgreSQL operations, Prometheus/Grafana monitoring. ## Cloud provider configurations [#cloud-provider-configurations] **AWS (EKS):** EKS control plane multi-AZ by default, Auto Scaling Groups across AZs, RDS Multi-AZ, ElastiCache Multi-AZ, S3 (99.999999999% durability). **Azure (AKS):** Zone-redundant control plane, Availability Zones for workers, Azure Database zone-redundant HA, Azure Cache zone redundancy, Blob Storage ZRS or GRS. **GCP (GKE):** Regional multi-zone control plane, multi-zone node pool, Cloud SQL Regional HA, Memorystore Standard tier, Cloud Storage multi-regional. **OpenShift (OCP/OKD):** Multi-master with etcd quorum, workers across failure domains, OpenShift Data Foundation (Ceph) for storage, router sharding for HA. # Hot-Cold (Backup-Based Recovery) Source: https://docs.settlemint.com/docs/architecture/self-hosting/high-availability/hot-cold Backup-based disaster recovery with cold standby cluster. Lowest cost option with significant RPO (4–24 hours) and RTO (8–72 hours). Use only when cost constraints outweigh availability requirements. **Purpose:** Describe the hot-cold backup-based recovery pattern and its tradeoffs. * **Doc type:** Reference * **Related:** [HA Overview](/docs/architecture/self-hosting/high-availability), [Hot-warm](/docs/architecture/self-hosting/high-availability/hot-warm), [Backup & Recovery](/docs/architecture/self-hosting/high-availability/backup-recovery) *** Hot-cold deployments accept significant data loss (RPO 4–24 hours) and long recovery times (RTO 8–72 hours). Only use when cost constraints outweigh availability requirements. Backup-based disaster recovery with a cold standby cluster that is provisioned on-demand. ## When to use hot-cold [#when-to-use-hot-cold] * Cost is the primary constraint — only one cluster runs continuously * The system is not business-critical (development, staging, non-production) * Acceptable RPO is 4+ hours and RTO is 8+ hours * Data can be rebuilt by replaying blockchain events (re-indexing is acceptable) Do not use hot-cold for production financial systems where data loss or multi-hour outages are unacceptable. ## Architecture [#architecture] ## Recovery metrics [#recovery-metrics] | Metric | Target | Notes | | ------ | ----------- | ----------------------------- | | RTO | 8–72 hours | Highly variable by chain size | | RPO | 4–24 hours | Depends on backup frequency | | RTT | 12–96 hours | Including resync and reindex | ## Recovery time breakdown [#recovery-time-breakdown] | Phase | Duration | Notes | | --------------------- | -------------- | ---------------------------- | | Cluster provisioning | 15–60 minutes | If not pre-provisioned | | Operator installation | 5–15 minutes | Operator readiness checks | | PostgreSQL restore | 30–120 minutes | Depends on database size | | Velero restore | 15–60 minutes | Depends on resources | | Blockchain resync | 4–48+ hours | Depends on chain size | | Indexer rebuild | 1–24 hours | Depends on events to process | | **Total RTT** | **8–72 hours** | Highly variable | ## Cost advantage [#cost-advantage] Hot-cold is significantly cheaper than other patterns: * Only 1 active cluster running * Cold cluster provisioned on-demand (pay only during recovery) * Minimal cross-region networking costs Trade-off: Longer recovery time and potential data loss. ## Setup and maintenance [#setup-and-maintenance] | Task | Time estimate | Client role | | ------------------------------- | --------------- | ------------------------ | | Active cluster provisioning | 4–8 hours | Client platform engineer | | Cold cluster IaC preparation | 4–8 hours | Client platform engineer | | CloudNativePG setup | 4–8 hours | Client platform engineer | | PostgreSQL backup configuration | 4–8 hours | Client platform engineer | | Velero installation | 2–4 hours | Client platform engineer | | Recovery script development | 1 day | Client platform engineer | | Recovery procedure testing | 1–2 days | Client platform engineer | | **Total initial setup** | **1–1.5 weeks** | 1 client engineer | | Activity | Frequency | Time per cycle | | --------------------------- | --------- | --------------- | | Backup verification | Daily | 15 minutes | | Backup integrity testing | Weekly | 1–2 hours | | Helm chart updates | Monthly | 1–2 hours | | Recovery drill (full) | Quarterly | 1–2 days | | Cold cluster IaC validation | Quarterly | 2–4 hours | | Security patching | Monthly | 2–4 hours | | **Monthly effort** | | **10–20 hours** | # Hot-Hot (Active-Active) Source: https://docs.settlemint.com/docs/architecture/self-hosting/high-availability/hot-hot Multi-cluster active-active deployment for consortium and public blockchain networks. Provides the lowest RTO (1–10 minutes) at the highest operational cost. **Purpose:** Describe both hot-hot deployment variants: consortium networks and public networks. * **Doc type:** Reference * **Related:** [HA Overview](/docs/architecture/self-hosting/high-availability), [Hot-warm](/docs/architecture/self-hosting/high-availability/hot-warm), [Backup & Recovery](/docs/architecture/self-hosting/high-availability/backup-recovery) *** Two variants of active-active multi-cluster deployment, depending on whether you run consortium or public blockchain networks. ## Consortium networks [#consortium-networks] Multi-cluster active-active deployment for consortium blockchain networks where you manage validators. ### Architecture [#architecture] ### Recovery metrics (consortium) [#recovery-metrics-consortium] | Metric | Target | Notes | | ------ | --------------- | --------------------------- | | RTO | 1–10 minutes | Automatic failover via GSLB | | RPO | Seconds–minutes | Async replication lag | | RTT | 10–60 minutes | Including traffic rerouting | ### Setup and maintenance (consortium) [#setup-and-maintenance-consortium] | Task | Time estimate | Client role | | ------------------------------------- | ------------- | ------------------------------- | | Four cluster provisioning | 1–2 days | Client platform engineer | | Network connectivity (peering or VPN) | 1–2 days | Client network engineer | | CloudNativePG setup (four clusters) | 1–2 days | Client platform engineer | | PostgreSQL distributed topology | 2–3 days | Client DBA or platform engineer | | Failover automation and testing | 2–3 days | Client platform engineer | | End-to-end DR drill | 1–2 days | Client platform team | | **Total initial setup** | **3–5 weeks** | 2–3 client engineers | | Activity | Frequency | Time per cycle | | ------------------------------------ | --------- | --------------- | | Cross-cluster replication monitoring | Daily | 30 minutes | | Backup verification (all clusters) | Weekly | 2 hours | | Helm chart updates (4 clusters) | Monthly | 4–8 hours | | DR drill / failover test | Quarterly | 1–2 days | | Security patching (4 clusters) | Monthly | 1–2 days | | **Monthly effort** | | **40–60 hours** | **Team requirements:** 1.5–2 FTE dedicated platform engineers + 0.5 FTE DBA support, 24/7 on-call rotation. *** ## Public networks [#public-networks] Multi-cluster active-active deployment for public blockchain networks where on-chain data can be re-derived. ### Key differences from consortium [#key-differences-from-consortium] * No validators to manage — the chain is external * Data is re-derivable — indexed data can be rebuilt by re-indexing * Lower operational overhead than consortium * Focus on minimizing user-facing downtime ### Architecture [#architecture-1] ### Recovery metrics (public networks) [#recovery-metrics-public-networks] | Scenario | RTO | RPO | Notes | | ----------------------- | ------------ | ----------- | ------------------------------------ | | Single pod failure | \<1 minute | 0 | Kubernetes reschedules automatically | | Database failover | 1–5 minutes | Seconds | CloudNativePG automatic failover | | Cluster failover (GSLB) | 1–10 minutes | 1–5 minutes | Traffic shifts to healthy cluster | | Full re-index required | 5–60 minutes | N/A | Depends on chain size | ### Setup and maintenance (public networks) [#setup-and-maintenance-public-networks] | Task | Time estimate | Client role | | ---------------------------------- | --------------- | ------------------------ | | Two cluster provisioning | 1 day | Client platform engineer | | CloudNativePG setup (two clusters) | 1 day | Client platform engineer | | DIDX setup | 1–2 days | Client platform engineer | | Global traffic management | 4–8 hours | Client platform engineer | | **Total initial setup** | **1.5–2 weeks** | 1–2 client engineers | | Activity | Frequency | Time per cycle | | ------------------------------ | --------- | --------------- | | Replication lag monitoring | Daily | 15 minutes | | Indexer sync verification | Daily | 15 minutes | | DR drill / failover test | Quarterly | 4–8 hours | | Security patching (2 clusters) | Monthly | 4–8 hours | | **Monthly effort** | | **20–30 hours** | **Team requirements:** 0.5–1 FTE dedicated platform engineer, lower effort than consortium due to simpler topology. # Hot-Warm (Active-Standby) Source: https://docs.settlemint.com/docs/architecture/self-hosting/high-availability/hot-warm Active-standby deployment with warm validators and continuous database replication. Provides geographic redundancy with RTO of 30–180 minutes. **Purpose:** Describe the hot-warm active-standby deployment pattern. * **Doc type:** Reference * **Related:** [HA Overview](/docs/architecture/self-hosting/high-availability), [Cloud-native](/docs/architecture/self-hosting/high-availability/cloud-native), [Hot-cold](/docs/architecture/self-hosting/high-availability/hot-cold) *** Active-standby deployment with warm validators and continuous database replication across two clusters. ## Architecture [#architecture] ## When to use hot-warm [#when-to-use-hot-warm] * Acceptable RTO of 30–180 minutes * Regulatory requirements for geographic redundancy * Consortium networks where validator keys can be pre-staged * Cost optimization compared to full hot-hot ## Recovery metrics [#recovery-metrics] | Metric | Target | Notes | | ------ | -------------- | -------------------------------- | | RTO | 30–180 minutes | Depends on automation level | | RPO | 5–60 minutes | Based on replication lag | | RTT | 1–6 hours | Including validation and testing | **Important:** Failover is manual and requires trained operator availability. RTO depends on staff availability and time zones. Regular drills are required to keep procedures current. ## Setup and maintenance [#setup-and-maintenance] | Task | Time estimate | Client role | | ------------------------------------- | ------------- | ------------------------ | | Two cluster provisioning | 1 day | Client platform engineer | | Network connectivity setup | 4–8 hours | Client platform engineer | | CloudNativePG setup (two clusters) | 1 day | Client platform engineer | | PostgreSQL primary and replica config | 1–2 days | Client platform engineer | | Replication verification | 4–8 hours | Client platform engineer | | Velero installation (two clusters) | 4–8 hours | Client platform engineer | | Warm validator configuration | 1 day | Client platform engineer | | Key management setup | 4–8 hours | Client security engineer | | Failover scripts and automation | 1–2 days | Client platform engineer | | Failover drill and validation | 1 day | Client platform team | | **Total initial setup** | **2–3 weeks** | 1–2 client engineers | | Activity | Frequency | Time per cycle | | ------------------------------- | --------- | --------------- | | Replication lag monitoring | Daily | 15 minutes | | Standby health verification | Daily | 15 minutes | | Backup verification | Weekly | 1 hour | | Helm chart updates (2 clusters) | Monthly | 2–4 hours | | Failover drill (full) | Quarterly | 1 day | | Security patching (2 clusters) | Monthly | 4–8 hours | | **Monthly effort** | | **25–40 hours** | ## Team requirements [#team-requirements] * **Minimum:** 0.75–1 FTE dedicated platform engineer * **Recommended:** 1–1.5 FTE with on-call rotation * **Critical:** Documented failover procedure executable by on-call staff # High Availability Source: https://docs.settlemint.com/docs/architecture/self-hosting/high-availability HA and DR philosophy for self-hosted DALP deployments. Covers RTO/RPO/RTT definitions and a scenario selection guide to help you choose the right deployment pattern. **Purpose:** Explain the HA/DR philosophy and help you select the right deployment scenario for your requirements. * **Doc type:** Explanation * **What you'll find here:** * HA/DR guiding principles * RTO, RPO, RTT definitions * Scenario selection table * **Related:** * [Cloud-native (Recommended)](/docs/architecture/self-hosting/high-availability/cloud-native) * [Hot-warm](/docs/architecture/self-hosting/high-availability/hot-warm) * [Hot-cold](/docs/architecture/self-hosting/high-availability/hot-cold) * [Hot-hot](/docs/architecture/self-hosting/high-availability/hot-hot) * [Backup & Recovery](/docs/architecture/self-hosting/high-availability/backup-recovery) * [Prerequisites](/docs/architecture/self-hosting/prerequisites) *** SettleMint strongly recommends the cloud-native approach with managed services. This configuration provides excellent HA/DR with minimal operational overhead. ## Guiding principles [#guiding-principles] 1. Prefer cloud-native managed services over self-hosted operators 2. Backup and restore should be automatic and tested regularly 3. RTO and RPO targets should be defined before deployment 4. DR drills should be scheduled quarterly ## Recovery metrics [#recovery-metrics] | Metric | Definition | Typical target | | ------- | ---------------------------------- | -------------------- | | **RTO** | Maximum acceptable downtime | 15 minutes – 4 hours | | **RPO** | Maximum acceptable data loss | 0 – 15 minutes | | **RTT** | Realistic end-to-end recovery time | 30 minutes – 8 hours | **RTO** is the target. **RTT** is the realistic measured time including verification steps. Always plan for RTT > RTO. ## Scenario selection [#scenario-selection] Choose a scenario based on your requirements: | Scenario | RTO | RPO | Monthly effort | When to use | | --------------------------------------------------------------------------------- | -------------- | --------------- | -------------- | --------------------------------------- | | [Cloud-native](/docs/architecture/self-hosting/high-availability/cloud-native) | 2–15 minutes | Seconds–1 min | 8–16 hours | Most deployments (recommended) | | [Hot-warm](/docs/architecture/self-hosting/high-availability/hot-warm) | 30–180 minutes | 5–60 minutes | 25–40 hours | Geographic redundancy requirements | | [Hot-cold](/docs/architecture/self-hosting/high-availability/hot-cold) | 8–72 hours | 4–24 hours | 10–20 hours | Cost optimization, acceptable data loss | | [Hot-hot (consortium)](/docs/architecture/self-hosting/high-availability/hot-hot) | 1–10 minutes | Seconds–minutes | 40–60 hours | Multi-region active-active | | [Hot-hot (public)](/docs/architecture/self-hosting/high-availability/hot-hot) | 1–10 minutes | 1–5 minutes | 20–30 hours | Public networks, re-derivable data | Start with cloud-native unless you have specific regulatory, geographic, or cost constraints that require an alternative pattern. # Overview Source: https://docs.settlemint.com/docs/architecture/self-hosting Complete guide to deploying the Digital Asset Lifecycle Platform in your own Kubernetes or OpenShift infrastructure. Covers prerequisites, installation process, and high availability configurations for enterprise deployments. ## Overview [#overview] Self-hosted DALP deployments run entirely within your organization's infrastructure. SettleMint provides Helm charts, container images, and installation support while you maintain operational control over the environment. This approach suits organizations with strict data residency requirements, existing Kubernetes or OpenShift investments, or regulatory mandates for infrastructure ownership. Self-hosting requires experienced Kubernetes or OpenShift operators. Organizations without dedicated platform engineering teams should consider SettleMint's managed deployment options. ## Deployment model [#deployment-model] SettleMint delivers a tested, versioned Helm chart package. Your team provides the infrastructure and prerequisites. SettleMint engineers perform the initial installation and post-deployment configuration including smart contract deployment and indexer validation. ## Documentation sections [#documentation-sections] | Section | Purpose | | ---------------------------------------------------------------------------- | --------------------------------------------- | | [Prerequisites](/docs/architecture/self-hosting/prerequisites) | Infrastructure requirements and checklist | | [Installation process](/docs/architecture/self-hosting/installation-process) | What happens during deployment | | [High availability](/docs/architecture/self-hosting/high-availability) | HA/DR configurations and deployment scenarios | ## Responsibility matrix [#responsibility-matrix] | Area | SettleMint | Client | | -------------------- | ---------------------------------------------- | ------------------------------ | | Helm charts | Development, testing, versioning | Deployment, configuration | | Container images | Building, security scanning | Registry access, pulling | | Installation | Initial deployment, verification | Infrastructure provisioning | | Smart contracts | Deployment, verification | Network access | | Chain indexer | Deployment, validation, readiness checks | Runtime hosting and operations | | Kubernetes/OpenShift | Architecture guidance | Provisioning, maintenance | | Managed services | Configuration recommendations | Provisioning, credentials | | Monitoring | Dashboard templates, alert rules | Grafana hosting, alert routing | | Upgrades | Chart updates, migration guides | Execution, testing | | Incident response | Application-level response (dependent on SLAs) | Infrastructure-level response | SettleMint incident response is dependent on the contracted SLA tier. ## Getting started [#getting-started] 1. Review the [prerequisites](/docs/architecture/self-hosting/prerequisites) to understand infrastructure requirements 2. Provision required managed services (PostgreSQL, Redis, object storage) 3. Prepare DNS entries and TLS certificates 4. Contact SettleMint to schedule installation Do not proceed with infrastructure provisioning until you have reviewed the complete prerequisites checklist. Missing requirements delay installation and may require re-provisioning. ## See also [#see-also] * [DALP Execution Engine](/docs/architecture/components/infrastructure/execution-engine) for component architecture * [Platform overview](/docs/architecture/start-here) for system design # Installation process Source: https://docs.settlemint.com/docs/architecture/self-hosting/installation-process Overview of the SettleMint-managed installation process for self-hosted DALP deployments. Covers pre-installation verification, deployment phases, and post-installation handoff. ## Overview [#overview] This page explains the installation process and outcomes without listing commands. SettleMint performs the deployment while your team provides infrastructure access and prerequisites. The post-Kubernetes setup stage is always performed by SettleMint and requires access to the target environment. ## What SettleMint delivers [#what-settlemint-delivers] | Deliverable | Description | | -------------------------- | ------------------------------------------------------- | | Helm chart package | Versioned charts for DALP and supporting components | | Image registry credentials | Harbor credentials for harbor.settlemint.com | | Baseline configuration | Deployment-ready defaults aligned with your environment | | Deployment plan | Verified install sequence and validation checklist | ## What clients provide [#what-clients-provide] | Requirement | Description | | ------------------------------ | ------------------------------------------------------------------------------------------- | | Kubernetes or OpenShift access | kubeconfig with permissions to install charts and CRDs | | Prerequisites | All items from the [prerequisites](/docs/architecture/self-hosting/prerequisites) checklist | | Environment values | Domains, certificates, and managed service credentials | | Change window | Time window for deployment and verification | | Post-setup access | Network access for contract, and subgraph tasks | ## Installation stages [#installation-stages] ### Stage 1: Pre-installation verification [#stage-1-pre-installation-verification] Purpose: confirm that infrastructure, services, and network access match the prerequisites. * Validate cluster access, namespaces, and storage classes * Verify managed service connectivity (PostgreSQL, Redis, object storage) * Confirm DNS and TLS readiness for enabled routes * Review CRD approvals and security constraints (SCCs on OpenShift) ### Stage 2: Platform deployment [#stage-2-platform-deployment] Purpose: install the Helm charts and bring all DALP services online. * Install operators and supporting charts in the required order * Deploy DALP services and networking (Ingress on Kubernetes, Routes on OpenShift) * Apply default labels, annotations, and security settings Result: all core services are running and reachable inside the cluster. ### Stage 3: Post-deployment setup (SettleMint-owned) [#stage-3-post-deployment-setup-settlemint-owned] Purpose: connect on-chain assets, validate chain indexing, and finalize application configuration. * Deploy smart contracts and record addresses * Validate DIDX connectivity and sync health * Update application configuration with contract and endpoint references Result: the platform is fully wired to the blockchain network and ready for use. ### Stage 4: Verification and handoff [#stage-4-verification-and-handoff] Purpose: confirm health, security, and operational readiness before client handoff. * Validate ingress routes, TLS, and authentication * Confirm dashboards and alerts are producing data * Verify backup configuration and restore readiness Result: you receive a working platform with verified endpoints and access details. ## If the client must run the platform deployment [#if-the-client-must-run-the-platform-deployment] SettleMint can support client-led deployment in exceptional cases, but the post-deployment setup remains SettleMint-owned. ### Required tooling and access [#required-tooling-and-access] * Helm version 3.x and kubectl (or oc CLI on OpenShift) configured for the target cluster * Ability to install CRDs, IngressClass (or Routes on OpenShift), and namespace-scoped resources * Harbor credentials and egress access to harbor.settlemint.com * Access to managed service credentials and TLS certificates ### Required inputs [#required-inputs] * Final FQDN list for enabled routes * TLS certificates and private keys for each route * PostgreSQL, Redis, and object storage connection details * Approval for any operator CRDs required by the charts ## Handoff package [#handoff-package] You receive the following after installation: * Application and API endpoint inventory * Admin credentials for enabled services * Deployed contract addresses and network references * DIDX endpoint information and sync status * Configuration reference for future upgrades ## Post-installation support [#post-installation-support] Support response and incident handling depend on your contracted SLA tier. SettleMint provides upgrade guidance and remediation for DALP components within the agreed support scope. ## Timeline expectations [#timeline-expectations] | Phase | Typical duration | | ----------------------------- | -------------------- | | Pre-installation verification | 1 to 2 business days | | Platform deployment | 1 to 2 business days | | Post-deployment setup | 4 to 8 hours | | Verification and handoff | 2 to 4 hours | | Total | 2 to 4 business days | Timelines assume prerequisites are complete. Gaps in infrastructure or approvals extend the schedule. ## See also [#see-also] * [Prerequisites](/docs/architecture/self-hosting/prerequisites) for infrastructure requirements * [High availability](/docs/architecture/self-hosting/high-availability) for HA and DR configurations * [DALP Execution Engine](/docs/architecture/components/infrastructure/execution-engine) for component architecture # OpenShift installation Source: https://docs.settlemint.com/docs/architecture/self-hosting/openshift-installation OpenShift-specific deployment guidance for self-hosted DALP installations. Covers SCC requirements, Route configuration, and OpenShift Data Foundation integration for enterprise OpenShift environments. ## Overview [#overview] DALP supports deployment on Red Hat OpenShift Container Platform (OCP) and OKD, the community distribution of Kubernetes that powers OpenShift. OpenShift deployments use OpenShift-compatible security settings, Routes for exposed services, and an OpenShift storage class such as OpenShift Data Foundation. DALP components comply with the restricted Security Context Constraints (SCC) profile that OpenShift applies by default. ## Platform requirements [#platform-requirements] | Requirement | Minimum | Recommended | Notes | | ----------------- | ---------- | ----------- | ---------------------------------- | | OpenShift version | 4.14 | 4.16+ | OCP or OKD supported | | Worker nodes | 3 | 6+ | Spread across failure domains | | vCPU per worker | 8 | 16 | More for indexing workloads | | Memory per worker | 32 GB | 64 GB | More for blockchain nodes | | Storage | ODF or CSI | ODF | Must support RWX for some services | ## Security context constraints [#security-context-constraints] DALP images run as non-root with no privilege escalation. The pods satisfy OpenShift's restricted SCC profile without custom SCCs. ### Required security settings [#required-security-settings] All DALP workloads include these security context settings: ```yaml securityContext: runAsNonRoot: true allowPrivilegeEscalation: false capabilities: drop: - ALL seccompProfile: type: RuntimeDefault ``` ### User ID handling [#user-id-handling] OpenShift assigns arbitrary UIDs from each project's UID range. DALP charts set `runAsUser: null` to allow OpenShift's admission controller to inject the appropriate UID. Do not specify explicit UIDs in your values overrides. ## Networking with Routes [#networking-with-routes] OpenShift uses Routes instead of Kubernetes Ingress for external access. DALP charts create Routes only when the OpenShift Route API is available and the relevant Route option is enabled in your values file. ### Route configuration [#route-configuration] Enable Routes for the public surfaces you want the OpenShift Router to expose: ```yaml # DALP dApp Route dapp: openShiftRoute: enabled: true host: dalp.apps.example.com tls: termination: edge insecureEdgeTerminationPolicy: Redirect # Blockscout Route blockscout: openShiftRoute: enabled: true host: explorer.apps.example.com tls: termination: edge insecureEdgeTerminationPolicy: Redirect # Grafana Route in observability chart values grafana: openShiftRoute: enabled: true host: grafana.apps.example.com tls: termination: edge insecureEdgeTerminationPolicy: Redirect ``` The `settlemint/dalp` application chart consumes the dApp and Blockscout Route values. Configure the Grafana Route in the observability chart values when you install the observability chart. If an umbrella chart nests observability under an `observability` key, put the same Grafana values under that chart key. Leave the Grafana Route disabled when operators should reach Grafana through another ingress pattern or a private network path. For the monitoring components behind that route, see [Observability](/docs/architecture/operability/observability). ### TLS termination options [#tls-termination-options] | Option | Use case | Notes | | ----------- | ---------------------------------- | ---------------------------------------- | | edge | Standard HTTPS termination | Router terminates TLS, backend uses HTTP | | passthrough | End-to-end encryption | TLS passes to pod, requires cert in pod | | reencrypt | Internal encryption with own certs | Router terminates and re-encrypts to pod | Most deployments should use `edge` termination with OpenShift's wildcard certificate or a custom certificate. ## Storage configuration [#storage-configuration] ### OpenShift Data Foundation (ODF) [#openshift-data-foundation-odf] For production deployments, OpenShift Data Foundation provides: * Ceph-based distributed storage * RWX support for shared volumes * Built-in replication and recovery * S3-compatible object storage ### Storage class selection [#storage-class-selection] ```yaml global: storageClass: ocs-storagecluster-ceph-rbd # ODF block storage ``` For object storage (RustFS alternative): ```yaml rustfs: enabled: false # Use ODF object storage instead objectStorage: endpoint: s3://rook-ceph-rgw-ocs-storagecluster-cephobjectstore.openshift-storage.svc bucket: dalp-assets existingSecret: dalp-s3-credentials ``` ## Operator integration [#operator-integration] OpenShift operators simplify management of supporting infrastructure. ### CloudNativePG [#cloudnativepg] The CloudNativePG operator works on OpenShift without modification. Install via OperatorHub or Helm. ### Velero [#velero] For backup and disaster recovery, Velero integrates with OpenShift through the OADP (OpenShift API for Data Protection) operator available in OperatorHub. ## NetworkPolicy considerations [#networkpolicy-considerations] OpenShift Network Policies work identically to Kubernetes, with one addition: the OpenShift Router requires explicit ingress rules. DALP charts automatically add router access when running on OpenShift: ```yaml # Automatically included when route.openshift.io/v1 API is detected ingress: - from: - namespaceSelector: matchLabels: network.openshift.io/policy-group: ingress ``` ## Installation steps [#installation-steps] ### Step 1: Prepare the project [#step-1-prepare-the-project] ```bash # Create project (namespace) oc new-project dalp-production # Verify SCC assignment oc get scc restricted-v2 -o yaml ``` ### Step 2: Add Helm repository [#step-2-add-helm-repository] ```bash helm repo add settlemint https://harbor.settlemint.com/chartrepo/dalp helm repo update ``` ### Step 3: Configure values [#step-3-configure-values] Create an OpenShift-specific values file by merging the base `values-openshift.yaml` with your environment configuration: ```yaml # values-production.yaml global: platform: openshift storageClass: ocs-storagecluster-ceph-rbd # Enable Routes for user-facing services dapp: openShiftRoute: enabled: true host: dalp.apps.example.com blockscout: openShiftRoute: enabled: true host: explorer.apps.example.com # Disable Traefik (OpenShift Router handles ingress) traefik: enabled: false ``` ### Step 4: Install the chart [#step-4-install-the-chart] ```bash helm install dalp settlemint/dalp \ -n dalp-production \ -f values-openshift.yaml \ -f values-production.yaml ``` ### Step 5: Verify deployment [#step-5-verify-deployment] ```bash # Check pod status oc get pods -n dalp-production # Verify Routes oc get routes -n dalp-production # Check Route TLS oc get route dalp -n dalp-production -o jsonpath='{.spec.tls.termination}' ``` ## Troubleshooting [#troubleshooting] ### Pod fails with SCC denied [#pod-fails-with-scc-denied] Verify your deployment does not specify explicit UIDs: ```bash oc get deployment -o yaml | grep -A5 securityContext ``` Remove any `runAsUser` with explicit values. Use `null` or omit the field entirely. ### Route not accessible [#route-not-accessible] Check that the Route hostname resolves correctly: ```bash oc get route -o jsonpath='{.spec.host}' nslookup ``` Verify the router pod is running: ```bash oc get pods -n openshift-ingress ``` ### Storage provisioning fails [#storage-provisioning-fails] Confirm the storage class exists and is default: ```bash oc get storageclass oc get pvc -n dalp-production ``` For ODF, verify the storage cluster is healthy: ```bash oc get storagecluster -n openshift-storage ``` ## See also [#see-also] * [Prerequisites](/docs/architecture/self-hosting/prerequisites) for infrastructure requirements * [Installation process](/docs/architecture/self-hosting/installation-process) for deployment steps * [High availability](/docs/architecture/self-hosting/high-availability) for HA/DR configurations # Prerequisites Source: https://docs.settlemint.com/docs/architecture/self-hosting/prerequisites Exhaustive checklist of infrastructure, services, and credentials required before SettleMint can begin DALP installation. Review and complete all requirements to prevent deployment delays. ## Overview [#overview] This page documents everything required before SettleMint engineers can begin installation. Requirements are intentionally strict to prevent deployment failures and ensure reliable operation. Do not schedule installation until all prerequisites are met. Missing requirements cause delays and may require re-provisioning of infrastructure. ## Managed cloud baseline (required) [#managed-cloud-baseline-required] For AWS, Azure, and GCP deployments, DALP requires managed services for PostgreSQL, Redis, object storage, backups, and observability. In-cluster alternatives are only supported for non-hypercloud environments and require additional approvals. If you cannot meet the managed service baseline, review the fully self-hosted section before proceeding. ## Kubernetes or OpenShift cluster requirements [#kubernetes-or-openshift-cluster-requirements] DALP supports deployment on both standard Kubernetes distributions and Red Hat OpenShift. The Helm charts automatically detect the platform and configure appropriate resources (Ingress on Kubernetes, Routes on OpenShift). ### Cluster specifications [#cluster-specifications] | Requirement | Minimum | Recommended | Notes | | ------------------- | ------------------ | ------------------ | --------------------- | | Kubernetes version | 1.27+ | 1.29+ | Standard CNI required | | OpenShift version | 4.14+ | 4.16+ | OCP or OKD supported | | Node count | 3 | 6+ | Multi-AZ distribution | | Node size (compute) | 4 vCPU / 16 GB RAM | 8 vCPU / 32 GB RAM | Per node | | Storage class | ReadWriteOnce | ReadWriteOnce | Default class defined | ### Required platform capabilities [#required-platform-capabilities] **Kubernetes:** * RBAC enabled (namespace-scoped access is supported) * LoadBalancer service type available for Traefik ingress controller * StorageClass available for stateful workloads * Metrics server available for HPA (SettleMint can install if not present) * NetworkPolicy support available in the CNI **OpenShift:** * RBAC enabled (namespace-scoped access is supported) * OpenShift Router available for Route-based ingress (Traefik is disabled) * StorageClass available for stateful workloads * Metrics server built-in * NetworkPolicy support built-in * Security Context Constraints (restricted-v2 SCC) compatible ### Multi-AZ distribution [#multi-az-distribution] Nodes must be distributed across a minimum of three availability zones. Single-AZ deployments are not supported for production workloads. * Topology spread constraints rely on standard zone labels * Pod disruption budgets assume cross-zone scheduling ### Networking expectations [#networking-expectations] * Pod-to-pod communication must be allowed within the deployment namespace * Service mesh injection is not supported (disable Istio or Linkerd sidecars) * HTTPS only for external routes; HTTP is allowed only for redirects ## Managed PostgreSQL (required in AWS, Azure, GCP) [#managed-postgresql-required-in-aws-azure-gcp] ### Cloud provider options [#cloud-provider-options] | Provider | Service | Minimum sizing (baseline) | HA requirement | | -------- | --------------------------------------------- | --------------------------------------- | ----------------- | | AWS | RDS PostgreSQL | 4 vCPU / 16 GB RAM (db.r6g.large) | Multi-AZ enabled | | Azure | Azure Database for PostgreSQL Flexible Server | 4 vCPU / 16 GB RAM (Standard\_D4ds\_v5) | Zone-redundant HA | | GCP | Cloud SQL for PostgreSQL | 4 vCPU / 16 GB RAM (db-custom-4-16384) | Regional HA | ### Database configuration requirements [#database-configuration-requirements] | Parameter | Requirement | Purpose | | ---------------------- | ---------------------------------------------------------- | ------------------------ | | PostgreSQL version | version 17.x (tested on 17.5) | Feature compatibility | | High availability | Multi-AZ or zone-redundant | Automatic failover | | Storage | 100 GB minimum | With auto-growth enabled | | PITR | Enabled, 7-day retention | Point-in-time recovery | | SSL or TLS | Required | Encrypted connections | | `max_connections` | 300+ | Connection pool sizing | | `shared_buffers` | 25 percent of RAM | Memory allocation | | `effective_cache_size` | 75 percent of RAM | Query planner hint | | `work_mem` | 64 MB | Per-operation memory | | `maintenance_work_mem` | 512 MB | Maintenance operations | | Required extensions | pg\_trgm, btree\_gist, pg\_stat\_statements, postgres\_fdw | DALP services | Version flexibility and exceptions: if your cloud provider does not offer PostgreSQL version 17.x, SettleMint can validate PostgreSQL version 16.x only after a formal compatibility review. The review must confirm required extensions, collation options, and performance targets, and it must be approved before scheduling installation. ### Required databases [#required-databases] Create three databases with dedicated owners before installation: | Database | Owner | Notes | | ---------- | ---------- | ------------------------- | | blockscout | blockscout | Required for the explorer | | dapp | dapp | Required for the dapp | ### Connection details to provide [#connection-details-to-provide] * Host endpoint and port * Database names and users * Passwords for each database user * SSL mode and CA bundle if using a private CA ## Managed Redis (required in AWS, Azure, GCP) [#managed-redis-required-in-aws-azure-gcp] ### Cloud provider options [#cloud-provider-options-1] | Provider | Service | Minimum sizing (baseline) | HA requirement | | -------- | --------------------- | ------------------------- | ---------------- | | AWS | ElastiCache for Redis | 6 GB cache (r6g.large) | Multi-AZ enabled | | Azure | Azure Cache for Redis | Premium tier, 6 GB cache | Zone redundancy | | GCP | Memorystore for Redis | Standard tier, 6 GB cache | HA enabled | ### Configuration requirements [#configuration-requirements] | Parameter | Requirement | Purpose | | ----------------- | ----------------------------- | ------------------------- | | Redis version | version 8.x (tested on 8.4.0) | Feature compatibility | | Cluster mode | Disabled | Database index support | | Memory | 6 GB minimum | Session and cache storage | | High availability | Multi-AZ or zone redundancy | Automatic failover | | TLS encryption | Required | Encrypted connections | | AUTH password | Required | Access control | | Persistence | AOF or snapshots enabled | Data durability | Version flexibility and exceptions: if your cloud provider does not offer Redis version 8.x, SettleMint can validate Redis version 7.x only after a formal compatibility review. Cluster mode must remain disabled, TLS and AUTH must be supported, and the exception must be approved before scheduling installation. ### Connection details to provide [#connection-details-to-provide-1] * Primary endpoint and port * AUTH password * TLS CA bundle if using a private CA ## Object storage (required) [#object-storage-required] ### Cloud provider options [#cloud-provider-options-2] | Provider | Service | Configuration baseline | | -------- | ------------- | ------------------------------------------ | | AWS | S3 | Standard storage class, versioning enabled | | Azure | Blob Storage | Hot tier, LRS minimum (GRS recommended) | | GCP | Cloud Storage | Standard class, regional or multi-regional | ### Bucket requirements [#bucket-requirements] | Bucket purpose | Example name | When required | Configuration | | -------------------------- | ----------------------------- | ---------------------------------- | --------------------------------- | | Application storage | project-dalp-storage | Always | Versioning enabled | | Velero backups | project-dalp-velero-backups | If using Velero | Versioning and lifecycle policies | | PostgreSQL WAL and backups | project-dalp-postgres-backups | Only for self-hosted PostgreSQL | Versioning and lifecycle policies | | Observability backups | project-dalp-observability | Only for self-hosted observability | Versioning and lifecycle policies | ### Lifecycle policies [#lifecycle-policies] * Transition to cold storage after 30 days * Delete non-current versions after 90 days * Keep versioning enabled for all buckets ### IAM access requirements [#iam-access-requirements] | Provider | Recommended approach | Alternative | | --------- | ------------------------------------- | ------------------ | | AWS EKS | IRSA (IAM Roles for Service Accounts) | Static credentials | | Azure AKS | Workload Identity | Static credentials | | GCP GKE | Workload Identity | Static credentials | ## Stateful storage sizing and backup capacity [#stateful-storage-sizing-and-backup-capacity] The chart defaults define baseline PVC sizes for stateful workloads. Production networks typically run four Besu validators and two Besu RPC nodes, which increases storage requirements. ### Baseline PVC sizing (chart defaults) [#baseline-pvc-sizing-chart-defaults] | Component | Count | Size | Total | | --------------- | ----- | ---- | ----- | | IPFS cluster | 1 | 1Gi | 1Gi | | IPFS | 1 | 1Gi | 1Gi | | Besu validators | 1 | 10Gi | 10Gi | | Besu RPC nodes | 2 | 10Gi | 20Gi | | Base total | | | 32Gi | We typically run four validators in production. With four validators, the base total becomes 62Gi. Scale this base linearly as validator counts change. ### Backup and retention sizing example [#backup-and-retention-sizing-example] Example with chart defaults (one validator): 32Gi x 59 (retention multiplier) x 0.4 (compression) x 1.2 (headroom) = 906Gi, rounded to 1Ti. Each RustFS replica gets a 1Ti PVC. With two replicas in distributed mode, total storage is 2Ti with about 1Ti usable capacity after replication. Example with four validators (typical production): 62Gi x 59 (retention multiplier) x 0.4 (compression) x 1.2 (headroom) = 1,756Gi, rounded to 2Ti. Each RustFS replica gets a 2Ti PVC. With two replicas in distributed mode, total storage is 4Ti with about 2Ti usable capacity after replication. ## Managed observability (required in AWS, Azure, GCP) [#managed-observability-required-in-aws-azure-gcp] DALP requires metrics, logs, traces, and alerting. For hypercloud deployments, this must be delivered by the cloud provider or an approved managed service. | Provider | Managed service options | Minimum requirement | | -------- | ----------------------------------------------- | ------------------------------------------ | | AWS | CloudWatch, Managed Prometheus, Managed Grafana | Metrics, logs, traces, alerting, retention | | Azure | Azure Monitor, Managed Grafana | Metrics, logs, traces, alerting, retention | | GCP | Cloud Monitoring and Logging | Metrics, logs, traces, alerting, retention | Provide endpoints and credentials so SettleMint can route telemetry from DALP workloads. If you opt out of managed observability, the in-cluster observability stack must be installed instead. ## Managed vs self-hosted configuration matrix [#managed-vs-self-hosted-configuration-matrix] Use this matrix to align the managed baseline with the self-hosted fallback. SettleMint will confirm the final configuration during pre-installation verification. | Capability | Managed baseline (hypercloud) | Self-hosted in cluster | Helm values and inputs | | -------------- | --------------------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | PostgreSQL | Managed database with PITR and HA | CloudNativePG cluster | Managed: set `support.postgresql.mode: external` or `support.postgresql.enabled: false`, and provide `global.datastores.*.postgresql` connection details or `existingSecret`. Self-hosted: set `support.postgresql.mode: cloudnativepg` and enable `operators.cloudnativepg.enabled: true`, then configure `support.postgresql.cloudnativepg.backup` if backups are required. | | Redis | Managed Redis with TLS and AUTH | In-cluster Redis | Managed: set `support.redis.enabled: false` and provide `global.datastores.*.redis` connection details or `existingSecret`. Self-hosted: set `support.redis.enabled: true`. | | Object storage | Managed S3-compatible service | RustFS | Managed: set `support.rustfs.enabled: false`, configure `global.backup.s3`, and update dApp object storage env values. Self-hosted: set `support.rustfs.enabled: true` and size PVCs for retention. | | Observability | Managed metrics, logs, and traces | In-cluster observability stack | Managed: enable `observability.endpoints.external.prometheus`, `observability.endpoints.external.loki`, and `observability.endpoints.external.otel` and disable `observability.grafana`, `observability.loki`, `observability.tempo`, and `observability.victoria-metrics-single`. Self-hosted: keep internal endpoints enabled. | | Backups | Provider-managed backups | Velero and CNPG backups | Managed: keep `global.backup.enabled: false` and disable `operators.velero.enabled` if not required. Self-hosted: enable `operators.velero.enabled`, `global.backup.enabled`, and CloudNativePG scheduled backups when needed. | ## DNS configuration (required) [#dns-configuration-required] ### Default enabled routes [#default-enabled-routes] These routes are enabled in the standard chart configuration: | Service | Example FQDN | Notes | | --------------------- | ------------------------ | ------------------------------------ | | dApp | app.yourcompany.com | Main application | | Explorer (Blockscout) | explorer.yourcompany.com | Explorer UI and API on a single host | | Traefik dashboard | traefik.yourcompany.com | Enabled by default; can be disabled | ### Optional routes (enable only if needed) [#optional-routes-enable-only-if-needed] | Service | Example FQDN | When to enable | | -------------- | ------------------------------ | ----------------------------------------- | | RPC | rpc.yourcompany.com | External JSON-RPC access | | Graph | graph.yourcompany.com | Direct subgraph access | | IPFS | ipfs.yourcompany.com | External IPFS access | | Grafana | grafana.yourcompany.com | Only when using in-cluster observability | | RustFS | rustfs.yourcompany.com | Only when using in-cluster object storage | | RustFS console | rustfs-console.yourcompany.com | Only when console ingress is enabled | When managed observability or managed object storage is used, SettleMint disables the Grafana and RustFS routes in the Helm values. ### DNS requirements [#dns-requirements] * All domains must be delegated and resolvable before installation * Private deployments must use internal DNS that resolves inside the cluster * Wildcard certificates are supported, but individual certificates are preferred ## TLS certificates (required) [#tls-certificates-required] DALP only supports HTTPS for external routes. Provide TLS for every enabled FQDN. ### Options in order of preference [#options-in-order-of-preference] 1. Let's Encrypt via Traefik ACME solver 2. Existing certificates provided as Kubernetes TLS secrets 3. cert-manager with a private CA ### Certificate requirements [#certificate-requirements] * One certificate per domain or a wildcard certificate * Full certificate chain included * Private key in PKCS8 or PKCS1 format * Minimum RSA 2048 or ECDSA P-256 ## Network and outbound access [#network-and-outbound-access] ### Outbound access required [#outbound-access-required] | Destination | Port | Purpose | | --------------------------- | ---------- | --------------------------------------------- | | harbor.settlemint.com | 443 | Container image pulls for all DALP components | | Let's Encrypt ACME | 443 | Certificate issuance if ACME is used | | SMTP provider | 587 or 465 | Transactional email if SMTP is enabled | | Managed PostgreSQL endpoint | 5432 | Database connectivity | | Managed Redis endpoint | 6379 | Cache and session connectivity | | Object storage endpoint | 443 | Application storage and backups | | Observability endpoints | 443 | External metrics, logs, and traces ingestion | All container images are served through harbor.settlemint.com, which proxies upstream registries. Direct access to ghcr.io or docker.io is not required. ### Internal networking [#internal-networking] * Namespace-local pod traffic must be unrestricted * NetworkPolicy resources must be allowed * Service mesh sidecars are not supported ### Ingress requirements [#ingress-requirements] * LoadBalancer must be reachable from intended client networks * Port 443 must be exposed; port 80 optional for redirects ## Image registry access [#image-registry-access] SettleMint provides Harbor registry credentials for harbor.settlemint.com. No GHCR or Docker Hub credentials are required. If you mirror images into your own registry, provide access to that registry before installation. ## Cluster-wide resources, CRDs, and operators [#cluster-wide-resources-crds-and-operators] Some components require cluster-scoped CRDs and resources. Ensure your security team approves CRD installation before scheduling deployment. ### CRDs required by the Helm charts [#crds-required-by-the-helm-charts] | Component | CRDs required | When required | | -------------- | -------------------------------------------------------------------------------------------- | -------------------------------- | | Traefik | IngressRoute, Middleware, TLSOption, TLSStore | Always | | CloudNativePG | clusters.postgresql.cnpg.io, poolers.postgresql.cnpg.io, scheduledbackups.postgresql.cnpg.io | Self-hosted PostgreSQL only | | Velero | backups.velero.io, schedules.velero.io, restores.velero.io | Only if Velero is enabled | | VolumeSnapshot | snapshot.storage.k8s.io resources | Only if snapshot backups enabled | Traefik installs CRDs in the traefik.io and hub.traefik.io API groups. Velero installs CRDs in the velero.io API group, and CloudNativePG installs CRDs in the postgresql.cnpg.io API group. ### Cluster-scoped resources [#cluster-scoped-resources] **Kubernetes:** * IngressClass named dalp is created by the Traefik chart * Traefik CRDs are installed cluster-wide * CloudNativePG and Velero CRDs are cluster-wide even when RBAC is namespaced **OpenShift:** * Routes are used instead of Ingress (no IngressClass required) * Traefik is disabled; OpenShift Router handles ingress * CloudNativePG and Velero CRDs are cluster-wide even when RBAC is namespaced * SecurityContextConstraints may require configuration for non-root workloads ### Namespace-scoped RBAC [#namespace-scoped-rbac] Operators are configured for namespace-scoped RBAC, so ClusterRoleBindings are not required unless you change chart defaults. CRD installation still requires cluster-level permissions. If your organization already operates these components, you can supply them instead as long as they watch the DALP namespace. On OpenShift, the charts are compatible with the restricted-v2 Security Context Constraint. All containers run as non-root with arbitrary UID assignment. ## Fully self-hosted (non-hypercloud) option [#fully-self-hosted-non-hypercloud-option] If managed services are not available, DALP can run PostgreSQL, Redis, object storage, observability, and backups inside the cluster. This requires additional capacity and operational ownership. ### Additional requirements and impact [#additional-requirements-and-impact] * CloudNativePG operator with PostgreSQL version 17.5 image * Redis version 8.4.0 in-cluster deployment with persistence enabled * RustFS for S3-compatible object storage with required buckets * Observability stack (Grafana, Victoria Metrics, Loki, Tempo, Alloy) * Velero operator for Kubernetes resource backups * CRD approval for Traefik, CloudNativePG, Velero, and VolumeSnapshot CRDs * Increased storage, monitoring, and backup verification workload In this mode, service credentials for PostgreSQL, Redis, and RustFS are generated by the charts, and you do not provide external connection details. ## Prerequisites checklist [#prerequisites-checklist] ### Kubernetes or OpenShift infrastructure [#kubernetes-or-openshift-infrastructure] * [ ] Multi-AZ Kubernetes (1.27+) or OpenShift (4.14+) cluster * [ ] Minimum three nodes across three zones * [ ] LoadBalancer service type available (Kubernetes) or OpenShift Router available (OpenShift) * [ ] Metrics server installed or approved for installation (built-in on OpenShift) * [ ] NetworkPolicy resources allowed * [ ] Service mesh injection disabled * [ ] OpenShift only: restricted-v2 SCC compatibility verified ### Managed PostgreSQL [#managed-postgresql] * [ ] PostgreSQL version 17.x * [ ] Multi-AZ or zone-redundant HA enabled * [ ] PITR enabled with seven-day retention * [ ] Required extensions approved and available * [ ] Three databases created with owners * [ ] Connection details documented * [ ] SSL or TLS enabled ### Managed Redis [#managed-redis] * [ ] Redis version 8.x with cluster mode disabled * [ ] Multi-AZ or zone redundancy enabled * [ ] TLS encryption enabled * [ ] AUTH password configured * [ ] Connection details documented ### Object storage [#object-storage] * [ ] Application bucket created * [ ] Backup buckets created if Velero or CNPG backups are enabled * [ ] Versioning and lifecycle policies configured * [ ] IAM or Workload Identity configured ### Managed observability [#managed-observability] * [ ] Metrics, logs, traces, and alerting service selected * [ ] Retention and export requirements defined * [ ] Endpoints and credentials ready for configuration ### DNS and TLS [#dns-and-tls] * [ ] Required FQDNs registered and resolvable * [ ] TLS certificates provided for every enabled route * [ ] ACME access verified if Let's Encrypt is used ### Network [#network] * [ ] Outbound access to harbor.settlemint.com confirmed * [ ] Outbound access to managed services and observability endpoints confirmed * [ ] Ingress LoadBalancer reachable from intended networks ### CRD approval [#crd-approval] * [ ] Traefik CRDs approved (Kubernetes only; not required on OpenShift) * [ ] CloudNativePG CRDs approved if self-hosted PostgreSQL * [ ] Velero CRDs approved if backups are enabled * [ ] VolumeSnapshot CRDs approved if snapshot backups are enabled ## See also [#see-also] * [Installation process](/docs/architecture/self-hosting/installation-process) for deployment phases * [High availability](/docs/architecture/self-hosting/high-availability) for HA and DR configurations * [DALP Execution Engine](/docs/architecture/components/infrastructure/execution-engine) for component details # Asset model Source: https://docs.settlemint.com/docs/architecture/start-here/asset-model How DALP models an issued asset from an asset class and instrument template through token metadata, token features, and compliance rules. A DALP asset is a configured asset definition issued as a [SMART Protocol token](/docs/architecture/components/asset-contracts/smart-protocol-integration), DALP's ERC-3643 implementation with ERC-20-compatible balances and transfers. Asset class, instrument template, metadata fields, token features, and compliance rules define the issued asset. For the issuance workflow, see [Asset issuance](/docs/architecture/flows/asset-issuance). For component details, see [SMART Protocol integration](/docs/architecture/components/asset-contracts/smart-protocol-integration), [Instrument profiles](/docs/architecture/components/asset-contracts/instrument-profiles), [Token features](/docs/architecture/components/token-features), and [Compliance modules](/docs/architecture/security/compliance). ## The short version [#the-short-version] A DALP asset is a [SMART Protocol token configuration](/docs/architecture/components/asset-contracts/smart-protocol-integration), not a standalone token contract. It combines business classification, token metadata, optional runtime token features, and compliance rules. ## Underlying token standard [#underlying-token-standard] DALP issues new assets as [SMART Protocol tokens](/docs/architecture/components/asset-contracts/smart-protocol-integration). SMART Protocol is DALP's ERC-3643 implementation. It keeps ERC-20-compatible balances and transfers, then adds the identity registry, compliance engine, and token-feature hooks that regulated assets need before mints or transfers complete. ## Layers in the model [#layers-in-the-model] | Layer | What it answers | Examples | | ------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | Token standard | Which on-chain standard underlies the issued asset? | SMART Protocol, DALP's ERC-3643 implementation with ERC-20-compatible balances and transfers | | Asset class | What business category does this asset belong to? | Fixed income, equity, fund, stable value, deposit, real estate, precious metal | | Instrument template | Which deployable configuration should the Asset Designer start from? | A system template or organization-specific template with a base asset type | | Metadata fields | Which asset-specific facts must be captured? | Identifier, classification, dates, numeric bounds, addresses, or enum values | | Token features | Which token behavior should run at the asset layer? | Historical balances, maturity redemption, fixed treasury yield, voting power, fees, conversion, permit | | Compliance rules | Which eligibility, supply, approval, or jurisdiction controls apply? | Identity verification, country controls, investor limits, transfer approval, collateral, supply caps, time locks | ## How templates shape issuance [#how-templates-shape-issuance] Instrument templates are the bridge between business language and deployable configuration. A template can define: * the asset class shown to operators, * the base asset type used by the deployment flow, * required token features, * metadata fields and validation rules, * feature-specific configuration fields. During asset creation, operators fill in the configurable fields exposed by the selected template. DALP then uses that completed configuration as the input to the issuance flow. ## Metadata, features, and compliance are separate [#metadata-features-and-compliance-are-separate] Keep these three layers separate when designing an asset: * **Metadata** records asset facts. It describes the instrument and can include required fields, mutability rules, and field-level validation. * **Token features** extend token behavior. They add economic, governance, lifecycle, reporting, or approval-helper behavior at the token layer. * **Compliance modules** decide whether regulated actions are allowed. They enforce identity, jurisdiction, supply, investor-count, collateral, capital-raise, approval, or holding-period rules. Separating the layers lets teams change the right control without treating every product question as a new token type. ## Composition patterns [#composition-patterns] DALPAsset is the runtime-configurable asset contract: it combines the template-selected layers needed for the instrument. The examples below show common patterns for DALPAsset, not separate token types that force a new contract for every business case; legacy specialized asset types cannot use this feature-composition system. | Pattern | Typical token features | Typical compliance controls | What this enables | | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | | Fixed income | [Fixed Treasury Yield](/docs/architecture/components/token-features/fixed-treasury-yield), [Maturity Redemption](/docs/architecture/components/token-features/maturity-redemption), [Historical Balances](/docs/architecture/components/token-features/historical-balances) | Supply cap and jurisdiction-specific controls | Coupon-style yield, maturity date handling, and holder snapshots on one issued asset. | | Equity | [Voting Power](/docs/architecture/components/token-features/voting-power), [Historical Balances](/docs/architecture/components/token-features/historical-balances) | Identity, country, and investor-count controls as required by the offering | Governance voting and shareholder-record snapshots with transfer eligibility checks. | | Managed fund | [AUM Fee](/docs/architecture/components/token-features/aum-fee), [Voting Power](/docs/architecture/components/token-features/voting-power), [Historical Balances](/docs/architecture/components/token-features/historical-balances) | Identity and jurisdiction controls | Management-fee collection, investor governance, and balance snapshots for NAV or reporting workflows. | | Stable value asset | [Historical Balances](/docs/architecture/components/token-features/historical-balances) | Collateral, identity, country, or other jurisdiction controls | Collateral-aware issuance controls with audit snapshots for holder and supply reporting. | | Precious metal | [Historical Balances](/docs/architecture/components/token-features/historical-balances) | Jurisdiction controls selected for the issuance context | Custody and ownership reporting while supply can grow as backing changes. | Configurable features add guided Asset Designer inputs when the selected template needs them. Current configurable feature inputs include maturity redemption terms, AUM fee settings, external transaction fee settings, and conversion terms. Self-contained features can still change token behavior, but they do not always add a separate setup step unless the template supplies defaults or related settings. Use this table as a design aid, then review the detailed feature and compliance pages before creating the asset configuration. ## When to read the detailed pages [#when-to-read-the-detailed-pages] | Question | Read next | | ----------------------------------------------- | ---------------------------------------------------------------------------------------- | | Which asset profile should I choose? | [Instrument profiles](/docs/architecture/components/asset-contracts/instrument-profiles) | | What happens during deployment? | [Asset issuance](/docs/architecture/flows/asset-issuance) | | Which optional token behavior is available? | [Token features](/docs/architecture/components/token-features) | | Which rules can restrict transfers or issuance? | [Compliance modules](/docs/architecture/security/compliance) | | How does identity-backed compliance work? | [Identity & Compliance](/docs/architecture/security/identity-compliance) | ## Design checklist [#design-checklist] Before issuing an asset, confirm: 1. The selected asset class matches the business instrument. 2. The template's base asset type matches the deployment behavior you need. 3. Required metadata fields are known and can be maintained by the right operators. 4. Token features are necessary for the asset behavior, not a substitute for compliance policy. 5. Compliance modules and parameters match the intended eligibility and transfer controls. 6. The initial operators have the required platform and on-chain roles for issuance and later servicing. # Glossary Source: https://docs.settlemint.com/docs/architecture/start-here/glossary Definitions of key terms, protocols, and components used throughout the DALP architecture documentation. **Purpose:** Define the terms used across the architecture documentation set. **Doc type:** Reference. **What you will find here:** * Platform and protocol terms * Component names and their meanings * On-chain concepts and standards **Related:** [Architecture map](/docs/architecture/start-here) | [System context](/docs/architecture/start-here/system-context) | [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) *** ## Terms [#terms] | Term | Definition | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **DALP** | Digital Asset Lifecycle Platform. SettleMint's platform for issuing, managing, and servicing tokenized financial instruments across their full lifecycle. | | **SMART Protocol** | SettleMint Adaptable Regulated Token. The foundational protocol specification that defines token standards, compliance modules, and identity interfaces based on ERC-3643. | | **ERC-3643** | Ethereum standard for regulated security tokens with conditional transfers based on investor eligibility and integrated identity verification. DALP provides one of two complete implementations. | | **OnchainID** | On-chain identity framework implementing ERC-734 (key management) and ERC-735 (claim management). Stores verifiable claims about users and entities. | | **Compliance Module** | A pluggable on-chain rule evaluated on every transfer. Examples: country allow/block lists, identity verification, transfer limits, time-based lock-ups. Multiple modules compose into an asset's compliance policy. | | **Token Feature** | A runtime-configurable capability registered on a token through the Configurable extension. Unlike compile-time extensions, features can be added after deployment. Examples: fixed-treasury-yield, lock-up enforcement. | | **Identity Registry** | Per-system contract mapping wallet addresses to on-chain identity contracts. Manages verification status and supports wallet recovery. | | **Trusted Issuer** | An entity authorized to issue verifiable claims for specific claim topics. Registered in the Trusted Issuers Registry. Multiple issuers can cover the same topic. | | **Claim Topic** | A category of verifiable attestation (e.g., KYC, nationality, accreditation). Defined in the Topic Scheme Registry and referenced by tokens to specify required investor claims. | | **Key Guardian** | Secure cryptographic key storage component. Abstracts over local encrypted keys, DFNS MPC wallets, Fireblocks vaults, and hardware security modules. | | **Transaction Signer** | Prepares transactions (gas estimation, nonce assignment), delegates signing to the custody provider, and manages broadcast and confirmation. | | **Chain Indexer** | Listens to on-chain events from SMART Protocol contracts, translates them into structured data, and persists them to the application database. | | **Chain Gateway** | Multi-network connectivity layer providing EVM RPC access. Abstracts network-specific differences (gas models, confirmation depth) behind a unified interface. | | **Feeds System** | Market data infrastructure providing price and FX rate feeds through a centralized directory (FeedsDirectory). Chainlink-compatible. Supports global and token-specific feeds. | | **XvP Settlement** | Cross-value proposition settlement. Addon enabling atomic delivery-versus-payment between two asset legs -- both complete or neither does. | | **DAIO** | Digital Asset Initial Offering. Primary distribution mechanism for moving newly issued assets to verified investors with atomic settlement, lock-up enforcement, and soft-cap refund mechanics. | | **Airdrop** | Token distribution addon delivering tokens to recipient addresses according to a configured strategy. Integrates with the compliance layer for eligibility. | | **Vault** | Multi-signature treasury management addon for holding and governing settlement currency or digital assets with configurable approval thresholds. | | **Denomination Asset** | The settlement currency designated for an asset's financial operations (distributions, offerings, redemptions). Typically a regulated stablecoin or fiat-linked token. | | **Factory Pattern** | Deployment pattern where factory contracts deploy new instances of assets, addons, and infrastructure contracts. Registered in the Factory Registry for consistent configuration. | | **Virtual Object** | A Restate durable, keyed state machine that survives process restarts. Used in the Execution Engine for long-running workflows like signing and multi-step operations. | | **Dead Letter Queue** | Holding area for operations that exhausted retry attempts in the Execution Engine. Items require manual investigation. | | **Addon** | An operational tool contract extending asset capabilities beyond the core SMART Protocol. Registered through the Addon Registry. Current addons: Airdrop, Vault, XvP Settlement, Token Sale (DAIO), Yield. | ## Standards referenced [#standards-referenced] | Standard | Full name | Usage in DALP | | ------------ | ------------------------- | ----------------------------------------------------- | | **ERC-20** | Fungible token standard | Base token compatibility for all SMART tokens | | **ERC-165** | Interface detection | Programmatic capability queries on SMART tokens | | **ERC-734** | Key management | On-chain identity key management in OnchainID | | **ERC-735** | Claim management | Verifiable claims on identity contracts | | **ERC-2771** | Meta-transactions | Gasless transaction support via trusted forwarders | | **ERC-3643** | Regulated security tokens | Foundation for SMART Protocol compliance architecture | ## Next steps [#next-steps] * [System context](/docs/architecture/start-here/system-context) to see how these terms relate to the platform architecture * [Key flows](/docs/architecture/start-here/key-flows) to see these components in action * [Components](/docs/architecture/components) for detailed boundaries and responsibilities of each component # Architecture map Source: https://docs.settlemint.com/docs/architecture/start-here One-page orientation for the DALP architecture documentation set. Use this page to understand the platform structure and navigate to the section you need. **Purpose:** Entry point to the DALP architecture documentation. **Doc type:** Tutorial (onboarding to the doc set). **Audience:** Head of IT, enterprise architects, security leadership. This audience applies to all pages in the architecture documentation set. **What you will find here:** * A high-level view of the four platform layers plus the Feeds system * A navigable table linking every documentation section * Guidance on which section to read for a given question **Related:** [System context](/docs/architecture/start-here/system-context) | [Asset model](/docs/architecture/start-here/asset-model) | [Lifecycle after issuance](/docs/architecture/start-here/lifecycle-after-issuance) | [Key flows](/docs/architecture/start-here/key-flows) | [Glossary](/docs/architecture/start-here/glossary) | [Platform overview](/docs/architecture/overview) *** ## Platform at a glance [#platform-at-a-glance] DALP organizes into four integrated layers. User interactions enter through the Asset Console or Unified API, flow through the DALP Execution Engine for durable orchestration, and settle on-chain through the SMART Protocol. The Feeds system runs alongside, providing market data to any layer that needs it. Platform dashboard with portfolio overview and key operational metrics **Three takeaways:** 1. Every user action passes through the Execution Engine before reaching the blockchain. 2. The Feeds system is decoupled from the main request path and can be queried by any layer. 3. The SMART Protocol is the single source of truth for asset state and compliance. ## Navigate this documentation [#navigate-this-documentation] | Section | What you will find | When to read it | | ----------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------- | | [Start Here](/docs/architecture/start-here) | System context, asset model, lifecycle after issuance, key flows index, glossary | First visit or onboarding a new architect | | [Overview](/docs/architecture/overview) | Principles, quality attributes, deployment topology, data domains | Evaluating platform fit or planning capacity | | [Components](/docs/architecture/components) | Catalog of every platform component with boundaries and responsibilities | Understanding ownership or planning an integration | | [Flows](/docs/architecture/flows) | Step-by-step sequences for the seven key platform operations | Tracing a specific operation end-to-end | | [Integrations](/docs/architecture/integrations) | External systems, custody providers, supported networks | Assessing third-party dependencies or adding a new chain | | [Security](/docs/architecture/security) | Authentication, authorization, identity compliance, wallet verification | Security review, audit preparation, or threat modeling | | [Operability](/docs/architecture/operability) | Observability model, database architecture, failure modes | Operational readiness review or incident preparation | ## Suggested reading paths [#suggested-reading-paths] **First-time orientation:** Start Here (this page) then [System context](/docs/architecture/start-here/system-context) then [Asset model](/docs/architecture/start-here/asset-model) then [Lifecycle after issuance](/docs/architecture/start-here/lifecycle-after-issuance) then [Key flows](/docs/architecture/start-here/key-flows) then [Overview](/docs/architecture/overview). **Security review:** [Security](/docs/architecture/security) then [Signing flow](/docs/architecture/flows/signing-flow) then [Integrations](/docs/architecture/integrations/custody-providers). **Operational readiness:** [Operability](/docs/architecture/operability) then [Deployment topology](/docs/architecture/overview/deployment-topology) then [Failure modes](/docs/architecture/operability/failure-modes). **Integration planning:** [Components](/docs/architecture/components) then [Integrations](/docs/architecture/integrations) then [Unified API](/docs/architecture/components/platform/unified-api). # Key flows Source: https://docs.settlemint.com/docs/architecture/start-here/key-flows Index of the seven most important system flows in DALP, covering platform operations and asset lifecycle capabilities with links to detailed walkthroughs. **Purpose:** Provide a navigable index of the key platform and capability flows. **Doc type:** Reference (flow index). **What you will find here:** * Platform flows that every transaction passes through * Capability flows for specific asset lifecycle operations * For each flow: trigger, outcome, and key components involved **Related:** [Architecture map](/docs/architecture/start-here) | [System context](/docs/architecture/start-here/system-context) | [Lifecycle after issuance](/docs/architecture/start-here/lifecycle-after-issuance) | [Flows section](/docs/architecture/flows) | [Components](/docs/architecture/components) *** ## Platform flows [#platform-flows] These flows represent core platform operations. Every asset lifecycle action depends on one or more of them. | Flow | Trigger | Outcome | Key components | | ------------------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | | [Signing flow](/docs/architecture/flows/signing-flow) | Any state-changing operation (transfer, mint, redeem) | Transaction signed by custody provider and confirmed on-chain | Execution Engine, Transaction Signer, Key Guardian, Custody Provider (DFNS/Fireblocks), Chain Gateway, SMART Protocol compliance engine | | [Asset issuance](/docs/architecture/flows/asset-issuance) | Issuer creates a new tokenized instrument | Asset contract deployed with compliance rules, identity requirements, and optional features configured | Asset Console/API, Execution Engine, Factory Registry, System layer, SMART Protocol | | [Compliance transfer](/docs/architecture/flows/compliance-transfer) | Token holder initiates a transfer | Transfer completes if both on-chain compliance and custodian policies pass; reverts otherwise | SMART Protocol compliance engine, Identity Registry, Compliance Modules, Custody Provider policy engine | Asset Designer guides issuers through a structured 7-step creation process ## Capability flows [#capability-flows] These flows represent specific asset lifecycle capabilities that issuers configure per instrument. | Flow | Trigger | Outcome | Key components | | ----------------------------------------------------------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | | [DAIO offering](/docs/architecture/flows/daio-offering) | Issuer creates a Digital Asset Initial Offering | Verified investors participate atomically through settlement-currency exchange for digital assets with optional lock-up | DAIO Module, Compliance Layer, Settlement Engine, Custody Accounts | | [Airdrop distribution](/docs/architecture/flows/airdrop-distribution) | Issuer initiates token distribution to a set of recipients | Tokens distributed to eligible addresses according to the configured strategy | Airdrop Addon, Compliance Layer, Identity Registry | | [Treasury distribution](/docs/architecture/flows/treasury-distribution) | Scheduled interval or lifecycle event (coupon date, maturity) | Settlement currency distributed from asset treasury to eligible investors proportional to holdings | Asset Treasury, Payment Features (Yield/Coupon/Redemption), Compliance Layer | | [XvP settlement](/docs/architecture/flows/xvp-settlement) | Counterparties agree to exchange assets | Atomic delivery-versus-payment across two asset legs, where both complete or neither does | XvP Addon, Compliance Layer (both legs), Settlement Engine | XVP settlement configuration for atomic swap operations ## How to read a flow page [#how-to-read-a-flow-page] Each flow page in the [Flows section](/docs/architecture/flows) follows a consistent structure: 1. **Overview** -- what the flow does and why it matters 2. **Sequence diagram** -- step-by-step visual with numbered steps 3. **Step breakdown** -- detailed explanation of each step 4. **Failure handling** -- what happens when a step fails 5. **Related resources** -- links to component, security, and API documentation ## Flow dependencies [#flow-dependencies] All capability flows depend on the signing flow for on-chain execution and on the compliance transfer logic for transfer validation. The dependency structure: * **Signing flow** -- used by every flow that changes on-chain state * **Compliance transfer** -- used by every flow that moves tokens between addresses * **Asset issuance** -- prerequisite for all capability flows (an asset must exist first) * **DAIO, Airdrop, Treasury, XvP** -- independent of each other; each can be configured separately on an asset ## Next steps [#next-steps] * [Flows section](/docs/architecture/flows) for full walkthroughs of each flow * [Components](/docs/architecture/components) to understand the components referenced above * [Security](/docs/architecture/security) for how authentication and compliance enforcement work across these flows # Lifecycle after issuance Source: https://docs.settlemint.com/docs/architecture/start-here/lifecycle-after-issuance How DALP operators manage an issued asset after deployment, from the asset detail workspace through supply, transfer, pause, role, compliance, feature, and audit operations. **Purpose:** Explain the day-two operating model for an issued DALP asset. * **Doc type:** Explanation * **What you'll find here:** * Which surfaces operators use after an asset is deployed * How common servicing actions relate to token roles, compliance, and features * Where to read the detailed user and API guides for each operation * **Related:** * [Asset model](/docs/architecture/start-here/asset-model) * [Asset issuance](/docs/architecture/flows/asset-issuance) * [Asset detail workspace](/docs/user-guides/asset-servicing/asset-detail-workspace) * [Token lifecycle API](/docs/developer-guides/api-integration/token-lifecycle) *** ## The short version [#the-short-version] After issuance, a DALP asset is operated from the asset detail workspace, the Unified API, and the token's configured feature pages. Operators use these surfaces to monitor holders and transfers, manage supply, pause or unpause activity, run permitted servicing actions, update token-level roles, and reconcile events. The exact actions available for an asset depend on its token roles, compliance modules, enabled token features, wallet verification state, and current asset state. ## Operating surfaces [#operating-surfaces] | Surface | Use it for | Notes | | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | Asset detail workspace | Review the issued asset, holder table, actions, compliance configuration, documents, feature tiles, and the Manage Asset menu. | The workspace only shows actions and tabs that apply to the current asset and system configuration. | | Manage Asset menu | Start common operator workflows such as minting, pause or unpause, forced transfer, verification actions, collateral updates, transfer approvals, and token-sale creation when available. | Menu entries are permission-gated and can be hidden or disabled when the asset state, role, feature, or system check does not pass. | | Token lifecycle API | Automate creation, minting, transfer, burn, feature, and reconciliation workflows. | State-changing calls return synchronous transaction metadata or async status links, depending on the operation. | | Token feature pages | Operate feature-specific workflows such as maturity redemption, fixed treasury yield, conversion, AUM fees, transaction fees, and historical balance reads. | Read the token's attached features before submitting a feature mutation. | ## Common post-issuance operations [#common-post-issuance-operations] | Operation area | What changes | Primary control | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- | | Supply servicing | Minting increases supply; burning decreases supply and can target one or more holder balances. | Supply-management token role, wallet verification, holder balance checks, and asset state checks. | | Transfer control | Normal holder transfers must pass identity and compliance checks; forced-transfer workflows are custodian operations for exceptional servicing cases. | Token-holder balance and compliance checks for normal transfers; custodian permission for forced transfers. | | Pause state | Pausing blocks selected token operations until an authorized operator unpauses the asset. | Emergency token role and current paused state. | | Token roles | Role grants and revocations change which operators can administer, govern, manage supply, or run emergency workflows for the token. | Token admin role. | | Compliance configuration | Operators can review and, where permitted, configure token-level compliance modules and parameters. | Compliance-manager or governance-controlled routes, depending on the operation. | | Feature operations | Configured features add day-two actions such as maturity, redemption, yield claims, treasury top-ups, fee configuration, conversion, and historical balance reads. | The feature must be attached; each operation uses the role or signer condition for that feature. | | Reconciliation | Operators review action status, token events, holder balances, transfers, documents, and feature status after transactions confirm. | Actions, events, holder, transfer, document, and feature reads. | ## Feature-gated lifecycle actions [#feature-gated-lifecycle-actions] Not every issued asset has the same day-two operations. Feature actions should be treated as available only when the token reports the matching feature attached. Examples of feature-gated operations include: * maturity-redemption operations for maturing, early maturity, treasury top-ups, treasury updates, wallet-treasury allowance, and holder redemption; * fixed-treasury-yield operations for treasury setup, top-ups, and holder yield claims; * conversion operations for triggers, conversion windows, holder conversion, forced conversion, and authorized converters; * fee operations for rate, recipient, exemption, freeze, collection, or reconciliation workflows, depending on the configured fee feature; * historical balance reads for holder snapshots and reporting. Treasury-backed feature operations depend on denomination-asset funding. A top-up funds the configured feature treasury or legacy redemption pool. Top-ups do not mint new payout assets. ## Read before you mutate [#read-before-you-mutate] Before running a lifecycle mutation, confirm: 1. The token exists and the current user can see it in the asset detail workspace or token read endpoint. 2. The caller has the token role or signer condition required for the operation. 3. The asset is in the right state, such as unpaused for minting, sufficient holder balance before burning, or matured for redemption. 4. The required compliance module, token feature, treasury, trigger, or collateral configuration is present. 5. Wallet verification is current when the workflow signs a transaction. 6. The resulting transaction status, action record, event, holder balance, or feature status has been reconciled before retrying. ## Where to go next [#where-to-go-next] | If you need to... | Read next | | ------------------------------------ | -------------------------------------------------------------------------------------- | | Review an issued asset in the UI | [Asset detail workspace](/docs/user-guides/asset-servicing/asset-detail-workspace) | | Mint new supply | [Mint assets](/docs/user-guides/asset-servicing/mint-assets) | | Burn outstanding supply | [Burn assets](/docs/user-guides/asset-servicing/burn-assets) | | Pause or unpause an asset | [Pause or unpause an asset](/docs/user-guides/asset-servicing/pause-unpause-asset) | | Change token-level administrators | [Change asset admin roles](/docs/user-guides/asset-servicing/change-asset-admin-roles) | | Review all API operation flows | [Token lifecycle API](/docs/developer-guides/api-integration/token-lifecycle) | | Understand configured token behavior | [Token features](/docs/architecture/components/token-features) | | Understand transfer eligibility | [Compliance transfer](/docs/architecture/flows/compliance-transfer) | # System context Source: https://docs.settlemint.com/docs/architecture/start-here/system-context DALP system boundary, external actors, trust boundaries, and the five-layer smart contract architecture that underpins the platform. DALP separates user interfaces, API orchestration, and on-chain enforcement into clear system boundaries. A system owns the infrastructure that keeps assets, identities, compliance rules, and factory registries scoped to that operating context. **Related:** [Architecture map](/docs/architecture/start-here) | [Key flows](/docs/architecture/start-here/key-flows) | [Security](/docs/architecture/security) | [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) ## External actors [#external-actors] | Actor | Interaction | Entry point | | ----------------------- | --------------------------------------------------------------------------------- | -------------------------------------------------- | | **Asset Issuers** | Configure and issue tokenized financial instruments, manage lifecycle events | Asset Console, Unified API | | **Investors** | Participate in offerings, hold assets, receive distributions | Asset Console (read-only views), custodian wallets | | **Compliance Officers** | Define compliance rules, manage identity claims, review audit trails | Asset Console, Unified API | | **Platform Operators** | Deploy infrastructure, monitor health, manage access control | Helm charts, observability dashboards, Unified API | | **External Systems** | Wallets, exchanges, custodians (DFNS, Fireblocks), EVM RPC nodes, oracle services | Unified API, Chain Gateway, Feeds system | ## Trust boundaries [#trust-boundaries] Three trust boundaries separate external actors from the blockchain state that DALP manages. **Three takeaways:** 1. Authentication and authorization are enforced at the interface layer before any operation reaches the engine. 2. The Execution Engine adds durable orchestration, transaction signing, and custody policy evaluation. 3. The SMART Protocol enforces compliance on-chain as the final, immutable gate on every state change. ### Boundary details [#boundary-details] | Boundary | Controls | Enforced by | | ---------------------- | ---------------------------------------------------------------- | ----------------------------------------- | | **1 - Authentication** | Session auth (Better Auth), API key validation, rate limiting | Asset Console, Unified API | | **2 - Orchestration** | Custody policies, nonce management, gas estimation, retry logic | DALP Execution Engine (Restate workflows) | | **3 - On-chain** | Identity verification, compliance modules, transfer restrictions | SMART Protocol smart contracts | ## Five-layer smart contract architecture [#five-layer-smart-contract-architecture] The on-chain side of DALP follows a layered architecture where each level builds on the one below it. Lower layers are more stable and shared; upper layers are more specific and change more frequently. **Three takeaways:** 1. The SMART Protocol foundation is shared across all deployments and defines the ERC-3643 token standard. 2. Global and System layers are infrastructure, deployed once per chain and once per system instance, respectively. 3. Assets and Addons represent business logic that issuers configure per financial instrument. ### Layer summary [#layer-summary] | Layer | Purpose | Key components | | ------------------ | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | | **SMART Protocol** | ERC-3643 token framework with modular compliance, identity management, and extension system | Core token, compliance engine, identity registry interfaces | | **Global** | Platform-wide infrastructure shared across all system instances on a given chain | Central directory, identity factory, identity implementations | | **System** | Per-system infrastructure managing identity registration, compliance, access control, and token factory scope | Identity registry, compliance orchestration, access manager, factory registries | | **Assets** | Deployed tokenized financial instruments built on the SMART Protocol and created through the system's registered factories | DALPAsset, Bond, Equity, Fund, Deposit, StableCoin, RealEstate, PreciousMetal | | **Addons** | Operational tools that extend assets with distribution, settlement, and treasury capabilities | Airdrop, Vault, XvP Settlement, Token Sale (DAIO), Yield | ### How layers interact [#how-layers-interact] A user request flows top-down through the stack: 1. An **addon** (e.g., Airdrop) or direct API call triggers an operation on an **asset** (e.g., Bond) 2. The asset delegates identity and compliance checks to the **system** layer 3. The system resolves implementations through the **global** directory 4. The **SMART Protocol** executes the compliant transfer or state change ### System and asset isolation [#system-and-asset-isolation] Each DALP system has its own access manager, identity registry, compliance orchestration, and factory registries. Assets created through those registries stay associated with that system. API reads that return system-scoped token data use the active system address or the active system's token factory registry to keep results inside the selected system. Factory registries are part of the isolation boundary. If a system has no registered token factories, token reads that depend on factory scope fail closed instead of showing assets from another system. ## Next steps [#next-steps] * [Key flows](/docs/architecture/start-here/key-flows) to see how the most important operations traverse these layers * [Components](/docs/architecture/components) for detailed component boundaries and responsibilities * [Security](/docs/architecture/security) for authentication, authorization, and compliance controls # Account native balances Source: https://docs.settlemint.com/docs/developer-guides/api-integration/account-native-balances Read the latest indexed native balance and recent balance history for accounts in the active DALP system. Use account native-balance reads when an integration needs the latest indexed gas balance for a platform-relevant address, such as an operator wallet, smart account, system contract, or asset contract. DALP returns indexed account state for the active system. Each account result includes the chain ID, address, entity type, optional contract name, latest native balance, the observed block, and the observed timestamp. ## Endpoints [#endpoints] The account native-balance API exposes three read endpoints: | Endpoint | Use it for | | ----------------------------------------------------------------- | ------------------------------------------------------- | | `GET /api/v2/accounts` | List indexed accounts with their latest native balance. | | `GET /api/v2/accounts/{chainId}/{address}` | Read one indexed account by chain ID and address. | | `GET /api/v2/accounts/{chainId}/{address}/native-balance/history` | Read recent native-balance history for one account. | These endpoints use indexed state. If a wallet was funded very recently, compare `nativeBalanceObservedAtBlock` with chain and indexer health before treating a missing or stale balance as final. ## Read one account [#read-one-account] Use the by-address endpoint when you already know the account address. ```bash curl "$DALP_API_URL/api/v2/accounts/1/0x1000000000000000000000000000000000000001" \ --header "X-Api-Key: $DALP_API_TOKEN" ``` A successful response contains one account and links to the same resource and its history endpoint: ```json { "data": { "chainId": 1, "address": "0x1000000000000000000000000000000000000001", "entityType": "operator-wallet", "contractName": "Operator Wallet", "nativeBalance": "12345", "nativeBalanceObservedAtBlock": "8154321", "nativeBalanceObservedAt": "2026-05-01T11:59:30.000Z" }, "links": { "self": "/v2/accounts/1/0x1000000000000000000000000000000000000001", "history": "/v2/accounts/1/0x1000000000000000000000000000000000000001/native-balance/history" } } ``` DALP returns a not-found response when the account is unknown for the active system or when the account has no indexed native-balance state for that system. ## List indexed accounts [#list-indexed-accounts] Use the collection endpoint when you need to discover monitored addresses before reading one account. ```bash curl --globoff "$DALP_API_URL/api/v2/accounts?filter[chainId][eq]=1&filter[entityType][eq]=operator-wallet&sort=address&page[limit]=50" \ --header "X-Api-Key: $DALP_API_TOKEN" ``` The list endpoint returns a paginated collection envelope with `data`, `meta`, and `links`. It can filter by `chainId` and `entityType`. It can sort by `firstSeenBlock`, `address`, `nativeBalance`, and `nativeBalanceObservedAt`. Supported `entityType` values include `eoa`, `asset`, `bond`, `equity`, `fund`, `vault`, `deposit`, `stablecoin`, `real-estate`, `precious-metal`, `system`, `smart-account`, `operator-wallet`, and `contract`. ## Read balance history [#read-balance-history] Use the history endpoint when you need recent balance observations for one account. The request must include `filter[since]` as an observed-block lower bound, and each page can request at most 100 rows. ```bash curl --globoff "$DALP_API_URL/api/v2/accounts/1/0x1000000000000000000000000000000000000001/native-balance/history?filter[since][gte]=8154000&page[limit]=100&sort=-observedAtBlock" \ --header "X-Api-Key: $DALP_API_TOKEN" ``` History rows include `nativeBalance`, `nativeBalanceObservedAtBlock`, and `nativeBalanceObservedAt`. Results are scoped to the active system and exclude rows outside the configured history retention window. ## Read the latest balance from the CLI [#read-the-latest-balance-from-the-cli] For scripts and operations checks, use the CLI account command to read the latest indexed native balance for one address: ```bash dalp account native-balance read 1 0x1000000000000000000000000000000000000001 ``` The command calls the same by-address account read used by the API. Use API collection and history reads when you need pagination, filtering, or historical observations. ## Related [#related] * [API reference](/docs/developer-guides/api-integration/api-reference) * [Blockchain monitoring](/docs/developer-guides/operations/blockchain-monitoring) * [CLI command reference](/docs/developer-guides/cli/command-reference) # Address book contacts Source: https://docs.settlemint.com/docs/developer-guides/api-integration/address-book-contacts Use the DALP contacts API to store frequently used wallet recipients, search them by name or address, and reduce operator error in transfer workflows. DALP includes an authenticated address book for wallet contacts. Use it to store frequent recipients such as treasury wallets, custodian accounts, issuer operations wallets, or investor addresses that operators reuse in servicing flows. Contacts are not identity attestations, KYC records, or permission grants. They are user-owned address book entries that make operational workflows easier to execute and review. Transfer compliance still runs through the token, identity, claim, freeze, and approval rules configured for the asset. ## When to use contacts [#when-to-use-contacts] Use contacts when your integration needs to: * keep named wallet recipients available to the authenticated user * avoid copying raw wallet addresses into every transfer flow * search common counterparties by label or wallet fragment * review recipient addresses before mint, transfer, forced transfer, or servicing operations ## Endpoint summary [#endpoint-summary] The v2 contacts API exposes these operations: * `GET /api/v2/contacts` lists contacts for the authenticated user. * `GET /api/v2/contacts/{id}` reads one contact entry. * `POST /api/v2/contacts` creates or updates a contact entry. * `DELETE /api/v2/contacts/{id}` deletes one contact entry. The legacy v1 API also exposes `/api/contacts`, `/api/contacts/{id}`, and `/api/contacts/search` for older integrations. ## Contact shape [#contact-shape] A contact stores: * `id`: contact identifier * `name`: human-readable label, up to 120 characters * `wallet`: Ethereum wallet address * `createdAt`: creation timestamp * `updatedAt`: last update timestamp Only valid Ethereum addresses are accepted. DALP wallet contacts use EVM account addresses. ## Create or update a contact [#create-or-update-a-contact] Create a contact by posting a name and wallet. Provide `id` only when updating an existing contact. ```bash curl -X POST https://your-platform.example.com/api/v2/contacts \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "name": "Treasury Wallet", "wallet": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F" }' ``` When creating without an `id`, if the wallet already belongs to an existing contact, DALP updates the existing contact name instead of creating a duplicate. ## List contacts [#list-contacts] Use the list endpoint for address-book screens and recipient pickers. ```bash curl "https://your-platform.example.com/api/v2/contacts?limit=50&sortBy=name" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` Contacts can be sorted by: * `name` * `wallet` * `createdAt` * `updatedAt` Use a search filter when the operator types a name or wallet fragment: ```bash curl "https://your-platform.example.com/api/v2/contacts?filter[q]=treasury" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` ## Read or delete a contact [#read-or-delete-a-contact] Read a single contact: ```bash curl https://your-platform.example.com/api/v2/contacts/CONTACT_ID \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` Delete a contact: ```bash curl -X DELETE https://your-platform.example.com/api/v2/contacts/CONTACT_ID \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` Deleting a contact only removes the address-book entry. It does not affect token balances, identities, claims, transfer approvals, custody rules, or any historic transaction evidence. ## Operational guidance [#operational-guidance] For regulated asset operations, treat contacts as convenience data, not control data: 1. Use contacts to reduce copy-paste errors in recurring operations. 2. Resolve the selected contact to a wallet address before submitting a token operation. 3. Let the token operation enforce identity, compliance, freeze, approval, and role checks. 4. Record the resulting transaction hash or action status in the relevant servicing workflow. This keeps address-book usability separate from the controls that decide whether a transfer, mint, burn, forced transfer, or servicing action can execute. ## Related [#related] * [Developer guides](/docs/developer-guides) * [Token holders and transfers](/docs/developer-guides/api-integration/token-holders-transfers) # API reference Source: https://docs.settlemint.com/docs/developer-guides/api-integration/api-reference Access the OpenAPI specification and generate type-safe clients for integrating with the DALP platform programmatically. The DALP platform exposes a complete REST API with an auto-generated OpenAPI 3.x specification. You can explore endpoints interactively, generate client SDKs for your language, or integrate with API testing tools. REST API documentation with full endpoint reference ## OpenAPI specification endpoint [#openapi-specification-endpoint] The OpenAPI spec is served at your platform's `/api/` path: Open API Explorer This endpoint returns a JSON document conforming to the [OpenAPI 3.x specification](https://spec.openapis.org/oas/latest.html). The spec includes: * **Endpoint definitions** – All available routes organized by namespace (token, system, user, etc.) * **Request schemas** – Parameter types, validation rules, and required fields * **Response schemas** – Return types for successful responses and error codes * **Authentication** – Security scheme using `X-Api-Key` header * **Examples** – Sample requests and responses for common operations ### Viewing the specification [#viewing-the-specification] **In your browser**: Visit `/api` on your platform (e.g., `https://your-platform.example.com/api`) to see the interactive API documentation powered by OpenAPI Reference Plugin. **With curl**: ```bash curl https://your-platform.example.com/openapi.json | jq ``` **In Postman/Insomnia**: Import the OpenAPI spec URL to auto-generate a collection with all endpoints configured. *** ## Authentication [#authentication] All API requests require authentication, either via an API key in the `X-Api-Key` header or a session cookie. When using session-based (cookie) authentication, sensitive operations that trigger blockchain transactions also require wallet verification. When using API key authentication, wallet verification is skipped automatically — the `walletVerification` field can be omitted. Use the optional `X-Wallet` header only when a request must run through a specific wallet controlled by the authenticated signer: * Omit `X-Wallet` to use the requester's default wallet routing. * Send `X-Wallet: eoa` to force the request through the requester's externally owned account. * Send `X-Wallet: 0x...` to select a specific wallet address. The address must be a valid Ethereum address. The authenticated signer must control that wallet, either directly or through an indexed smart wallet relationship. If DALP cannot validate the header value or the signer does not control the selected wallet, the API rejects the request before queueing the blockchain operation. Remove the header to fall back to automatic selection, use `eoa` when you need the direct signer account, or wait for a newly deployed smart wallet to index before retrying with its address. See [Getting started](/docs/developer-guides/api-integration/getting-started) for API key creation and client configuration. See [Smart wallet thresholds](/docs/developer-guides/api-integration/smart-wallet-thresholds) for multisig approval configuration. *** ## Response headers [#response-headers] ### Transaction hash header [#transaction-hash-header] Mutation operations that submit blockchain transactions return the transaction hash in the `X-Transaction-Hash` response header. The API automatically waits for transaction confirmation—you typically don't need this header. However, if you receive a `CONFIRMATION_TIMEOUT` error, use the hash to check whether the transaction eventually succeeded before retrying. See [Transaction tracking](/docs/developer-guides/operations/transaction-tracking) for timeout recovery patterns. *** ## API namespaces [#api-namespaces] The API is organized into logical namespaces, each containing related procedures: | Namespace | Description | Example operations | | ----------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------- | | **token** | Token CRUD, supply management, compliance, stats | `token.create`, `token.mint`, `token.burn`, `token.transfer`, `token.freezeAddress` | | **transaction** | Blockchain transaction status tracking | `transaction.read` | | **system** | Platform infrastructure (access control, identity, claims) | `system.accessManager.grantRole`, `system.identity.register`, `system.trustedIssuers.create` | | **user** | User profile and organization membership | `user.me`, `user.update` | | **account** | Blockchain wallet and identity management | `account.identity`, `account.claims` | | **actions** | Scheduled tasks and executable operations | `actions.list`, `actions.read` | | **addons** | Optional features (token sale, fixed yield) | `addons.tokenSale.create`, `addons.fixedYield.configure` | | **contacts** | Address book for frequent recipients | `contacts.list`, `contacts.create` | | **exchangeRates** | Foreign exchange rates for multi-currency assets | `exchangeRates.list`, `exchangeRates.convert` | | **search** | Global search across tokens, contacts, transactions | `search.query` | | **settings** | Platform configuration and preferences | `settings.get`, `settings.update` | | **externalToken** | Import and track tokens from other systems | `externalToken.register`, `externalToken.list` | | **auth** | Better Auth endpoints (sign-in, sessions, passkeys) | `/auth/sign-in`, `/auth/session`, `/auth/passkey/create` | Each namespace is prefixed in the API path. For example, `token.create` maps to `POST /api/token/create`. ### User statistics data sources [#user-statistics-data-sources] User statistics endpoints (`user.stats`, `user.statsUserCount`, `user.statsGrowthOverTime`) report counts based on organization membership data in the DALP database. Recent activity is derived from each member's most recent login timestamp, falling back to their created date when login history is unavailable. *** ## TypeScript SDK [#typescript-sdk] Generate a type-safe client from the OpenAPI specification using `@hey-api/openapi-ts`. The generated SDK provides full IntelliSense, request/response types, and BigDecimal helpers. See [Getting started](/docs/developer-guides/api-integration/getting-started#configure-the-openapi-client) for SDK generation and client configuration. *** ## Using with API tools [#using-with-api-tools] ### Swagger UI [#swagger-ui] Swagger UI provides an interactive API explorer: 1. Open `/api` in your browser (e.g., `https://your-platform.example.com/api`) 2. The OpenAPI Reference Plugin serves a styled documentation page 3. Expand endpoint groups to see request schemas and examples 4. Click "Try it out" to execute requests directly from the browser ### Redoc [#redoc] For a cleaner, read-only documentation view: 1. Download the OpenAPI spec: `curl https://your-platform.example.com/openapi.json > openapi.json` 2. Serve with Redoc: ```bash npx @redocly/cli preview-docs openapi.json ``` 3. Open `http://localhost:8080` in your browser ### Postman [#postman] Import the OpenAPI spec into Postman: 1. Click **Import** in Postman 2. Select **Link** tab 3. Paste: `https://your-platform.example.com/openapi.json` 4. Click **Continue** → **Import** 5. Configure the collection's authorization with your API key: * Auth Type: **API Key** * Key: `X-Api-Key` * Value: `sm_dalp_xxxxxxxxxxxxxxxx` * Add to: **Header** ### Insomnia [#insomnia] Import the OpenAPI spec into Insomnia: 1. Click **Create** → **Import** 2. Paste: `https://your-platform.example.com/openapi.json` 3. Click **Scan** → **Import** 4. Add an environment variable for `X-Api-Key` header ### curl examples [#curl-examples] **Fetch user profile**: ```bash curl -X POST https://your-platform.example.com/api/user/me \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{}' ``` **List tokens**: ```bash curl -X POST https://your-platform.example.com/api/token \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{"limit": 10, "offset": 0}' ``` **Create a token** (API key auth — no walletVerification needed): ```bash curl -X POST https://your-platform.example.com/api/token/create \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "type": "stablecoin", "name": "Test USD Coin", "symbol": "TUSD", "decimals": 6, "countryCode": "840", "priceCurrency": "USD", "basePrice": ["100", 2], "initialModulePairs": [] }' ``` When using session-based authentication, add the `walletVerification` field: ```bash curl -X POST https://your-platform.example.com/api/token/create \ -H "Cookie: session=..." \ -H "Content-Type: application/json" \ -d '{ "type": "stablecoin", "name": "Test USD Coin", "symbol": "TUSD", "decimals": 6, "countryCode": "840", "priceCurrency": "USD", "basePrice": ["100", 2], "initialModulePairs": [], "walletVerification": { "verificationType": "PINCODE", "secretVerificationCode": "123456" } }' ``` *** ## Generating client SDKs [#generating-client-sdks] Use the OpenAPI spec to generate clients for languages beyond TypeScript. ### Python (openapi-generator) [#python-openapi-generator] ```bash openapi-generator-cli generate \ -i https://your-platform.example.com/openapi.json \ -g python \ -o ./dalp-python-client ``` ### Go [#go] ```bash openapi-generator-cli generate \ -i https://your-platform.example.com/openapi.json \ -g go \ -o ./dalp-go-client ``` ### C# / .NET [#c--net] ```bash openapi-generator-cli generate \ -i https://your-platform.example.com/openapi.json \ -g csharp-netcore \ -o ./dalp-csharp-client ``` ### Java [#java] ```bash openapi-generator-cli generate \ -i https://your-platform.example.com/openapi.json \ -g java \ -o ./dalp-java-client ``` **Note**: Generated clients may require manual adjustments for BigInt/BigDecimal serialization. The TypeScript SDK includes `toBigDecimal()` and `fromBigDecimal()` helpers for this purpose. *** ## Error handling [#error-handling] The API returns standardized HTTP status codes and machine-readable error codes. Errors include detailed messages and contextual data to help diagnose issues. See [Error handling](/docs/developer-guides/api-integration/error-handling) for the complete error reference, retry strategies, and blockchain revert reasons. *** ## Next steps [#next-steps] * **[Getting started](/docs/developer-guides/api-integration/getting-started)** – Create an API key and configure the TypeScript client * **[Error handling](/docs/developer-guides/api-integration/error-handling)** – Handle errors and implement retry strategies * **[Transaction tracking](/docs/developer-guides/operations/transaction-tracking)** – Recover from confirmation timeouts * **[Token lifecycle](/docs/developer-guides/api-integration/token-lifecycle)** – Visual flowcharts for token operations # Asset decimals Source: https://docs.settlemint.com/docs/developer-guides/api-integration/asset-decimals Understand how asset amounts work with scaled integers and dnum format. Asset amounts in the API use **scaled integers**, not decimal numbers. This guide explains why and how to work with them correctly. ## Quick summary [#quick-summary] * Amounts are **integers scaled by 10^decimals** (no floating-point) * API accepts **plain strings** OR **dnum tuples** for input * API responses return **decimal strings**: `"1.000000"` (locale-independent, trailing zeros preserve precision) * Formula: `scaled_value = human_amount × 10^decimals` ## Why scaled integers? [#why-scaled-integers] Computers can't do reliable decimal math. Try this in any programming language: ``` 0.1 + 0.2 = 0.30000000000000004 ``` For financial calculations, this is unacceptable. The solution: **store everything as integers**. Think of it like storing money as cents instead of dollars: * `$10.50` becomes `1050` cents * No decimal point, no rounding errors Blockchains use the same approach. The `decimals` field tells you how to convert: * **0 decimals** = whole units (like shares) * **2 decimals** = cents (like USD) * **18 decimals** = standard for most tokens ## The dnum format [#the-dnum-format] The platform uses **dnum** (decimal number) tuples to represent amounts: ```json [scaled_value, decimals] ``` Examples: * 100 shares (0 decimals): `["100", 0]` * $10.50 (2 decimals): `["1050", 2]` * 1.5 tokens (18 decimals): `["1500000000000000000", 18]` The first element is a **BigInt** in code. Over JSON/HTTP, it serializes as a string to preserve precision. ## Sending API requests [#sending-api-requests] The API accepts two formats for amounts: ### Option 1: Plain string (simpler) [#option-1-plain-string-simpler] Just send the scaled value as a string: ```json { "amounts": "1500000000000000000" } ``` ### Option 2: Dnum tuple (recommended) [#option-2-dnum-tuple-recommended] Pairs the value with decimals—matches the response format: ```json { "amounts": ["1500000000000000000", 18] } ``` ### Batch operations [#batch-operations] For multiple amounts, use arrays: ```json { "amounts": [ ["1500000000000000000", 18], ["500000000000000000", 18] ] } ``` ## Reading API responses [#reading-api-responses] Responses return locale-independent decimal strings. Trailing zeros encode the precision so the round-trip is lossless: ```json { "totalSupply": "1000.000000000000000000", "basePrice": "100.00" } ``` To convert back to human-readable, parse the decimal string directly — no division required: ``` "1000.000000000000000000" → 1000 tokens (18 decimal places of precision) "100.00" → $100.00 (2 decimal places) ``` ### Legacy format (backward compatibility) [#legacy-format-backward-compatibility] Older API versions returned dnum tuples. Both the SDK and direct callers handle this transparently: ```json { "totalSupply": ["1000000000000000000000", 18] } ``` If you interact with a mix of old and new API versions during a rolling deployment, the deserializer accepts both formats automatically. ## Quick reference [#quick-reference] | You want to send | Decimals | Plain string | Dnum tuple | | ---------------- | -------- | ----------------------- | ----------------------------- | | 100 shares | 0 | `"100"` | `["100", 0]` | | 10.50 dollars | 2 | `"1050"` | `["1050", 2]` | | 1.5 tokens | 18 | `"1500000000000000000"` | `["1500000000000000000", 18]` | | 0.001 tokens | 18 | `"1000000000000000"` | `["1000000000000000", 18]` | ## Converting amounts [#converting-amounts] ### Human-readable to API format [#human-readable-to-api-format] To convert "1.5" with 18 decimals: ``` 1. Split at decimal point: whole = "1", fraction = "5" 2. Pad fraction to 18 chars: "500000000000000000" 3. Concatenate: "1" + "500000000000000000" = "1500000000000000000" 4. Result: ["1500000000000000000", 18] ``` ### API response to human-readable [#api-response-to-human-readable] Responses now return a locale-independent decimal string like `"1.500000000000000000"`. Parse the decimal point directly — no division required. The number of digits after the decimal point tells you the precision. If you receive a legacy tuple `["1500000000000000000", 18]` (from an older server during a rolling deployment): ``` 1. Divide: 1500000000000000000 ÷ 10^18 = 1 (whole part) 2. Remainder: 1500000000000000000 % 10^18 = 500000000000000000 3. Format: "1.500000000000000000" ``` Never use floating-point for these calculations. Use your language's arbitrary precision integer library (BigInt, BigInteger, bcmath, etc.) to avoid precision loss. ## Checking asset decimals [#checking-asset-decimals] Asset detail showing contract-level decimal configuration Always check the asset's decimals before sending amounts: ```bash curl -X GET "https://your-platform.example.com/api/token/0x9459D52E60edBD3178f00F9055f6C117a21b4220" \ -H "X-Api-Key: YOUR_API_KEY" ``` Response: ```json { "name": "ACME Holdings Common Stock", "symbol": "ACME", "decimals": 0, "totalSupply": "1000000" } ``` ## Common mistakes [#common-mistakes] | Mistake | Problem | Fix | | ---------------------------- | --------------------------------------- | ----------------------------- | | `"amounts": "1.5"` | Sends 15 smallest units, not 1.5 tokens | Multiply by 10^decimals first | | Using `float`/`double` | Precision loss on large numbers | Use BigInt/BigInteger | | `"1.5e18"` | Scientific notation may not parse | Use full integer string | | Forgetting to check decimals | Wrong scale for the asset | Always GET the asset first | ## Full example [#full-example] ```bash # Step 1: Get asset info (including decimals) curl -X GET "https://your-platform.example.com/api/token/0x1234..." \ -H "X-Api-Key: YOUR_API_KEY" # Response shows decimals: 18 # Step 2: Calculate amount for 1.5 tokens # 1.5 × 10^18 = 1500000000000000000 # Step 3: Mint using dnum tuple (recommended) curl -X POST "https://your-platform.example.com/api/token/0x1234.../mint" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "recipients": ["0xABCD..."], "amounts": ["1500000000000000000", 18], "walletVerification": { "verificationType": "PINCODE", "secretVerificationCode": "123456" } }' ``` ## Related guides [#related-guides] * [Mint Assets](/docs/developer-guides/asset-servicing/mint-assets) - Issue assets to investors * [Forced Transfer](/docs/developer-guides/asset-servicing/forced-transfer) - Administrative transfers * [API Reference](/docs/developer-guides/api-integration/api-reference) - Full OpenAPI specification # Compliance templates Source: https://docs.settlemint.com/docs/developer-guides/api-integration/compliance-templates Create, list, update, publish, and delete reusable compliance templates through the DALP API. Compliance template endpoints let integrations manage reusable compliance policy patterns for asset creation. A template stores compliance modules, jurisdictions, required controls, draft or published status, and the compliance module generation the template belongs to. Use these endpoints when an integration needs to prepare policy templates before operators create assets in the Asset Designer. For the operator workflow, see [Compliance templates](/docs/user-guides/compliance/templates). ## Endpoints [#endpoints] The compliance template API exposes these endpoints: | Endpoint | Use it for | | -------------------------------------------------------- | ----------------------------------------------- | | `GET /api/v2/settings/compliance-templates` | List compliance templates in the active tenant. | | `POST /api/v2/settings/compliance-templates` | Create a draft compliance template. | | `GET /api/v2/settings/compliance-templates/{id}` | Read one compliance template. | | `PUT /api/v2/settings/compliance-templates/{id}` | Update a compliance template. | | `PUT /api/v2/settings/compliance-templates/{id}/publish` | Publish a draft template for asset creation. | | `DELETE /api/v2/settings/compliance-templates/{id}` | Delete a compliance template. | Responses use the DALP single-resource or collection envelope with `data` and `links.self`. ## Create a draft template [#create-a-draft-template] Create requests start a template in draft state. If you omit `moduleSetVersion`, DALP uses the current compliance module generation. Older clients may still send the deprecated `legacy` boolean; new integrations should send `moduleSetVersion` instead. ```bash curl --request POST \ "$DALP_API_URL/api/v2/settings/compliance-templates" \ --header "X-Api-Key: $DALP_API_TOKEN" \ --header "Content-Type: application/json" \ --data '{ "name": "Global capital raise policy", "description": "Reusable capital raise controls for regulated assets", "jurisdictions": [], "moduleSetVersion": 2, "modules": [], "requiredControls": ["capital-raise-limit"] }' ``` The response includes the created template and a `links.self` path for the new resource. ## Update template configuration [#update-template-configuration] Update requests can change the name, description, jurisdictions, modules, and required controls. They cannot change the template's module generation. If an integration needs a different generation, create a new template with the target `moduleSetVersion` and move the required modules or controls there. DALP validates modules and required controls against the template generation on create, update, and publish. A current-generation template cannot save controls that only belong to a legacy module set. If the API returns a module-set compatibility error, remove the incompatible controls or create a template with the matching generation. ## Publish a template [#publish-a-template] Publish the template when it is ready to appear in asset creation workflows: ```bash curl --request PUT \ "$DALP_API_URL/api/v2/settings/compliance-templates/$TEMPLATE_ID/publish" \ --header "X-Api-Key: $DALP_API_TOKEN" ``` Publishing changes `isDraft` to `false`. Published templates can be selected during asset creation. Draft templates remain editable preparation records. ## List and filter templates [#list-and-filter-templates] Use the list endpoint to find templates by search, draft status, system status, jurisdiction, or module generation. For example, request only current-generation draft templates when your integration is preparing a new policy set: ```bash curl --globoff \ "$DALP_API_URL/api/v2/settings/compliance-templates?filter[moduleSetVersion]=2&filter[isDraft]=true" \ --header "X-Api-Key: $DALP_API_TOKEN" ``` Use the template returned by the API as the source of truth before you update it. Another operator or integration may have changed the template version or draft status since your last read. ## Related [#related] * [Compliance modules](/docs/architecture/security/compliance) * [Compliance templates user guide](/docs/user-guides/compliance/templates) * [Asset creation with instrument templates](/docs/user-guides/asset-creation/instrument-templates) * [DAPI error reference](/docs/developer-guides/api-integration/dapi-error-reference) # DAPI Error Reference Source: https://docs.settlemint.com/docs/developer-guides/api-integration/dapi-error-reference Complete reference of all DAPI error codes returned by the DALP API, with HTTP status, retryability, and remediation guidance. {/* ENTRY_COUNT:519 */} {/* SURFACE_COUNT:9 */} # DAPI Error Reference [#dapi-error-reference] This page is generated from the unified DAPI error registry. ## Errors [#errors] | ID | Category | HTTP status | Retryable | Why | Fix | | ----------- | ----------- | ----------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `DALP-0001` | client | 400 | no | The request is malformed or cannot be understood by the API. | Check the request syntax, parameters, and content type before retrying. | | `DALP-0002` | auth | 401 | no | The request did not include valid authentication credentials. | Authenticate again or send a valid API key/session credential. | | `DALP-0003` | permission | 403 | no | The authenticated actor does not have permission to perform this action. | Use an actor with the required role or ask an administrator to grant access. | | `DALP-0004` | auth | 403 | no | The authenticated user has not completed onboarding required for this operation. | Complete onboarding and retry the request. | | `DALP-0005` | auth | 403 | no | The operation requires wallet-signing protection that is not configured for this user. | Set up PIN or two-factor authentication and retry. | | `DALP-0006` | permission | 403 | no | The actor lacks at least one role required by the token or system contract. | Grant the required role or retry with an authorized actor. | | `DALP-0007` | permission | 403 | no | The encrypted XvP settlement secret could not be decrypted with the caller's current wallet signature. | Retry with the same wallet credentials that were used when the settlement secret was created. | | `DALP-0008` | operational | 500 | no | The API could not determine the effective wallet for this request after checking the session, organization context, and X-Wallet header. | Retry with a valid active organization and wallet selection. If the problem continues, contact support with the request id. | | `DALP-0009` | auth | 500 | no | The API could not resolve the authenticated session from the request headers. | Retry with a fresh session or API key. If the problem continues, contact support with the request id. | | `DALP-0010` | dependency | 503 | yes | The API could not build the system context from indexed system registry data. | Retry after the indexer catches up. If the problem continues, contact support with the request id. | | `DALP-0011` | dependency | 503 | yes | The API could not complete a transaction queue operation outside the known contract-error and Restate-error mappings. | Retry after a short backoff. If the problem continues, contact support with the request id. | | `DALP-0012` | dependency | 503 | yes | The configured chain RPC provider could not read transaction state for this request. | Retry after a short backoff. If the problem continues, contact support with the request id. | | `DALP-0013` | dependency | 503 | yes | The API could not preview, sign, or submit the smart-wallet approval workflow after reserving the account-abstraction nonce. | Retry after a short backoff. The nonce reservation is released best-effort and stale reservations expire automatically. | | `DALP-0014` | dependency | 503 | yes | The API could not query the Restate admin plane to determine whether an organization deployment workflow is active. | Retry after a short backoff. If deployment state still cannot be checked, contact support with the request id. | | `DALP-0015` | operational | 503 | yes | The system deployment workflow reached a failed state before producing a usable system address. | Inspect the deployment status and retry after correcting the workflow failure. Include the request id when contacting support. | | `DALP-0016` | operational | 504 | yes | The deployment workflow stayed active but did not publish the system address within the synchronous wait window. | Poll the deployment status or retry with an asynchronous preference instead of waiting for the address in the same request. | | `DALP-0017` | dependency | 503 | yes | The API could not resolve a system factory contract address from the request or directory service before starting deployment. | Retry after the system directory has been configured, or pass an explicit system factory contract address. | | `DALP-0018` | operational | 503 | no | The signer rotation transaction was confirmed, but DAPI could not promote the new signer secret into the secrets provider. | Do not retry blindly. Check the pending signer secret state and rollback status, then recover or rerun the rotation with operator oversight. | | `DALP-0019` | dependency | 503 | yes | An indexer aggregate count could not be converted into a non-negative JavaScript safe integer for the API response. | Retry after the indexer data has been corrected or reindexed. If it continues, contact support with the request id and affected stats endpoint. | | `DALP-0020` | dependency | 503 | yes | The deployment event stream could not read the latest workflow status from Restate. | Reconnect to the stream or poll deployment status after a short backoff. If it continues, contact support with the request id. | | `DALP-0021` | operational | 503 | no | The deployment workflow ended with a Restate terminal error before the stream could read a typed failed workflow tree. | Open the deployment details or retry only after correcting the workflow failure. Include the request id when contacting support. | | `DALP-0022` | client | 404 | no | The requested resource could not be found. | Check the identifier and retry only if the resource should exist. | | `DALP-0023` | client | 409 | no | The request tried to create or register a resource that already exists. | Use the existing resource or choose a unique identifier. | | `DALP-0024` | client | 409 | no | The requested change conflicts with the current resource state. | Refresh the resource state, resolve the conflict, and retry. | | `DALP-0025` | domain | 422 | no | The target token does not expose the interface required for this operation. | Use a compatible token contract or enable the required interface before retrying. | | `DALP-0026` | domain | 403 | no | The DALP system bootstrap has not completed for this environment or organization. | Create or finish deploying the system before calling this operation. | | `DALP-0027` | domain | 501 | no | The requested capability depends on an addon that is not available in this system. | Install or deploy the required addon before retrying the operation. | | `DALP-0028` | domain | 422 | no | The operation targets a token feature that is not attached or enabled. | Enable the required token feature or call an operation supported by this token. | | `DALP-0029` | domain | 403 | no | The authenticated user's email does not match the invitation recipient. | Sign in with the invited email address or ask an admin to send a new invitation. | | `DALP-0030` | domain | 409 | no | The invitation token has already been used successfully. | Continue to the dashboard or ask an admin for a new invitation if access is still missing. | | `DALP-0031` | operational | 504 | yes | The transaction was submitted but confirmation did not arrive before the timeout. | Check transaction status using the transaction hash and retry only if it was not confirmed. | | `DALP-0032` | permission | 403 | no | The operation requires elevated permissions and the authenticated actor does not hold the role required for this route. | Retry with an account that has the role required by this route, or check the route documentation for the required role. | | `DALP-0033` | operational | 500 | no | The route or middleware ran without databaseMiddleware even though it needs database access to validate permissions or load resources. | Contact support with the request id; this indicates a server middleware ordering issue. | | `DALP-0034` | permission | 403 | no | The request requires organization-scoped permissions, but the session does not have an active organization selected. | Select an organization, refresh the session, and retry the request. | | `DALP-0035` | permission | 403 | no | The request uses a read-only API key on a method that can mutate state. | Use a read-write API key or send the request with a safe read method. | | `DALP-0036` | auth | 403 | no | The request used API key authentication on an endpoint that only accepts session authentication. | Call the REST endpoint with the API key, or authenticate with a supported session for RPC. | | `DALP-0037` | operational | 500 | no | A token-scoped route ran before token middleware attached the indexed token context. | Contact support with the request id; this indicates a server middleware ordering issue. | | `DALP-0038` | operational | 500 | no | The route requires token permissions, but token middleware did not attach user permission state. | Contact support with the request id; this indicates a server middleware ordering issue or missing indexed permission data. | | `DALP-0039` | dependency | 404 | yes | The system exists or is being created, but indexed registry children required by this operation are not available yet. | Retry after the indexer catches up. If the system was never deployed, deploy it first. | | `DALP-0040` | client | 404 | no | The operation requires an indexed system deployment, but none is available for the active organization. | Deploy a system for the organization, wait for indexing, and retry. | | `DALP-0041` | dependency | 500 | yes | The system was indexed without the access-control state required to evaluate the caller's roles. | Retry after indexing catches up. If the problem continues, contact support with the request id. | | `DALP-0042` | dependency | 500 | yes | The route could not load indexed trusted-issuer context for the active system. | Retry after indexing catches up. If the problem continues, contact support with the request id. | | `DALP-0043` | permission | 403 | no | The user's issuer identity is not trusted for the claim topic being mutated. | Use a trusted issuer for the topic, or ask a system manager to trust this issuer for the topic. | | `DALP-0044` | permission | 403 | no | The operation requires a registered issuer identity for the authenticated user. | Register an issuer identity for the user, then retry. | | `DALP-0045` | dependency | 503 | yes | The API could not contact the indexer service that starts blockchain reindex jobs. | Retry after a short backoff. If the problem continues, contact support with the request id. | | `DALP-0046` | client | 400 | no | The requested blockchain reindex range, target, or mode was rejected by the reindex service. | Adjust the reindex request to a supported chain, range, and mode before retrying. | | `DALP-0047` | dependency | 503 | yes | The indexer service accepted the request path but reported that reindexing cannot be started now. | Retry after a short backoff. If the problem continues, contact support with the request id. | | `DALP-0048` | client | 404 | no | No API monitoring log entry exists for the requested id in the active organization. | Verify the log entry id and organization, then retry. | | `DALP-0049` | client | 400 | no | The request type filter does not match a request type known to API monitoring. | Use one of the documented request type values, or omit the filter. | | `DALP-0050` | permission | 403 | no | The X-Wallet header selected a wallet that is neither the authenticated EOA nor an indexed smart wallet controlled by that EOA. | Use a wallet controlled by the authenticated signer, wait for newly created smart wallets to index, or remove the X-Wallet header. | | `DALP-0051` | client | 404 | no | No invitation matching the requested id exists for the caller. | Check the invitation link or ask the organization to issue a new invitation. | | `DALP-0052` | client | 403 | no | The invitation exists but its expiry time has passed. | Ask the organization to send a fresh invitation. | | `DALP-0053` | client | 403 | no | The invitation was explicitly revoked or is no longer in an accepted state for this flow. | Ask the organization to send a fresh invitation. | | `DALP-0054` | client | 404 | no | The requested deployment id is malformed, stale, or does not belong to a deployment the caller may observe. | Refresh deployment state and subscribe using the current deployment id. | | `DALP-0055` | permission | 403 | no | Retrying deployment mutates organization settings and on-chain system state, so member-level access is not sufficient. | Retry with an organization owner or platform administrator account. | | `DALP-0056` | dependency | 503 | yes | The API could not safely inspect or purge the previous Restate workflow before submitting a retry. | Retry after a short backoff. If the problem continues, contact support with the request id. | | `DALP-0057` | permission | 403 | no | The deployment is creating a new organization while the environment restricts organization creation to platform admins. | Ask a platform admin to create the organization or disable the restriction for this environment. | | `DALP-0058` | dependency | 503 | yes | The system directory did not return the factory address required to start deployment. | Retry after directory configuration has propagated. If the problem continues, contact support with the request id. | | `DALP-0059` | client | 404 | no | The settlement exists but the encrypted secret payload is missing from XvP secret storage. | Verify the settlement address and ensure the secret was created before decrypting. | | `DALP-0060` | client | 400 | no | The stored secret payload was encrypted with a method this DAPI version does not support. | Recreate the settlement secret with the supported encryption method. | | `DALP-0061` | client | 400 | no | A cross-chain XvP settlement was created with a hashlock that is not valid hex. | Send a 0x-prefixed hashlock or provide the secret so the API can derive it. | | `DALP-0062` | client | 400 | no | The settlement includes an external flow, so a secret or precomputed hashlock is required to coordinate settlement. | Provide a settlement secret or a valid hashlock in the request. | | `DALP-0063` | client | 404 | yes | The requested factory address is not indexed as an XvP settlement addon for the authenticated system. | Use an installed XvP factory address or retry after addon indexing catches up. | | `DALP-0064` | client | 400 | no | The selected XvP factory version requires an ISO 3166-1 numeric country code. | Include a valid country code in the XvP creation request. | | `DALP-0065` | client | 404 | yes | The authenticated system does not have an indexed XvP settlement addon. | Install the XvP settlement addon or retry after indexing catches up. | | `DALP-0066` | client | 404 | no | The request references a system addon address that is not part of the caller's active system. | Use an addon address from the authenticated system. | | `DALP-0067` | client | 400 | no | The XvP list request needs a participant wallet filter and the authenticated user does not have a wallet to use as the default. | Provide a participant wallet filter or complete wallet onboarding. | | `DALP-0068` | client | 404 | yes | The requested settlement address is not indexed for the authenticated system. | Verify the settlement address or retry after indexing catches up. | | `DALP-0069` | permission | 403 | no | The caller is not the local sender recorded for the XvP settlement flow. | Approve from the wallet that is the local sender for this settlement. | | `DALP-0070` | client | 404 | no | The caller has not approved the requested XvP settlement or the approval is not indexed. | Approve the settlement first or retry after indexing catches up. | | `DALP-0071` | client | 400 | no | The XvP signature payload is not a 0x-prefixed hexadecimal value. | Sign the settlement message again and submit the hex-encoded signature. | | `DALP-0072` | permission | 403 | no | The authenticated user has no wallet id available for signing the XvP settlement message. | Complete wallet onboarding, refresh the session, and retry. | | `DALP-0073` | dependency | 503 | yes | The wallet signing service failed while signing the XvP settlement message. | Retry after a short backoff. If signing continues to fail, contact support with the request id. | | `DALP-0074` | client | 404 | yes | The requested fixed-yield schedule is not indexed for the authenticated system or addon. | Verify the schedule address or retry after indexing catches up. | | `DALP-0075` | dependency | 503 | yes | The fixed-yield schedule references a denomination asset whose metadata is not available in the indexer. | Retry after indexing catches up. | | `DALP-0076` | dependency | 503 | yes | The fixed-yield operation needs the denomination asset row, but the indexer has not exposed it yet. | Retry after indexing catches up. | | `DALP-0077` | permission | 403 | no | The requested fixed-yield schedule is associated with a different system than the caller's active system. | Use a schedule address from the authenticated system. | | `DALP-0078` | client | 404 | yes | The authenticated system does not have the fixed-yield addon indexed. | Install the addon or retry after indexing catches up. | | `DALP-0079` | dependency | 503 | yes | The fixed-yield deployment transaction completed but the created schedule is not visible in the indexer yet. | Retry after indexing catches up. | | `DALP-0080` | client | 422 | no | The request body or parameters did not match the API contract. | Check the request fields against the API documentation and retry with valid values. | | `DALP-0081` | contract | 422 | no | The smart contract rejected the operation with a known DALP error. | Use the DALP code and suggested action in the response to correct the request or token state. | | `DALP-0082` | dependency | 503 | yes | A required backend service is temporarily unavailable. | Retry after a short backoff. If the problem continues, contact support with the request id. | | `DALP-0083` | dependency | 503 | yes | The API is reading from an indexer schema that is still rolling out or reindexing. | Retry after the indexer rollout completes. If the problem continues, contact support with the request id. | | `DALP-0084` | unknown | 500 | no | The API could not complete the request because an unexpected server error occurred. | Retry later or contact support with the request id if the problem continues. | | `DALP-0085` | client | 400 | no | The JSON-RPC request is missing required fields or uses an unsupported shape. | Send a valid JSON-RPC 2.0 request with the required method, id, and params fields. | | `DALP-0086` | operational | 500 | yes | The API could not continue the event stream safely. | Reconnect to the stream. If failures continue, contact support with the request id. | | `DALP-0087` | permission | 404 | no | The resource does not exist or is not available to the current actor. | Check that the identifier is correct and that the actor has access to this resource. | | `DALP-0088` | client | 404 | no | DAPI could not find the asset class definition in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0089` | client | 404 | no | DAPI could not find the asset class definition not found definitions delete in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0090` | client | 404 | no | DAPI could not find the asset class definition not found definitions update in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0091` | client | 404 | no | DAPI could not find the asset type template in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0092` | dependency | 503 | yes | The route reached a database query helper that was not registered on the DAPI database context. | Contact support with the request id; this indicates the route is missing the schema or middleware required for this query. | | `DALP-0093` | dependency | 503 | yes | The route reached a database query helper that was not registered on the DAPI database context. | Contact support with the request id; this indicates the route is missing the schema or middleware required for this query. | | `DALP-0094` | dependency | 503 | yes | The route reached a database query helper that was not registered on the DAPI database context. | Contact support with the request id; this indicates the route is missing the schema or middleware required for this query. | | `DALP-0095` | client | 404 | no | DAPI could not find the compliance template in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0096` | client | 404 | no | DAPI could not find the contact in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0097` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0098` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0099` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0100` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0101` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0102` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0103` | client | 404 | no | DAPI could not find the core claim no active found topic identity in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0104` | client | 404 | no | DAPI could not find the core claim no enabled topic scheme found registry in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0105` | client | 404 | no | DAPI could not find the core claims wallet id not found user must issue session includes walletid in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0106` | client | 404 | no | DAPI could not find the core claims wallet id not found user must revoke session includes walletid in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0107` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0108` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0109` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0110` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0111` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0112` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0113` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0114` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0115` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0116` | client | 404 | no | DAPI could not find the exchange rate in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0117` | client | 404 | no | DAPI could not find the exchange rates base currency in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0118` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0119` | client | 404 | no | DAPI could not find the exchange rates no manual rate found in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0120` | client | 404 | no | DAPI could not find the exchange rates quote currency in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0121` | client | 404 | no | DAPI could not find the external token registry in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0122` | client | 404 | no | DAPI could not find the external token registry not found externaltokenregistry deployed registered in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0123` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0124` | client | 404 | no | DAPI could not find the feeds adapters adaptercreated event not found transaction adapter created but address coul in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0125` | client | 404 | no | DAPI could not find the feeds feed address in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0126` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0127` | client | 404 | no | DAPI could not find the feeds get feedsdirectory not found bootstrapped v3 indexer caught up in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0128` | client | 404 | no | DAPI could not find the feeds issuer feedcreated event not found transaction feed created but address could not ex in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0129` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0130` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0131` | client | 404 | no | DAPI could not find the feeds round in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0132` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0133` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0134` | client | 404 | no | DAPI could not find the identity recovery factory address not found user s indicates data integrity issue indexer in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0135` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0136` | client | 404 | no | DAPI could not find the identity recovery no active workflow found user in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0137` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0138` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0139` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0140` | client | 404 | no | DAPI could not find the identity recovery user not found id in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0141` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0142` | dependency | 503 | yes | The route reached a database query helper that was not registered on the DAPI database context. | Contact support with the request id; this indicates the route is missing the schema or middleware required for this query. | | `DALP-0143` | dependency | 503 | yes | The route reached a database query helper that was not registered on the DAPI database context. | Contact support with the request id; this indicates the route is missing the schema or middleware required for this query. | | `DALP-0144` | dependency | 503 | yes | The route reached a database query helper that was not registered on the DAPI database context. | Contact support with the request id; this indicates the route is missing the schema or middleware required for this query. | | `DALP-0145` | dependency | 503 | yes | The route reached a database query helper that was not registered on the DAPI database context. | Contact support with the request id; this indicates the route is missing the schema or middleware required for this query. | | `DALP-0146` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0147` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0148` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0149` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0150` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0151` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0152` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0153` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0154` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0155` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0156` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0157` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0158` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0159` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0160` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0161` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0162` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0163` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0164` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0165` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0166` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0167` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0168` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0169` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0170` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0171` | client | 404 | no | DAPI could not find the settings global organization in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0172` | client | 404 | no | DAPI could not find the settings setting in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0173` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0174` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0175` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0176` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0177` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0178` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0179` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0180` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0181` | client | 404 | no | DAPI could not find the smart wallets approval not found user op hash in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0182` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0183` | client | 400 | no | The smart-wallet approval request included a user operation hash that is not valid hex, so DAPI cannot safely look up or sign the approval. | Send the exact 0x-prefixed userOpHash returned by the smart-wallet preview or approval creation response. | | `DALP-0184` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0185` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0186` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0187` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0188` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0189` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0190` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0191` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0192` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0193` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0194` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0195` | permission | 403 | no | The authenticated wallet is not registered as a signer on the selected multisig validator. | Select a wallet that is a registered signer, or ask a wallet owner to add your signer before retrying. | | `DALP-0196` | permission | 403 | no | The authenticated wallet is not registered as a signer on the selected multisig validator, so it cannot initiate an approval request. | Switch to a registered signer wallet, or ask a wallet owner to add your signer before creating the approval. | | `DALP-0197` | permission | 403 | no | The authenticated wallet is not registered as a signer for the selected smart wallet. | Use a registered signer wallet, or ask a wallet owner to add your signer before retrying. | | `DALP-0198` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0199` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0200` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0201` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0202` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0203` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0204` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0205` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0206` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0207` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0208` | client | 404 | no | DAPI could not find the smart wallets wallet not found address indexed in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0209` | client | 404 | no | DAPI could not find the smart wallets wallet not found address indexed create approval in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0210` | client | 404 | no | DAPI could not find the smart wallets wallet not found address indexed gas status in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0211` | client | 404 | no | DAPI could not find the smart wallets wallet not found address indexed wallets read in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0212` | client | 404 | no | DAPI could not find the smart wallets wallet not found address not indexed yet in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0213` | client | 404 | no | DAPI could not find the smart wallets wallet not found indexing indexer not processed accountcreated event yet in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0214` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0215` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0216` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0217` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0218` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0219` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0220` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0221` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0222` | dependency | 503 | yes | The route ran without middleware context that is required before this branch can execute. | Contact support with the request id; this indicates middleware order or route composition is incorrect. | | `DALP-0223` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0224` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0225` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0226` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0227` | client | 404 | no | DAPI could not find the system addon factory not found indexer in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0228` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0229` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0230` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0231` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0232` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0233` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0234` | client | 404 | no | DAPI could not find the system addon registry in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0235` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0236` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0237` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0238` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0239` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0240` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0241` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0242` | client | 404 | no | DAPI could not find the system claim topic in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0243` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0244` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0245` | client | 404 | no | DAPI could not find the system compliance contract in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0246` | client | 404 | no | DAPI could not find the system compliance module implementations not found indexer in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0247` | client | 404 | no | DAPI could not find the system compliance module not registered registry in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0248` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0249` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0250` | client | 404 | no | DAPI could not find the system factory address not found directory in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0251` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0252` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0253` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0254` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0255` | operational | 500 | no | The route ran without middleware context that is required before this branch can execute. | Contact support with the request id; this indicates middleware order or route composition is incorrect. | | `DALP-0256` | dependency | 503 | yes | The route ran without middleware context that is required before this branch can execute. | Contact support with the request id; this indicates middleware order or route composition is incorrect. | | `DALP-0257` | operational | 500 | no | The route ran without middleware context that is required before this branch can execute. | Contact support with the request id; this indicates middleware order or route composition is incorrect. | | `DALP-0258` | dependency | 503 | yes | The route ran without middleware context that is required before this branch can execute. | Contact support with the request id; this indicates middleware order or route composition is incorrect. | | `DALP-0259` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0260` | client | 404 | no | DAPI could not find the system identity factory address in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0261` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0262` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0263` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0264` | client | 404 | no | DAPI could not find the system identity no contract found wallet in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0265` | client | 404 | no | DAPI could not find the system identity no found in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0266` | client | 404 | no | DAPI could not find the system identity in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0267` | client | 404 | no | DAPI could not find the system identity not found creation indexer did not process block within timeout in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0268` | client | 404 | no | DAPI could not find the system identity not found registry in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0269` | client | 404 | no | DAPI could not find the system identity not registered s registry in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0270` | client | 404 | no | DAPI could not find the system identity not registered yet in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0271` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0272` | client | 404 | no | DAPI could not find the system identity topic not registered scheme registry in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0273` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0274` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0275` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0276` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0277` | client | 404 | no | DAPI could not find the system identity wallet not registered in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0278` | client | 404 | no | DAPI could not find the system no found organization in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0279` | client | 404 | no | DAPI could not find the system in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0280` | dependency | 503 | yes | The route ran without middleware context that is required before this branch can execute. | Contact support with the request id; this indicates middleware order or route composition is incorrect. | | `DALP-0281` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0282` | client | 404 | no | DAPI could not find the system paymaster entrypoint not found indexer not processed directory s in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0283` | client | 404 | no | DAPI could not find the system paymaster not found address indexed list available paymasters via get in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0284` | client | 404 | no | DAPI could not find the system paymaster not found address indexed list available paymasters via get v2 in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0285` | client | 404 | no | DAPI could not find the system paymaster sponsorship not found address in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0286` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0287` | dependency | 503 | yes | The route ran without middleware context that is required before this branch can execute. | Contact support with the request id; this indicates middleware order or route composition is incorrect. | | `DALP-0288` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0289` | client | 404 | no | DAPI could not find the system token factory type in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0290` | client | 404 | no | DAPI could not find the system token no factory found address bootstrapped in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0291` | client | 404 | no | DAPI could not find the system token no factory found contract type not recognized in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0292` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0293` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0294` | client | 404 | no | DAPI could not find the system trusted issuer in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0295` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0296` | client | 404 | no | DAPI could not find the token access control not found manager configured in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0297` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0298` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0299` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0300` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0301` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0302` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0303` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0304` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0305` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0306` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0307` | operational | 500 | no | The route ran without middleware context that is required before this branch can execute. | Contact support with the request id; this indicates middleware order or route composition is incorrect. | | `DALP-0308` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0309` | client | 404 | no | DAPI could not find the token claim topic not registered scheme registry in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0310` | client | 404 | no | DAPI could not find the token compliance no binding found module type contract in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0311` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0312` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0313` | operational | 500 | no | The route ran without middleware context that is required before this branch can execute. | Contact support with the request id; this indicates middleware order or route composition is incorrect. | | `DALP-0314` | client | 404 | no | DAPI could not find the token create factory type in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0315` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0316` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0317` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0318` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0319` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0320` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0321` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0322` | client | 404 | no | DAPI could not find the token documents document group not found replacement in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0323` | client | 404 | no | DAPI could not find the token documents document in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0324` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0325` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0326` | client | 404 | no | DAPI could not find the token documents file not found storage upload failed in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0327` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0328` | client | 404 | no | DAPI could not find the token factory in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0329` | operational | 500 | no | The route ran without middleware context that is required before this branch can execute. | Contact support with the request id; this indicates middleware order or route composition is incorrect. | | `DALP-0330` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0331` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0332` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0333` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0334` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0335` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0336` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0337` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0338` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0339` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0340` | client | 404 | no | DAPI could not find the token indexer token in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0341` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0342` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0343` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0344` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0345` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0346` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0347` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0348` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0349` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0350` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0351` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0352` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0353` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0354` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0355` | client | 404 | no | DAPI could not find the token sale addon in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0356` | client | 404 | no | DAPI could not find the token sale addon not found addons in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0357` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0358` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0359` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0360` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0361` | client | 404 | no | DAPI could not find the token sale no registered identity found must create in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0362` | client | 404 | no | DAPI could not find the token sale not found indexer in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0363` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0364` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0365` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0366` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0367` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0368` | client | 404 | no | DAPI could not find the token transfer fromwallet not registered identity registry wallet must onboarded can in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0369` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0370` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0371` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0372` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0373` | permission | 403 | no | The authenticated actor does not satisfy the ownership, signer, role, or organization permission required by this branch. | Retry with an authorized actor, select the correct organization or wallet, or ask an administrator to grant the required permission. | | `DALP-0374` | client | 404 | no | DAPI could not find the token transfer not found address deployed indexed in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0375` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0376` | client | 404 | no | DAPI could not find the token transfer towallet not registered identity registry wallet must onboarded can in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0377` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0378` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0379` | client | 404 | no | DAPI could not find the token transfer wallet not registered identity registry must onboarded approving transfers in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0380` | client | 404 | no | DAPI could not find the token transfer wallet not registered identity registry must onboarded revoking approvals in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0381` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0382` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0383` | client | 404 | no | DAPI could not find the transaction in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0384` | client | 404 | no | DAPI could not find the transaction not found not chain no stored queue record hash in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0385` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0386` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0387` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0388` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0389` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0390` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0391` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0392` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0393` | client | 404 | no | DAPI could not find the user kyc document in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0394` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0395` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0396` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0397` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0398` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0399` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0400` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0401` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0402` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0403` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0404` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0405` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0406` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0407` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0408` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0409` | client | 404 | no | DAPI could not find the user kyc file not found storage upload failed in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0410` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0411` | client | 404 | no | DAPI could not find the user kyc no data found profile but version history not completed verification yet in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0412` | client | 404 | no | DAPI could not find the user kyc no profile found delete already removed in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0413` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0414` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0415` | domain | 409 | no | The request conflicts with the current resource, workflow, or domain state for this branch. | Refresh the resource state, make the required state transition first, or choose an operation that is valid for the current state. | | `DALP-0416` | client | 404 | no | DAPI could not find the user kyc profile in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0417` | client | 404 | no | DAPI could not find the user kyc profile not found action in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0418` | client | 404 | no | DAPI could not find the user kyc source version not found initialdata new users in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0419` | client | 404 | no | DAPI could not find the user kyc version in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0420` | client | 404 | no | DAPI could not find the user kyc version not found action in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0421` | client | 404 | no | DAPI could not find the user kyc version not found version read in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0422` | operational | 500 | no | DAPI reached a branch-specific operational failure while executing this route. | Retry if the operation is idempotent. If it continues, contact support with the request id and route name. | | `DALP-0423` | client | 400 | no | The request reached a branch whose input failed route-specific validation before the operation could be executed. | Adjust the request fields to satisfy this endpoint requirement, then retry. | | `DALP-0424` | client | 404 | no | DAPI could not find the user in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0425` | client | 404 | no | DAPI could not find the user not found by wallet in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0426` | client | 404 | no | DAPI could not find the user not found get security in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0427` | client | 404 | no | DAPI could not find the user not found national id in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0428` | client | 404 | no | DAPI could not find the user not found password reset in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0429` | client | 404 | no | DAPI could not find the user not found reset mfa in the active system, organization, or indexer scope. | Verify the identifier, ownership scope, and indexing state before retrying. | | `DALP-0430` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0431` | dependency | 503 | yes | A required downstream dependency, indexed record, workflow, storage service, or chain provider was not available for this branch. | Retry after the dependency or indexer catches up. If it continues, contact support with the request id. | | `DALP-0432` | dependency | 503 | yes | The indexer has not linked the fixed-yield schedule to its token metadata yet. | Retry after indexing catches up. | | `DALP-0433` | domain | 409 | no | The capital-raise-limit compliance module stores configuration that cannot be changed after the module instance has been installed. | Deploy a new capital-raise-limit module instance with the required parameters, then update token compliance to use that instance. | | `DALP-0434` | client | 400 | no | The token uses the V1 compliance model, which has no scoped module bindings and cannot atomically update module parameters with a scope. | Use configureComplianceModule for V1 tokens, or migrate the asset to a V2 token with a per-token compliance engine before configuring scoped modules. | | `DALP-0435` | client | 400 | no | The token uses the V1 compliance model, which supports only the legacy single-instance compliance-module install flow. | Use installComplianceModule for V1 tokens, or migrate the asset to a V2 token with a per-token compliance engine before installing scoped module instances. | | `DALP-0436` | client | 400 | no | The token uses the V1 compliance model, which has no per-instance scope state to update. | Use V1 compliance-module configuration routes for V1 tokens, or migrate the asset to a V2 token with a per-token compliance engine before setting module scope. | | `DALP-0437` | dependency | 400 | no | A Restate workflow endpoint returned HTTP 400 before DAPI could complete the downstream operation. | Retry only after verifying the API request shape and workflow input. If the request is valid, contact support with the request id. | | `DALP-0438` | dependency | 401 | no | A Restate workflow endpoint returned HTTP 401, so DAPI could not authenticate the downstream workflow call. | Retry after the service credentials are corrected. If you are an API consumer, contact support with the request id. | | `DALP-0439` | dependency | 403 | no | A Restate workflow endpoint returned HTTP 403, so the downstream workflow refused DAPI's call. | Retry after the workflow permissions or service identity are corrected. If you are an API consumer, contact support with the request id. | | `DALP-0440` | dependency | 404 | yes | A Restate workflow endpoint returned HTTP 404, which means the expected workflow service, handler, or invocation target was not available. | Retry after the workflow deployment and service discovery have caught up. If it continues, contact support with the request id. | | `DALP-0441` | dependency | 409 | no | A Restate workflow endpoint returned HTTP 409 because the downstream workflow state did not allow this call at this time. | Refresh the resource or deployment status, wait for the active workflow step to finish, then retry if the operation is still valid. | | `DALP-0442` | dependency | 422 | no | A Restate workflow endpoint returned HTTP 422 because the downstream workflow accepted the call envelope but rejected its semantic input. | Verify the requested operation is valid for the current resource state. If the request is valid, contact support with the request id. | | `DALP-0443` | dependency | 500 | no | A Restate workflow endpoint returned HTTP 500 while processing the downstream operation. | Retry if the operation is idempotent. If it continues, contact support with the request id so the workflow failure can be investigated. | | `DALP-0444` | dependency | 504 | yes | A Restate workflow endpoint returned HTTP 504 before the downstream operation completed. | Check the resource or deployment status before retrying so you do not duplicate a workflow action that may still complete. | | `DALP-0445` | dependency | 400 | no | The transaction queue returned BAD\_REQUEST before a transaction could be accepted for processing. | Verify the route input, wallet verification payload, and queued transaction parameters before retrying. | | `DALP-0446` | dependency | 409 | no | The transaction queue returned CONFLICT because the requested transaction cannot be accepted in the current queue or resource state. | Refresh the transaction or resource state, wait for any active queued operation to finish, then retry if the operation is still valid. | | `DALP-0447` | dependency | 422 | no | The transaction queue accepted the operation envelope but rejected its semantic transaction input before dispatch. | Verify the requested chain operation is valid for the current contract and token state before retrying. | | `DALP-0448` | dependency | 504 | yes | The transaction queue did not observe the queued transaction confirmation before its timeout window expired. | Check the transaction status before retrying so you do not submit a duplicate operation. | | `DALP-0449` | client | 404 | no | The migration comparison route needs the active system address before it can compare deployed component implementations. | Set the SYSTEM\_ADDRESS setting or deploy a system, then retry the comparison. | | `DALP-0450` | client | 400 | no | The migration comparison route needs the directory contract address from the active network configuration. | Set networks.\.contracts.directory in config.yml for the default network, then retry the comparison. | | `DALP-0451` | dependency | 404 | yes | The directory contract address is configured, but the indexer has not produced the directory row needed for component comparison. | Retry after the indexer processes the directory registration. If it continues, contact support with the request id. | | `DALP-0452` | client | 400 | no | The migration start route needs an active organization before it can resolve the system and workflow scope. | Select an organization, then start the migration again. | | `DALP-0453` | client | 404 | no | The migration start route needs the active system address before it can submit the migration workflow. | Deploy a system or set the SYSTEM\_ADDRESS setting, then start the migration again. | | `DALP-0454` | auth | 403 | no | The migration start route needs the authenticated account wallet before it can verify migration permissions. | Connect a wallet to your account, then start the migration again. | | `DALP-0455` | auth | 403 | no | The authenticated wallet does not hold a role that is allowed to start the system migration workflow. | Use an account with the system manager or admin role, or ask an admin to grant the role before retrying. | | `DALP-0456` | client | 400 | no | The migration start route needs the directory contract address from the active network configuration. | Set networks.\.contracts.directory in config.yml for the default network, then start the migration again. | | `DALP-0457` | domain | 409 | yes | The migration workflow journal still has an active invocation for this organization. | Wait for the current migration to finish, then retry if another migration is still needed. | | `DALP-0458` | dependency | 500 | yes | DAPI could not reset the previous system migration workflow journal and state before submitting a fresh run. | Retry in a moment. If the problem continues, contact support with the request id. | | `DALP-0459` | client | 400 | no | The migration workflow needs a connected sender wallet and wallet id for the on-chain transactions. | Connect a wallet, then start the migration again. | | `DALP-0460` | dependency | 500 | no | The system migration workflow reported a terminal failure: {reason} | Review the failed migration step and retry after correcting the underlying issue. If it continues, contact support with the request id. | | `DALP-0461` | domain | 409 | yes | Another add/remove claim-topic mutation for this trusted issuer is already running. DAPI does not queue same-key callers behind the database lock because that can consume database pool sessions. | Wait for the active mutation to finish, then retry the claim-topic request if the issuer's topic set still needs to change. | | `DALP-0462` | domain | 422 | no | The template contains configured modules or required controls from a different compliance module generation. | Remove the incompatible controls or create a template with the matching legacy/current module set. | | `DALP-0463` | client | 409 | no | The organization already has a non-system template with the submitted name. | Choose a unique template name or update the existing template instead. | | `DALP-0464` | client | 404 | no | DAPI could not find an account with native-balance state in the active system scope. | Verify the chain ID, address, system selection, and indexer freshness before retrying. | | `DALP-0465` | client | 400 | no | The configured maturity-redemption treasury is a smart contract. ERC-20 `approve` must be called from the contract that owns the funds, which this route cannot do on its behalf. | Call `approve(spender, amount)` directly from the treasury contract (e.g. via its admin or governance flow), or reconfigure the feature to use an externally-owned treasury wallet. | | `DALP-0466` | client | 404 | yes | The maturity-redemption feature row for this token has not been created or the indexer has not processed the feature initialization yet, so denomination asset and treasury are still unset. | Verify the token has the maturity-redemption feature attached and wait for the indexer to catch up, then retry. | | `DALP-0467` | client | 403 | no | ERC-20 `approve` must be called from the wallet that owns the funds. The authenticated caller is not the configured treasury wallet, so this route cannot proxy the approval. | Retry signed in as the configured treasury wallet. | | `DALP-0468` | dependency | 503 | yes | The indexer populates `treasury_is_contract` via a single `eth_getCode` on every `MaturityRedemptionFeatureCreated` / `TreasuryUpdated`, but the column is still `null` for this feature — the indexer has not finished the classification step yet (typical during a partial reindex / backfill). Without that flag we cannot decide whether the approve flow is allowed (EOA treasuries) or must be blocked (contract treasuries), so we refuse to proceed rather than risk a misclassified on-chain failure. | Wait for the indexer to catch up and retry. If the column stays `null` for an extended period, verify the indexer is healthy and that `MaturityRedemptionFeatureCreated` / `TreasuryUpdated` for this feature were ingested. | | `DALP-0469` | client | 400 | no | The native-balance history endpoint limits each page to 100 rows to keep indexer queries bounded. | Request 100 or fewer history rows per page and use the pagination links for additional rows. | | `DALP-0470` | client | 400 | no | The native-balance history endpoint requires a lower block bound so history scans stay within the supported query window. | Include `filter[since]` with a block number using the `gte` or `eq` operator, then retry. | | `DALP-0471` | client | 404 | no | DAPI could not find an active (non-revoked) claim matching the requested topic on the target identity. | Verify the topic and identity address, and confirm the claim has not already been revoked, before retrying. | | `DALP-0472` | auth | 403 | no | The route requires a recent re-authentication and the current session is older than the policy window. | Re-authenticate (sign in again or complete the step-up challenge) and retry the request. | | `DALP-0473` | dependency | 503 | yes | The indexer populates `treasury_is_contract` via a single `eth_getCode` on every `FixedTreasuryYieldFeatureCreated` / `TreasuryUpdated`, but the column is still `null` for this feature — the indexer has not finished the classification step yet (typical during a partial reindex / backfill). Without that flag we cannot decide whether the approve-yield-allowance flow is allowed (EOA treasuries) or must be blocked (contract treasuries), so we refuse to proceed rather than risk a misclassified on-chain failure. | Wait for the indexer to catch up and retry. If the column stays `null` for an extended period, verify the indexer is healthy and that `FixedTreasuryYieldFeatureCreated` / `TreasuryUpdated` for this feature were ingested. | | `DALP-0474` | client | 409 | no | Compliance modules that price mint amounts in fiat (currently capital-raise-limit) require an IDALPPriceResolver addon registered on the system. The dapi resolves the addon address server-side and injects it into the module's initialization payload — without an installed addon there is no resolver to inject, so the deployment is refused before any on-chain transaction is queued. | Install the PriceResolver addon on this system (Addons → Price Resolver) before creating tokens whose compliance template includes capital-raise-limit, adding capital-raise-limit to an existing token, or updating its parameters. | | `DALP-0475` | domain | 409 | no | The platform-wide Account Abstraction feature flag is disabled, so smart wallets cannot be selected as default wallets for routed transactions. | Enable the platform Account Abstraction feature flag, or create the smart wallet without setting it as default. | | `DALP-0476` | domain | 409 | no | The platform-wide Account Abstraction feature flag is disabled, so smart wallets cannot be selected as default wallets for routed transactions. | Enable the platform Account Abstraction feature flag, or update only the smart wallet metadata. | | `DALP-0477` | domain | 409 | no | `conversion-minter` declares `dependsOn: ["conversion"]` in the addon registry. Publishing a template with the minter but no conversion would produce a runtime-invalid token (a minter with nothing to mint into). | Add the missing dependency to `requiredFeatures` (or remove the orphan dependent), then retry the publish or update request. | | `DALP-0488` | auth | 401 | no | The compliance webhook signature or URL token could not be verified against the integration credentials. | Verify the provider webhook URL and signing secret configuration, then send a freshly signed event. | | `DALP-0489` | client | 400 | no | The compliance webhook timestamp was more than five minutes away from the server clock. | Send a fresh provider event with a current timestamp and verify clock synchronization for the provider integration. | | `DALP-0490` | domain | 404 | no | A compliance webhook referenced an external subject ID that has not been mapped to a DALP identity or wallet. | Create the subject through DALP before accepting provider webhooks for that external ID, or reconcile the event from the audit log. | | `DALP-0491` | domain | 404 | no | The webhook target is not currently available for compliance event intake. | Verify the integration configuration and resume the integration before sending more provider events. | | `DALP-0492` | domain | 409 | no | The compliance integration failed provisioning or health checks and cannot process provider events. | Repair or reprovision the integration before retrying webhook processing. | | `DALP-0493` | domain | 422 | no | The provider payload did not match a supported ClaimSource verdict or monitoring-alert shape and was persisted for audit. | Review the provider event type and update the adapter mapping before replaying the event. | | `DALP-0494` | domain | 409 | no | The event timestamp or sequence is behind the last applied event for the same integration, subject, and topic. | Do not replay stale provider events; inspect the audit event if the provider's ordering guarantees appear broken. | | `DALP-0495` | dependency | 503 | yes | DAPI could not reach the provider health endpoint or the provider returned an unhealthy response. | Retry after a short backoff; if the failure persists, verify provider availability and the integration credentials. | | `DALP-0496` | dependency | 502 | no | The provider applicant or monitored-subject creation call failed before DALP could persist the subject mapping. | Verify provider credentials and the submitted subject fields, then retry applicant creation. | | `DALP-0497` | contract | 502 | no | The tenant trusted issuer registry rejected the provider issuer EOA registration transaction. | Verify the provider issuer address, claim topic, tenant registry address, and transaction trace before retrying provisioning. | | `DALP-0498` | domain | 400 | no | The webhook payload declared a claim topic different from the single topic configured for the integration. | Send the event to an integration configured for that topic, or correct the provider adapter topic mapping. | | `DALP-0499` | domain | 404 | no | A wallet-subject compliance provider event referenced a wallet that DALP could not resolve to an OnchainID identity. | Register the wallet to an OnchainID identity before creating or replaying the compliance subject mapping. | | `DALP-0500` | domain | 410 | no | The pending webhook signing secret expired before it was promoted to the active secret. | Start a new secret rotation and update the provider dashboard with the active webhook signing secret. | | `DALP-0501` | client | 409 | no | The integration is not in a state that permits the requested action (for example, resume requires paused; revoke is terminal). | Refresh the integration and choose an action that matches its current state. | | `DALP-0502` | dependency | 502 | no | The provider transaction-registration call (Sumsub KYT) failed before DALP could persist the transaction-id mapping. | Verify provider credentials and the submitted transaction fields, then retry transaction registration. | | `DALP-0503` | client | 404 | no | DAPI could not find the webhook endpoint in the authenticated tenant scope. | Verify the endpoint identifier and tenant context before retrying. | | `DALP-0504` | client | 404 | no | DAPI could not find the webhook delivery in the authenticated tenant scope. | Verify the delivery identifier, endpoint identifier, and tenant context before retrying. | | `DALP-0505` | client | 404 | no | DAPI could not find the webhook replay in the authenticated tenant scope. | Verify the replay identifier, endpoint identifier, and tenant context before retrying. | | `DALP-0506` | client | 404 | no | DAPI could not find the webhook receipt in the authenticated tenant scope. | Verify the receipt identifier, delivery identifier, and tenant context before retrying. | | `DALP-0507` | client | 429 | yes | The tenant has exhausted the replay-specific webhook rate-limit bucket. | Wait for the Retry-After interval before starting another replay. | | `DALP-0508` | client | 400 | no | The replay request spans more blocks or events than the API allows in a single replay job. | Split the replay into smaller ranges and retry. | | `DALP-0509` | domain | 409 | no | The endpoint is disabled and cannot accept live, replay, or test deliveries until it is re-enabled. | Re-enable the endpoint after fixing the delivery failure cause, then retry. | | `DALP-0510` | auth | 401 | no | The webhook signature, timestamp, or signed payload did not verify against the expected signing material. | Verify the signing secret, timestamp tolerance, and exact raw request body before retrying. | | `DALP-0511` | permission | 404 | no | The event does not exist or is not available to the current actor for recall. | Verify the event identifier and use an actor with webhook recall permissions. | | `DALP-0512` | client | 400 | no | The endpoint URL resolves to a private, loopback, link-local, or otherwise disallowed network range. | Use a publicly reachable HTTPS endpoint that does not resolve to a private network address. | | `DALP-0513` | domain | 409 | no | The tenant has reached the maximum number of webhook endpoints allowed for this environment. | Delete an unused endpoint or contact support to raise the tenant endpoint limit. | | `DALP-0514` | client | 409 | no | The same Idempotency-Key was previously used for a request with a different method, path, or body hash. | Reuse an Idempotency-Key only for the exact same request, or send a new key for a different request. | | `DALP-0515` | client | 409 | yes | Another request with the same Idempotency-Key is still being processed for this tenant. | Wait for the in-flight request to finish, then retry the exact same request. | | `DALP-0516` | client | 409 | no | The endpoint URL change cannot proceed silently — there are pending delivery attempts the dispatcher would re-target to the new URL. | Re-send the request with `?acknowledgePending=true` to confirm those deliveries should target the new URL, or wait for the queue to drain. | | `DALP-0517` | client | 422 | no | Switching an endpoint to `defaultPayloadShape='fat'` requires an explicit GDPR ceremony — the operator must acknowledge each `<eventType>.<fieldPath>` that the thin shape would have stripped. The ack on the request didn't cover every PII field for the endpoint's subscriptions, so the switch would have silently broadened the consent surface. | Resend the PATCH with `fatEventsAcknowledgment.fieldsAcknowledged` set to the full list of `<eventType>.<fieldPath>` paths returned by `getWebhookFatAcknowledgmentFields(subscriptions)` — the dapp's switch-to-fat dialog computes this automatically. | | `DALP-0518` | dependency | 503 | yes | The API could not contact the Restate admin endpoint required to inspect or mutate workflow state. | Retry after a short backoff. If the problem continues, check the Restate cluster health and contact support with the request id. | | `DALP-0519` | client | 404 | no | The Restate admin API does not currently report a deployment matching the configured service URL. | Confirm the durable service is registered with Restate (force-redeploy if needed) and retry. | | `DALP-0520` | client | 409 | no | The workflow has an active invocation, has already succeeded, or its prior invocations could not be purged. | Inspect the workflow with the doctor route and resolve the blocking invocation before retrying. | | `DALP-0521` | dependency | 408 | yes | The Thales Luna 7 partition was waiting on out-of-band m-of-n approval and the configured retry window elapsed before the operator quorum signed off, so the workflow gave up. | Activate the partition on the HSM and re-submit the transaction. If expiries are frequent, lengthen the signing window via the signer config (luna.quorum.retryWindowMs). | | `DALP-0522` | dependency | 409 | no | The Luna vendor-extension reported the partition as activated, but the signing call still returned m-of-n-pending three times in a row. Either the firmware misreports activation or a concurrent caller is consuming the quorum. | Inspect the partition state on the HSM directly. If activation is genuinely pending, retry once the operator approves; otherwise contact support with the request id so the classification heuristic can be tuned. | | `DALP-0523` | domain | 422 | no | The token's transaction-fee rates have been permanently frozen on-chain. The mutation was rejected before reaching the queue. | Frozen rates cannot be modified. Update the fee recipient instead, or deploy a new token if different rates are needed. | | `DALP-0600` | domain | 409 | no | Removing a target currency would orphan its on-chain feed. Feeds can only be added. | Submit a value that is a strict superset of the existing target currencies. | | `DALP-0601` | client | 422 | no | The provider's supported-currency snapshot does not contain every requested code. | Pick currencies from GET /v2/exchange-rates/supported-currencies, or wait for the next provider refresh if you expect the code to appear. | | `DALP-0602` | dependency | 503 | yes | The workflow runtime did not durably accept the addCurrencyFeeds dispatch, so the new currencies were not persisted. | Retry the request; if it continues, contact support with the request id. | | `DALP-0603` | permission | 403 | no | Adding a target currency requires the caller's wallet to hold FEEDS\_MANAGER\_ROLE on the system AccessManager so the workflow's IssuerSignedScalarFeedFactory.createFeed call does not revert; the indexed access-control state shows the wallet is not a current member of that role. | Have a system manager grant FEEDS\_MANAGER\_ROLE to this wallet on the system, then retry. The setting is not persisted until the dispatch succeeds. | | `DALP-0604` | domain | 422 | no | The active chain has no FeedsDirectory wired up (V3-style deployment), so the workflow's IssuerSignedScalarFeedFactory.createFeed path that backs TARGET\_CURRENCIES additions cannot execute. Persisting the addition would leave the setting reflecting a currency with no path to a feed. | Use a system deployed on a chain that supports FeedsDirectory, or remove the new currency from the requested TARGET\_CURRENCIES value. | | `DALP-0605` | domain | 422 | no | Both features bind to the token transfer path and would account for the same transaction fee twice. | Remove one of the conflicting features from the template's `requiredFeatures` or `featureConfigs`. | ## Surface Coverage [#surface-coverage] | Surface | Transport | Coverage | Public adapter behavior | | ------------------------------------ | --------- | ----------- | ------------------------------------------------------------------------------------------------------------ | | Better Auth | hono | excluded | pass-through | | Known contract revert | orpc-rest | proof-slice | Preserve CONTRACT\_ERROR data and DALP code while adding unified registry metadata. | | Indexer database dependency failures | orpc-rest | in-scope | Report degraded indexer schema reads through registry observability while preserving safe fallback behavior. | | Restate dependency failures | orpc-rest | proof-slice | Return retryable dependency guidance through the unified public envelope. | | Bundler JSON-RPC handler | json-rpc | proof-slice | Preserve JSON-RPC 2.0 error framing and include registry data under error.data. | | oRPC RPC | orpc-rpc | proof-slice | Carry the same registry identity and public copy through the RPC-compatible error payload. | | oRPC v2 REST | orpc-rest | proof-slice | Return the unified public error envelope for greenfield v2/API REST failures. | | DAPI SSE streams | sse | proof-slice | Emit a protocol-valid error event after stream start or direct envelope before stream start. | | Hono unknown fallback | hono | proof-slice | Return the safe unknown public envelope after redaction and telemetry emission. | # Error code reference Source: https://docs.settlemint.com/docs/developer-guides/api-integration/error-code-reference Complete reference of all DALP contract error codes with messages, severity, and suggested actions. {/* Auto-generated by tools/generate-error-catalog.ts — do not edit */} {/* Run `bun run codegen:error-catalog` to regenerate */} This page lists every DALP contract error code extracted from the on-chain ABIs. When a smart contract reverts, the API returns an HTTP 500 response with the revert details including the DALP error code. Use this reference to understand what went wrong and how to resolve it. For API-level errors (authentication, validation, timeouts), see [Error handling](/docs/developer-guides/api-integration/error-handling). ## Contents [#contents] * [Compliance & token operations](#compliance-token-operations) — 124 errors (DALP-1001+) * [Settlement & XvP](#settlement-xvp) — 9 errors (DALP-2001+) * [Airdrop & distribution](#airdrop-distribution) — 31 errors (DALP-3001+) * [System & infrastructure](#system-infrastructure) — 412 errors (DALP-4001+) * [Internal (OpenZeppelin / low-level)](#internal-openzeppelin-low-level) — 68 errors (DALP-9001+) *** ## Compliance & token operations [#compliance--token-operations] | DALP Code | Message | Suggested Action | Audience | Severity | Retryable | Solidity Error | | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------- | --------- | ---------------------------------------------------------------- | | DALP-1001 | Bond already matured | — | user | error | No | `BondAlreadyMatured()` | | DALP-1002 | Bond invalid maturity date | — | user | error | No | `BondInvalidMaturityDate()` | | DALP-1003 | Bond not yet matured | — | user | error | No | `BondNotYetMatured(uint256,uint256)` | | DALP-1004 | Bytes feeds not supported | — | user | error | No | `BytesFeedsNotSupported()` | | DALP-1005 | Caller must have identity | — | user | error | No | `CallerMustHaveIdentity()` | | DALP-1006 | Caller not identity owner | — | user | error | No | `CallerNotIdentityOwner()` | | DALP-1007 | Cannot transfer converted tokens | — | user | error | No | `CannotTransferConvertedTokens(uint256,uint256)` | | DALP-1008 | Cannot withdraw sale token | — | user | error | No | `CannotWithdrawSaleToken()` | | DALP-1009 | Compliance check failed: \{\{reason}} | Review the compliance requirements for this token. | user | error | No | `ComplianceCheckFailed(string)` | | DALP-1010 | Compliance implementation not set | — | user | error | No | `ComplianceImplementationNotSet()` | | DALP-1011 | This operation has already been completed | — | user | error | No | `ComplianceModuleAlreadyRegistered(string)` | | DALP-1012 | Compliance module registry implementation not set | — | operator | error | No | `ComplianceModuleRegistryImplementationNotSet()` | | DALP-1013 | Contract identity topic id not set | — | user | error | No | `ContractIdentityTopicIdNotSet()` | | DALP-1014 | Contract missing identity interface | — | user | error | No | `ContractMissingIdentityInterface(address)` | | DALP-1015 | Empty token type | — | user | error | No | `EmptyTokenType()` | | DALP-1016 | Exceeds unconverted balance | — | user | error | No | `ExceedsUnconvertedBalance(uint256,uint256)` | | DALP-1017 | External token registry implementation not set | — | operator | error | No | `ExternalTokenRegistryImplementationNotSet()` | | DALP-1018 | Feature token mismatch | — | user | error | No | `FeatureTokenMismatch(address)` | | DALP-1019 | Fee rate is frozen | — | user | error | No | `FeeRateIsFrozen()` | | DALP-1020 | Fee rates are frozen | — | user | error | No | `FeeRatesAreFrozen()` | | DALP-1021 | Feed already exists | — | user | error | No | `FeedAlreadyExists(address,uint256)` | | DALP-1022 | The requested resource could not be found | — | user | error | No | `FeedNotFound(address,uint256)` | | DALP-1023 | Feeds directory implementation not set | — | user | error | No | `FeedsDirectoryImplementationNotSet()` | | DALP-1024 | Fees are frozen | — | user | error | No | `FeesAreFrozen()` | | DALP-1025 | Freeze amount exceeds available balance | — | user | error | No | `FreezeAmountExceedsAvailableBalance(uint256,uint256)` | | DALP-1026 | Global compliance not available | — | user | error | No | `GlobalComplianceNotAvailable()` | | DALP-1027 | Historical balances not available | — | user | error | No | `HistoricalBalancesNotAvailable()` | | DALP-1028 | Identity already accepted | — | user | error | No | `IdentityAlreadyAccepted(address)` | | DALP-1029 | Identity already exists | — | user | error | No | `IdentityAlreadyExists(address)` | | DALP-1030 | This operation has already been completed | — | user | error | No | `IdentityAlreadyRegistered(address)` | | DALP-1031 | Identity already set | — | user | error | No | `IdentityAlreadySet()` | | DALP-1032 | Identity factory implementation not set | — | operator | error | No | `IdentityFactoryImplementationNotSet()` | | DALP-1033 | Identity implementation not set | — | user | error | No | `IdentityImplementationNotSet()` | | DALP-1034 | Identity not pending | — | user | error | No | `IdentityNotPending(address)` | | DALP-1035 | Identity not registered | — | user | error | No | `IdentityNotRegistered(address)` | | DALP-1036 | Identity registry already bound | — | operator | error | No | `IdentityRegistryAlreadyBound(address)` | | DALP-1037 | Identity registry implementation not set | — | operator | error | No | `IdentityRegistryImplementationNotSet()` | | DALP-1038 | Identity registry not bound | — | operator | error | No | `IdentityRegistryNotBound(address)` | | DALP-1039 | Identity registry storage implementation not set | — | operator | error | No | `IdentityRegistryStorageImplementationNotSet()` | | DALP-1040 | Your account does not have enough resources for this operation | — | user | error | No | `InsufficientCollateral(uint256,uint256)` | | DALP-1041 | Your account does not have enough resources for this operation | — | user | error | No | `InsufficientDenominationAssetBalance(uint256,uint256)` | | DALP-1042 | Your account does not have enough resources for this operation | — | user | error | No | `InsufficientTokenBalance()` | | DALP-1043 | Your account does not have enough resources for this operation | — | user | error | No | `InsufficientTreasuryBalance(uint256,uint256)` | | DALP-1044 | The provided input is invalid | — | user | error | No | `InvalidCollateralRatio(uint16)` | | DALP-1045 | The provided input is invalid | — | user | error | No | `InvalidCollateralTopic(uint256)` | | DALP-1046 | The provided input is invalid | — | user | error | No | `InvalidComplianceModuleAddress()` | | DALP-1047 | The provided input is invalid | — | user | error | No | `InvalidContractIdentityImplementation()` | | DALP-1048 | The provided input is invalid | — | user | error | No | `InvalidFeeRate()` | | DALP-1049 | The provided input is invalid | — | user | error | No | `InvalidFeeRecipient()` | | DALP-1050 | The provided input is invalid | — | user | error | No | `InvalidFeeToken()` | | DALP-1051 | The provided input is invalid | — | user | error | No | `InvalidFeedAddress()` | | DALP-1052 | The provided input is invalid | — | user | error | No | `InvalidFeedInterface(address)` | | DALP-1053 | The provided input is invalid | — | user | error | No | `InvalidIdentityContract()` | | DALP-1054 | The provided input is invalid | — | operator | error | No | `InvalidIdentityFactoryAddress()` | | DALP-1055 | The provided input is invalid | — | user | error | No | `InvalidIdentityImplementation()` | | DALP-1056 | The provided input is invalid | — | operator | error | No | `InvalidIdentityRegistryAddress()` | | DALP-1057 | The provided input is invalid | — | user | error | No | `InvalidIdentityWalletAddress()` | | DALP-1058 | The provided input is invalid | — | user | error | No | `InvalidRedemptionAmount()` | | DALP-1059 | The provided input is invalid | — | user | error | No | `InvalidTargetIdentity()` | | DALP-1060 | The provided input is invalid | — | user | error | No | `InvalidTokenAddress()` | | DALP-1061 | The provided input is invalid | — | operator | error | No | `InvalidTokenFactoryAddress()` | | DALP-1062 | The provided input is invalid | — | user | error | No | `InvalidTokenImplementationAddress()` | | DALP-1063 | The provided input is invalid | — | user | error | No | `InvalidTokenImplementationInterface()` | | DALP-1064 | Issuer identity not initialized | — | user | error | No | `IssuerIdentityNotInitialized()` | | DALP-1065 | Maturity date in past | — | user | error | No | `MaturityDateInPast(uint256,uint256)` | | DALP-1066 | Maturity date not reached | — | user | error | No | `MaturityDateNotReached(uint256,uint256)` | | DALP-1067 | Minting failed | — | user | error | No | `MintingFailed()` | | DALP-1068 | Net balance invariant violation | — | user | error | No | `NetBalanceInvariantViolation(address)` | | DALP-1069 | No denomination asset balance | — | user | error | No | `NoDenominationAssetBalance()` | | DALP-1070 | No fees to reconcile | — | user | error | No | `NoFeesToReconcile()` | | DALP-1071 | No historical balances provider | — | user | error | No | `NoHistoricalBalancesProvider(address)` | | DALP-1072 | No tokens to recover | — | user | error | No | `NoTokensToRecover()` | | DALP-1073 | Not whitelisted | — | user | error | No | `NotWhitelisted()` | | DALP-1074 | Recipient address frozen | — | user | error | No | `RecipientAddressFrozen()` | | DALP-1075 | Recover insufficient balance | — | user | error | No | `RecoverInsufficientBalance()` | | DALP-1076 | Sender address frozen | — | user | error | No | `SenderAddressFrozen()` | | DALP-1077 | Token access manager implementation not set | — | user | error | No | `TokenAccessManagerImplementationNotSet()` | | DALP-1078 | Token already bound | — | user | error | No | `TokenAlreadyBound(address)` | | DALP-1079 | This operation has already been completed | — | user | error | No | `TokenAlreadyRegistered(address)` | | DALP-1080 | Token compliance already exists | — | user | error | No | `TokenComplianceAlreadyExists(address)` | | DALP-1081 | Token compliance factory not available | — | operator | error | No | `TokenComplianceFactoryNotAvailable()` | | DALP-1082 | Token decimals too high | — | user | error | No | `TokenDecimalsTooHigh(address,uint8)` | | DALP-1083 | Token factory registry implementation not set | — | operator | error | No | `TokenFactoryRegistryImplementationNotSet()` | | DALP-1084 | This operation has already been completed | — | operator | error | No | `TokenFactoryTypeAlreadyRegistered(string)` | | DALP-1085 | This operation has already been completed | — | operator | error | No | `TokenFactoryTypeAlreadyRegisteredV2(bytes32)` | | DALP-1086 | Token identity address mismatch | — | user | error | No | `TokenIdentityAddressMismatch(address,address)` | | DALP-1087 | Token implementation not set | — | user | error | No | `TokenImplementationNotSet()` | | DALP-1088 | Token must support access managed | — | user | error | No | `TokenMustSupportAccessManaged()` | | DALP-1089 | Token not bound | — | user | error | No | `TokenNotBound()` | | DALP-1090 | Token not bound | — | user | error | No | `TokenNotBound(address)` | | DALP-1091 | Token not registered | — | user | error | No | `TokenNotRegistered(address)` | | DALP-1092 | Transfer blocked after maturity | — | user | error | No | `TransferBlockedAfterMaturity(address,address)` | | DALP-1093 | You do not have permission for this operation | — | user | error | No | `UnauthorizedApprover()` | | DALP-1094 | You do not have permission for this operation | — | user | error | No | `UnauthorizedFeedCreation()` | | DALP-1095 | You do not have permission for this operation | — | user | error | No | `UnauthorizedTokenSaleCreation()` | | DALP-1096 | This transfer was blocked because the sender or recipient does not meet the compliance requirements for this token. | Verify that both the sender and recipient have completed identity verification and meet all token requirements. | user | error | No | `TransferNotCompliant()` | | DALP-1097 | Wallet not registered to this identity | — | user | error | No | `WalletNotRegisteredToThisIdentity(address,address)` | | DALP-1098 | A required value cannot be zero | — | user | error | No | `ZeroRedemptionAmount()` | | DALP-1099 | The provided input is invalid | — | user | error | No | `InvalidFeedsDirectory()` | | DALP-1100 | This wallet is not associated with the expected identity. | Ensure your wallet is registered to the correct identity, or contact your administrator for assistance. | user | error | No | `WalletNotAssociatedWithIdentity(address,address)` | | DALP-1101 | The account does not have enough tokens. Available: \{\{available}}, required: \{\{required}}. | Check the token balance and try again with a smaller amount, or acquire more tokens. | user | error | No | `ERC20InsufficientBalance(address,uint256,uint256)` | | DALP-1102 | The token allowance is too low. Current allowance: \{\{allowance}}, required: \{\{required}}. | Approve a sufficient token allowance before retrying the operation. | user | error | Yes | `ERC20InsufficientAllowance(address,uint256,uint256)` | | DALP-1103 | This operation is temporarily unavailable because it has been paused. | Wait for the administrator to resume operations, then try again. | user | warning | Yes | `EnforcedPause()` | | DALP-1104 | The account \{\{account}} does not have a registered identity on this platform. | The account owner needs to complete the identity registration process before they can interact with this token. | user | error | No | `IdentityDoesNotExist(address)` | | DALP-1105 | The approved spending amount is not enough. Current: \{\{currentAllowance}}, required: \{\{requiredAllowance}}. | Increase the token allowance to cover the required amount, then try again. | user | error | Yes | `InsufficientAllowance(address,address,address,uint256,uint256)` | | DALP-1106 | The amount exceeds the currently frozen token balance. Available: \{\{available}}, requested: \{\{requested}}. | Check the frozen balance and reduce the amount you are trying to unfreeze. | user | error | No | `InsufficientFrozenTokens(uint256,uint256)` | | DALP-1107 | The provided account is not valid for identity verification. | Double-check the account address and try again. | user | error | No | `InvalidIdentityAddress()` | | DALP-1108 | The provided token address is not valid. | Provide a valid, non-zero token address and try again. | user | error | No | `InvalidToken()` | | DALP-1109 | This token is currently paused and no operations can be performed. | Wait for the token administrator to resume the token, then try again. | user | warning | Yes | `TokenPaused()` | | DALP-1110 | This token cannot be issued to the recipient because they do not meet the compliance requirements. | Ensure the recipient has completed identity verification and meets all requirements for this token. | user | error | No | `MintNotCompliant()` | | DALP-1111 | Stale feed | — | user | error | No | `StaleFeed(address,uint256,uint256,uint256)` | | DALP-1112 | Token compliance creation failed | — | user | error | No | `TokenComplianceCreationFailed()` | | DALP-1113 | Not factory token | — | operator | error | No | `NotFactoryToken(address)` | | DALP-1114 | Only compliance engine | — | user | error | No | `OnlyComplianceEngine()` | | DALP-1115 | Token registries already exist | — | user | error | No | `TokenRegistriesAlreadyExist(address)` | | DALP-1116 | Token registry implementations missing | — | operator | error | No | `TokenRegistryImplementationsMissing()` | | DALP-1117 | Compliance module type mismatch | — | user | error | No | `ComplianceModuleTypeMismatch(bytes32,bytes32)` | | DALP-1118 | Unsupported compliance module | — | user | error | No | `UnsupportedComplianceModule(address)` | | DALP-1119 | Token scope not supported | — | user | error | No | `TokenScopeNotSupported()` | | DALP-1120 | Compliance check failed: \{\{reason}} | Review the compliance requirements for this token. | user | error | No | `ComplianceCheckFailed(address,bytes32,bytes32,string)` | | DALP-1121 | Identity v2 not supported | — | user | error | No | `IdentityV2NotSupported()` | | DALP-1122 | This operation can no longer be completed because the deadline has expired | — | user | error | No | `ExpiredIdentityManagementKeyAuthorization(uint256)` | | DALP-1123 | Identity management-key authorization was signed by \{\{recovered}}, but it must be signed by \{\{wallet}}. | Ask the target wallet owner to sign a fresh identity authorization, then retry. | user | error | No | `InvalidIdentityManagementKeyAuthorization(address,address)` | | DALP-EXT-TOKEN-NO-CODE | There is no contract deployed at \{\{tokenAddress}} on this network. External token registration requires the token contract to actually exist at that address. | Verify the address is correct, that you're connected to the right network, and that the token contract has been deployed there before retrying. | user | error | No | `TokenAddressHasNoCode(address)` | *** ## Settlement & XvP [#settlement--xvp] | DALP Code | Message | Suggested Action | Audience | Severity | Retryable | Solidity Error | | --------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | -------- | -------- | --------- | -------------------------------------------------- | | DALP-2001 | Sender not approved settlement | — | user | error | No | `SenderNotApprovedSettlement()` | | DALP-2003 | You are not a participant in this settlement. | Verify that you are using the correct account and settlement reference. | user | error | No | `SenderNotInvolvedInSettlement()` | | DALP-2004 | This settlement has already been cancelled. | No further action is possible — create a new settlement if needed. | user | error | No | `XvPSettlementAlreadyCancelled()` | | DALP-2005 | This settlement has already been completed. | No further action is needed — the settlement was executed successfully. | user | error | No | `XvPSettlementAlreadyExecuted()` | | DALP-2006 | This settlement has expired and can no longer be executed. | Create a new settlement with an updated deadline. | user | error | No | `XvPSettlementExpired()` | | DALP-2007 | This settlement expired and the funds have already been returned. | No further withdrawal is needed — funds were returned when the settlement expired. | user | error | No | `XvPSettlementExpiredWithdrawalAlreadyProcessed()` | | DALP-2008 | This settlement cannot be executed because not all parties have approved it. | Check which participants still need to approve, then ask them to do so. | user | error | Yes | `XvPSettlementNotApproved()` | | DALP-2009 | This settlement has not yet expired. | Expired withdrawal is only available after the settlement deadline has passed. | user | error | Yes | `XvPSettlementNotExpired()` | | DALP-2013 | You have already approved this settlement. | No further action is needed — your approval has been recorded. | user | warning | No | `SenderAlreadyApprovedSettlement()` | *** ## Airdrop & distribution [#airdrop--distribution] | DALP Code | Message | Suggested Action | Audience | Severity | Retryable | Solidity Error | | --------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | -------- | -------- | --------- | ------------------------------------------------- | | DALP-3001 | Airdrop ended | — | user | error | No | `AirdropEnded()` | | DALP-3002 | Airdrop not started | — | user | error | No | `AirdropNotStarted()` | | DALP-3003 | Claim already revoked | — | user | error | No | `ClaimAlreadyRevoked(bytes32)` | | DALP-3004 | The requested resource could not be found | — | user | error | No | `ClaimDoesNotExist(bytes32)` | | DALP-3005 | Claim not eligible | — | user | error | No | `ClaimNotEligible()` | | DALP-3006 | Claim not valid according to issuer | — | user | error | No | `ClaimNotValidAccordingToIssuer(address,uint256)` | | DALP-3007 | Cliff exceeds vesting duration | — | user | error | No | `CliffExceedsVestingDuration()` | | DALP-3008 | Distribution cap exceeded | — | user | error | No | `DistributionCapExceeded()` | | DALP-3009 | Duplicate claim topic | — | user | error | No | `DuplicateClaimTopic(uint256)` | | DALP-3010 | The provided input is invalid | — | user | error | No | `InvalidAirdropName()` | | DALP-3011 | The provided input is invalid | — | user | error | No | `InvalidClaimTrackerAddress()` | | DALP-3012 | The provided input is invalid | — | user | error | No | `InvalidDistributionAddress()` | | DALP-3013 | The provided input is invalid | — | user | error | No | `InvalidMerkleRoot()` | | DALP-3014 | The provided input is invalid | — | user | error | No | `InvalidVestingDuration()` | | DALP-3015 | The provided input is invalid | — | user | error | No | `InvalidVestingStrategy(address)` | | DALP-3016 | The provided input is invalid | — | user | error | No | `InvalidVestingStrategyAddress()` | | DALP-3017 | No claim topics provided | — | user | error | No | `NoClaimTopicsProvided()` | | DALP-3018 | Push airdrop claim not allowed | — | user | error | No | `PushAirdropClaimNotAllowed()` | | DALP-3019 | Sender lacks claim signer key | — | user | error | No | `SenderLacksClaimSignerKey()` | | DALP-3020 | You do not have permission for this operation | — | user | error | No | `UnauthorizedAirdropCreation()` | | DALP-3021 | You do not have permission for this operation | — | user | error | No | `UnauthorizedContractClaim(address,address)` | | DALP-3022 | Unsupported claim scheme | — | user | error | No | `UnsupportedClaimScheme(uint256)` | | DALP-3023 | Vesting airdrop implementation not set | — | user | error | No | `VestingAirdropImplementationNotSet()` | | DALP-3024 | Vesting already initialized | — | user | error | No | `VestingAlreadyInitialized()` | | DALP-3025 | Vesting not initialized | — | user | error | No | `VestingNotInitialized()` | | DALP-3026 | Claim fallback disabled | — | user | error | No | `ClaimFallbackDisabled()` | | DALP-3027 | No claim fallback | — | user | error | No | `NoClaimFallback(address)` | | DALP-3029 | The claim amount cannot be zero. | Enter a valid amount greater than zero to claim your airdrop tokens. | user | error | Yes | `ZeroClaimAmount()` | | DALP-3030 | This airdrop reward has already been claimed. | Check your account — the tokens from this claim should already be in your balance. | user | warning | No | `IndexAlreadyClaimed()` | | DALP-3031 | The claim amount does not match what was allocated in the airdrop. | Use the exact amount shown in your airdrop allocation. | user | error | Yes | `InvalidClaimAmount()` | | DALP-3032 | Your claim could not be verified against the airdrop distribution list. | Make sure you are using the correct account that was included in the airdrop. | user | error | No | `InvalidMerkleProof()` | *** ## System & infrastructure [#system--infrastructure] | DALP Code | Message | Suggested Action | Audience | Severity | Retryable | Solidity Error | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | -------- | --------- | ----------------------------------------------------------------- | | DALP-2010 | This settlement requires a security code to execute. | Provide the required security code when executing the settlement. | user | error | Yes | `HashlockRequired()` | | DALP-2011 | The settlement deadline is not valid because it is not far enough in the future. | Set the deadline to a time that is at least a few seconds from now and try again. | user | error | Yes | `InvalidCutoffDate()` | | DALP-2012 | The settlement has no payment flows defined. | Add at least one payment flow specifying the tokens and amounts to exchange. | user | error | Yes | `EmptyFlows()` | | DALP-4001 | Access managed invalid authority | — | user | error | No | `AccessManagedInvalidAuthority(address)` | | DALP-4002 | Access managed required delay | — | user | error | No | `AccessManagedRequiredDelay(address,uint32)` | | DALP-4003 | Access manager already deployed | — | user | error | No | `AccessManagerAlreadyDeployed(address)` | | DALP-4004 | Access manager already scheduled | — | user | error | No | `AccessManagerAlreadyScheduled(bytes32)` | | DALP-4005 | Access manager bad confirmation | — | user | error | No | `AccessManagerBadConfirmation()` | | DALP-4006 | This operation can no longer be completed because the deadline has expired | — | user | error | No | `AccessManagerExpired(bytes32)` | | DALP-4007 | Access manager invalid initial admin | — | user | error | No | `AccessManagerInvalidInitialAdmin(address)` | | DALP-4008 | Access manager locked role | — | user | error | No | `AccessManagerLockedRole(uint64)` | | DALP-4009 | Access manager not configured | — | user | error | No | `AccessManagerNotConfigured()` | | DALP-4010 | Access manager not ready | — | user | error | No | `AccessManagerNotReady(bytes32)` | | DALP-4011 | Access manager not scheduled | — | user | error | No | `AccessManagerNotScheduled(bytes32)` | | DALP-4012 | Access manager unauthorized account | — | user | error | No | `AccessManagerUnauthorizedAccount(address,uint64)` | | DALP-4013 | Access manager unauthorized call | — | user | error | No | `AccessManagerUnauthorizedCall(address,address,bytes4)` | | DALP-4014 | Access manager unauthorized cancel | — | user | error | No | `AccessManagerUnauthorizedCancel(address,address,address,bytes4)` | | DALP-4015 | Access manager unauthorized consume | — | user | error | No | `AccessManagerUnauthorizedConsume(address)` | | DALP-4016 | Account implementation not set | — | user | error | No | `AccountImplementationNotSet()` | | DALP-4017 | Account unauthorized | — | user | error | No | `AccountUnauthorized(address)` | | DALP-4018 | Accrual already closed | — | user | error | No | `AccrualAlreadyClosed(address)` | | DALP-4019 | Addon registry implementation not set | — | operator | error | No | `AddonRegistryImplementationNotSet()` | | DALP-4020 | Address already deployed | — | user | error | No | `AddressAlreadyDeployed(address)` | | DALP-4021 | Address already on bypass list | — | user | error | No | `AddressAlreadyOnBypassList(address)` | | DALP-4022 | The requested resource could not be found | — | user | error | No | `AddressNotFoundInList(address)` | | DALP-4023 | Address not on bypass list | — | user | error | No | `AddressNotOnBypassList(address)` | | DALP-4024 | Already archived | — | user | error | No | `AlreadyArchived(bytes32)` | | DALP-4025 | Already distributed | — | user | error | No | `AlreadyDistributed()` | | DALP-4026 | Already initialized | — | user | error | No | `AlreadyInitialized()` | | DALP-4027 | Already matured | — | user | error | No | `AlreadyMatured()` | | DALP-4028 | This operation has already been completed | — | user | error | No | `AlreadyRegistered(bytes32)` | | DALP-4029 | Ambiguous interest provider | — | user | error | No | `AmbiguousInterestProvider(uint256)` | | DALP-4030 | Amount exceeds int256 max | — | user | error | No | `AmountExceedsInt256Max()` | | DALP-4031 | And or operation requires two operands | — | user | error | No | `AndOrOperationRequiresTwoOperands()` | | DALP-4032 | And or operations require two operands | — | user | error | No | `AndOrOperationsRequireTwoOperands()` | | DALP-4033 | Approval already exists | — | user | error | No | `ApprovalAlreadyExists()` | | DALP-4034 | Approval already used | — | user | error | No | `ApprovalAlreadyUsed()` | | DALP-4035 | This operation can no longer be completed because the deadline has expired | — | user | error | No | `ApprovalExpired()` | | DALP-4036 | Approval required | — | user | error | No | `ApprovalRequired()` | | DALP-4037 | Archive not registered | — | user | error | No | `ArchiveNotRegistered(bytes32)` | | DALP-4038 | Array length mismatch | — | user | error | No | `ArrayLengthMismatch()` | | DALP-4039 | Array length mismatch | — | user | error | No | `ArrayLengthMismatch(uint256,uint256)` | | DALP-4040 | Associated contract not set | — | user | error | No | `AssociatedContractNotSet()` | | DALP-4041 | This operation has already been completed | — | user | error | No | `AuthorizationContractAlreadyRegistered(address)` | | DALP-4042 | Authorization contract not registered | — | user | error | No | `AuthorizationContractNotRegistered(address)` | | DALP-4043 | Batch size exceeds limit | — | user | error | No | `BatchSizeExceedsLimit()` | | DALP-4044 | Below min conversion amount | — | user | error | No | `BelowMinConversionAmount(uint256,uint256)` | | DALP-4045 | Buyer not eligible | — | user | error | No | `BuyerNotEligible()` | | DALP-4046 | Caller not factory | — | operator | error | No | `CallerNotFactory()` | | DALP-4047 | Cancel not allowed | — | user | error | No | `CancelNotAllowed()` | | DALP-4048 | Cancel vote already cast | — | user | error | No | `CancelVoteAlreadyCast(address)` | | DALP-4049 | Cancel vote not cast | — | user | error | No | `CancelVoteNotCast(address)` | | DALP-4050 | Cannot execute to zero address | — | user | error | No | `CannotExecuteToZeroAddress()` | | DALP-4051 | Cannot initialize logic contract | — | user | error | No | `CannotInitializeLogicContract()` | | DALP-4052 | Cannot recover self | — | user | error | No | `CannotRecoverSelf()` | | DALP-4053 | Cannot remove default validator | — | user | error | No | `CannotRemoveDefaultValidator(address)` | | DALP-4054 | Contract already linked | — | user | error | No | `ContractAlreadyLinked(address)` | | DALP-4055 | Conversion id already used | — | user | error | No | `ConversionIdAlreadyUsed(bytes32)` | | DALP-4056 | Conversion minter missing | — | user | error | No | `ConversionMinterMissing()` | | DALP-4057 | Conversion window closed | — | user | error | No | `ConversionWindowClosed()` | | DALP-4058 | Conversion window not open | — | user | error | No | `ConversionWindowNotOpen()` | | DALP-4059 | Create2 empty bytecode | — | user | error | No | `Create2EmptyBytecode()` | | DALP-4060 | This operation can no longer be completed because the deadline has expired | — | user | error | No | `DeadlineExpired()` | | DALP-4061 | Decimal mismatch | — | user | error | No | `DecimalMismatch(uint8,uint8)` | | DALP-4062 | Default validator not set | — | user | error | No | `DefaultValidatorNotSet()` | | DALP-4063 | Delegate and revert | — | user | error | No | `DelegateAndRevert(bool,bytes)` | | DALP-4064 | Denomination mismatch | — | user | error | No | `DenominationMismatch(address,address)` | | DALP-4065 | Deployment address mismatch | — | user | error | No | `DeploymentAddressMismatch()` | | DALP-4066 | Deposit withdrawal failed | — | user | error | No | `DepositWithdrawalFailed(address,address,uint256,bytes)` | | DALP-4067 | Directory already set | — | user | error | No | `DirectoryAlreadySet()` | | DALP-4068 | Directory not set | — | user | error | No | `DirectoryNotSet()` | | DALP-4069 | Duplicate feature | — | user | error | No | `DuplicateFeature(address)` | | DALP-4070 | Duplicate module | — | user | error | No | `DuplicateModule(address)` | | DALP-4071 | Duplicate signature | — | user | error | No | `DuplicateSignature(address)` | | DALP-4072 | Duplicate type id | — | user | error | No | `DuplicateTypeId(bytes32)` | | DALP-4073 | ETH not accepted | — | user | error | No | `ETHNotAccepted()` | | DALP-4074 | ETH transfers not allowed | — | user | error | No | `ETHTransfersNotAllowed()` | | DALP-4075 | Eip7702 sender not delegate | — | user | error | No | `Eip7702SenderNotDelegate(address)` | | DALP-4076 | Eip7702 sender without code | — | user | error | No | `Eip7702SenderWithoutCode(address)` | | DALP-4077 | Empty arrays provided | — | user | error | No | `EmptyArraysProvided()` | | DALP-4078 | Empty expression not allowed | — | user | error | No | `EmptyExpressionNotAllowed()` | | DALP-4079 | Empty id | — | user | error | No | `EmptyId()` | | DALP-4080 | Empty name | — | user | error | No | `EmptyName()` | | DALP-4081 | Empty signature | — | user | error | No | `EmptySignature()` | | DALP-4082 | Exceeded cap | — | user | error | No | `ExceededCap(uint256,uint256)` | | DALP-4083 | Execution already performed | — | user | error | No | `ExecutionAlreadyPerformed(uint256)` | | DALP-4084 | Execution failed | — | user | error | No | `ExecutionFailed()` | | DALP-4085 | The requested resource could not be found | — | user | error | No | `ExecutionIdDoesNotExist(uint256)` | | DALP-4086 | Expression stack overflow | — | user | error | No | `ExpressionStackOverflow()` | | DALP-4087 | Expression too complex | — | user | error | No | `ExpressionTooComplex()` | | DALP-4088 | Failed deployment | — | user | error | No | `FailedDeployment()` | | DALP-4089 | Failed op | — | user | error | No | `FailedOp(uint256,string)` | | DALP-4090 | Failed op with revert | — | user | error | No | `FailedOpWithRevert(uint256,string,bytes)` | | DALP-4091 | Failed send to beneficiary | — | user | error | No | `FailedSendToBeneficiary(address,uint256,bytes)` | | DALP-4092 | Feature already exists | — | user | error | No | `FeatureAlreadyExists()` | | DALP-4093 | Feature creation failed | — | user | error | No | `FeatureCreationFailed()` | | DALP-4094 | The requested resource could not be found | — | operator | error | No | `FeatureFactoryNotFound(bytes32)` | | DALP-4095 | Future lookup | — | user | error | No | `FutureLookup(uint256,uint48)` | | DALP-4096 | Global module already added | — | user | error | No | `GlobalModuleAlreadyAdded(address)` | | DALP-4097 | The requested resource could not be found | — | user | error | No | `GlobalModuleNotFound(address)` | | DALP-4098 | Governor already cast vote | — | user | error | No | `GovernorAlreadyCastVote(address)` | | DALP-4099 | Governor already queued proposal | — | user | error | No | `GovernorAlreadyQueuedProposal(uint256)` | | DALP-4100 | Governor disabled deposit | — | user | error | No | `GovernorDisabledDeposit()` | | DALP-4101 | Governor insufficient proposer votes | — | user | error | No | `GovernorInsufficientProposerVotes(address,uint256,uint256)` | | DALP-4102 | Governor invalid proposal length | — | user | error | No | `GovernorInvalidProposalLength(uint256,uint256,uint256)` | | DALP-4103 | Governor invalid signature | — | user | error | No | `GovernorInvalidSignature(address)` | | DALP-4104 | Governor invalid vote params | — | user | error | No | `GovernorInvalidVoteParams()` | | DALP-4105 | Governor invalid vote type | — | user | error | No | `GovernorInvalidVoteType()` | | DALP-4106 | Governor invalid voting period | — | user | error | No | `GovernorInvalidVotingPeriod(uint256)` | | DALP-4107 | Governor nonexistent proposal | — | user | error | No | `GovernorNonexistentProposal(uint256)` | | DALP-4108 | Governor not queued proposal | — | user | error | No | `GovernorNotQueuedProposal(uint256)` | | DALP-4109 | Governor only executor | — | user | error | No | `GovernorOnlyExecutor(address)` | | DALP-4110 | Governor queue not implemented | — | user | error | No | `GovernorQueueNotImplemented()` | | DALP-4111 | Governor restricted proposer | — | user | error | No | `GovernorRestrictedProposer(address)` | | DALP-4112 | Governor unable to cancel | — | user | error | No | `GovernorUnableToCancel(uint256,address)` | | DALP-4113 | Governor unexpected proposal state | — | user | error | No | `GovernorUnexpectedProposalState(uint256,uint8,bytes32)` | | DALP-4114 | Hard cap exceeded | — | user | error | No | `HardCapExceeded()` | | DALP-4115 | Hard cap must be positive | — | user | error | No | `HardCapMustBePositive()` | | DALP-4116 | Hashlock reveal not required | — | user | error | No | `HashlockRevealNotRequired()` | | DALP-4117 | History not supported | — | user | error | No | `HistoryNotSupported()` | | DALP-4118 | Identities required | — | user | error | No | `IdentitiesRequired()` | | DALP-4119 | Implementation not set in factory | — | operator | error | No | `ImplementationNotSetInFactory()` | | DALP-4120 | Index out of bounds | — | user | error | No | `IndexOutOfBounds(uint256,uint256)` | | DALP-4121 | Initial key already setup | — | user | error | No | `InitialKeyAlreadySetup()` | | DALP-4122 | Initialization deadline passed | — | user | error | No | `InitializationDeadlinePassed()` | | DALP-4123 | Initialization with zero address | — | user | error | No | `InitializationWithZeroAddress()` | | DALP-4124 | Your account does not have enough resources for this operation | — | user | error | No | `InsufficientAccruedInterest(uint256,uint256)` | | DALP-4125 | Your account does not have enough resources for this operation | — | user | error | No | `InsufficientDeposit(uint256,uint256)` | | DALP-4126 | Your account does not have enough resources for this operation | — | user | error | No | `InsufficientPrincipal(uint256,uint256)` | | DALP-4127 | Your account does not have enough resources for this operation | — | user | error | No | `InsufficientSignatures(uint256,uint256)` | | DALP-4128 | Your account does not have enough resources for this operation | — | user | error | No | `InsufficientWeight(uint256,uint256)` | | DALP-4129 | Interest provider missing | — | user | error | No | `InterestProviderMissing()` | | DALP-4130 | Interface registration limit reached | — | user | error | No | `InterfaceRegistrationLimitReached()` | | DALP-4131 | Internal function | — | user | error | No | `InternalFunction()` | | DALP-4132 | Interoperable address empty reference and address | — | user | error | No | `InteroperableAddressEmptyReferenceAndAddress()` | | DALP-4133 | Interoperable address parsing error | — | user | error | No | `InteroperableAddressParsingError(bytes)` | | DALP-4134 | The provided input is invalid | — | user | error | No | `InvalidAccessManager()` | | DALP-4135 | The provided input is invalid | — | user | error | No | `InvalidAccountNonce(address,uint256)` | | DALP-4136 | The provided input is invalid | — | user | error | No | `InvalidAddonAddress()` | | DALP-4137 | The provided input is invalid | — | user | error | No | `InvalidAddress()` | | DALP-4138 | The provided input is invalid | — | user | error | No | `InvalidAmount()` | | DALP-4139 | The provided input is invalid | — | user | error | No | `InvalidAuthorizationContract(address)` | | DALP-4140 | The provided input is invalid | — | user | error | No | `InvalidBasisPerUnit()` | | DALP-4141 | The provided input is invalid | — | user | error | No | `InvalidBeneficiary(address)` | | DALP-4142 | The provided input is invalid | — | user | error | No | `InvalidCap(uint256)` | | DALP-4143 | The provided input is invalid | — | user | error | No | `InvalidContractAddress()` | | DALP-4144 | The provided input is invalid | — | user | error | No | `InvalidConversionWindow(uint256,uint256)` | | DALP-4145 | The provided input is invalid | — | user | error | No | `InvalidDecimals(uint8)` | | DALP-4146 | The provided input is invalid | — | user | error | No | `InvalidDenominationAsset()` | | DALP-4147 | The provided input is invalid | — | user | error | No | `InvalidDirectory()` | | DALP-4148 | The provided input is invalid | — | user | error | No | `InvalidDirectoryAddress()` | | DALP-4149 | The provided input is invalid | — | user | error | No | `InvalidEndDate()` | | DALP-4150 | The provided input is invalid | — | user | error | No | `InvalidEndTime()` | | DALP-4151 | The provided input is invalid | — | user | error | No | `InvalidExpressionMustEvaluateToOneResult()` | | DALP-4152 | The provided input is invalid | — | user | error | No | `InvalidExpressionStackResult()` | | DALP-4153 | The provided input is invalid | — | user | error | No | `InvalidExternalChainId(uint64)` | | DALP-4154 | The provided input is invalid | — | user | error | No | `InvalidFaceValue()` | | DALP-4155 | The provided input is invalid | — | operator | error | No | `InvalidFactoryAddress()` | | DALP-4156 | The provided input is invalid | — | user | error | No | `InvalidFeatureConfig()` | | DALP-4157 | The provided input is invalid | — | operator | error | No | `InvalidGlobalRegistryAddress(address)` | | DALP-4158 | The provided input is invalid | — | user | error | No | `InvalidHistorySize()` | | DALP-4159 | The provided input is invalid | — | user | error | No | `InvalidImplementation()` | | DALP-4160 | The provided input is invalid | — | user | error | No | `InvalidImplementationAddress()` | | DALP-4161 | The provided input is invalid | — | user | error | No | `InvalidImplementationInterface(address,bytes4)` | | DALP-4162 | The provided input is invalid | — | user | error | No | `InvalidInitialManagementKey()` | | DALP-4163 | The provided input is invalid | — | user | error | No | `InvalidInitialization()` | | DALP-4164 | The provided input is invalid | — | user | error | No | `InvalidInitializationDeadline()` | | DALP-4165 | The provided input is invalid | — | user | error | No | `InvalidInputArrayLengths()` | | DALP-4166 | The provided input is invalid | — | user | error | No | `InvalidInterval()` | | DALP-4167 | The provided input is invalid | — | user | error | No | `InvalidIssuerAddress()` | | DALP-4168 | The provided input is invalid | — | user | error | No | `InvalidLostWallet()` | | DALP-4169 | The provided input is invalid | — | user | error | No | `InvalidModule()` | | DALP-4170 | The provided input is invalid | — | user | error | No | `InvalidNonce()` | | DALP-4171 | The provided input is invalid | — | user | error | No | `InvalidObservedAt()` | | DALP-4172 | The provided input is invalid | — | user | error | No | `InvalidOnchainID()` | | DALP-4173 | The provided input is invalid | — | user | error | No | `InvalidOnchainId()` | | DALP-4174 | The provided input is invalid | — | user | error | No | `InvalidParameter()` | | DALP-4175 | The provided input is invalid | — | user | error | No | `InvalidParameters()` | | DALP-4176 | The provided input is invalid | — | user | error | No | `InvalidParameters(string)` | | DALP-4177 | The provided input is invalid | — | user | error | No | `InvalidPaymaster(address)` | | DALP-4178 | The provided input is invalid | — | user | error | No | `InvalidPaymasterData(uint256)` | | DALP-4179 | The provided input is invalid | — | user | error | No | `InvalidPaymasterSignatureLength(uint256,uint256)` | | DALP-4180 | The provided input is invalid | — | user | error | No | `InvalidPaymentCurrency()` | | DALP-4181 | The provided input is invalid | — | user | error | No | `InvalidPeriod()` | | DALP-4182 | The provided input is invalid | — | user | error | No | `InvalidPhaseTransition()` | | DALP-4183 | The provided input is invalid | — | user | error | No | `InvalidPriceCalculation()` | | DALP-4184 | The provided input is invalid | — | user | error | No | `InvalidRange()` | | DALP-4185 | The provided input is invalid | — | user | error | No | `InvalidRate()` | | DALP-4186 | The provided input is invalid | — | user | error | No | `InvalidRedeemAddress()` | | DALP-4187 | The provided input is invalid | — | user | error | No | `InvalidRedeemAmount()` | | DALP-4188 | The provided input is invalid | — | operator | error | No | `InvalidRegistryAddress()` | | DALP-4189 | The provided input is invalid | — | operator | error | No | `InvalidRegistryAddress(address)` | | DALP-4190 | The provided input is invalid | — | user | error | No | `InvalidRequirement(uint256,uint256)` | | DALP-4191 | The provided input is invalid | — | user | error | No | `InvalidSaleStatus()` | | DALP-4192 | The provided input is invalid | — | user | error | No | `InvalidScalarSchemaHash(bytes32,bytes32)` | | DALP-4193 | The provided input is invalid | — | user | error | No | `InvalidSecret()` | | DALP-4194 | The provided input is invalid | — | user | error | No | `InvalidShortString()` | | DALP-4195 | The provided input is invalid | — | user | error | No | `InvalidSignature()` | | DALP-4196 | The provided input is invalid | — | user | error | No | `InvalidSignatureLength()` | | DALP-4197 | The provided input is invalid | — | user | error | No | `InvalidSigner()` | | DALP-4198 | The provided input is invalid | — | user | error | No | `InvalidStake(uint256,uint256)` | | DALP-4199 | The provided input is invalid | — | user | error | No | `InvalidStartDate()` | | DALP-4200 | The provided input is invalid | — | user | error | No | `InvalidStartTime()` | | DALP-4201 | The provided input is invalid | — | user | error | No | `InvalidStorageAddress()` | | DALP-4202 | The provided input is invalid | — | user | error | No | `InvalidSubjectAddress()` | | DALP-4203 | The provided input is invalid | — | user | error | No | `InvalidSystemAddress()` | | DALP-4204 | The provided input is invalid | — | user | error | No | `InvalidTimeWindow()` | | DALP-4205 | The provided input is invalid | — | user | error | No | `InvalidTiming()` | | DALP-4206 | The provided input is invalid | — | user | error | No | `InvalidTopicIdZeroNotAllowed()` | | DALP-4207 | The provided input is invalid | — | operator | error | No | `InvalidTopicSchemeRegistry()` | | DALP-4208 | The provided input is invalid | — | operator | error | No | `InvalidTopicSchemeRegistryAddress()` | | DALP-4209 | The provided input is invalid | — | user | error | No | `InvalidTreasury()` | | DALP-4210 | The provided input is invalid | — | operator | error | No | `InvalidTrustedIssuersRegistry()` | | DALP-4211 | The provided input is invalid | — | user | error | No | `InvalidUnstakeDelay(uint256,uint256)` | | DALP-4212 | The provided input is invalid | — | user | error | No | `InvalidUserAddress()` | | DALP-4213 | The provided input is invalid | — | user | error | No | `InvalidWithdrawalAddress()` | | DALP-4214 | Issuer already exists | — | user | error | No | `IssuerAlreadyExists(address)` | | DALP-4215 | Issuer cannot be zero address | — | user | error | No | `IssuerCannotBeZeroAddress()` | | DALP-4216 | The requested resource could not be found | — | user | error | No | `IssuerDoesNotExist(address)` | | DALP-4217 | You do not have permission for this operation | — | user | error | No | `IssuerNotAuthorized()` | | DALP-4218 | The requested resource could not be found | — | user | error | No | `IssuerNotFoundInTopicList(address,uint256)` | | DALP-4219 | Key already has this purpose | — | user | error | No | `KeyAlreadyHasThisPurpose(bytes32,uint256)` | | DALP-4220 | Key cannot be zero | — | user | error | No | `KeyCannotBeZero()` | | DALP-4221 | The requested resource could not be found | — | user | error | No | `KeyDoesNotExist(bytes32)` | | DALP-4222 | Key does not have this purpose | — | user | error | No | `KeyDoesNotHaveThisPurpose(bytes32,uint256)` | | DALP-4223 | Kind mismatch | — | user | error | No | `KindMismatch(uint8,uint8)` | | DALP-4224 | Length mismatch | — | user | error | No | `LengthMismatch()` | | DALP-4225 | Locked amount mismatch | — | user | error | No | `LockedAmountMismatch(address,address,uint256,uint256)` | | DALP-4226 | Max features reached | — | user | error | No | `MaxFeaturesReached()` | | DALP-4227 | Maximum allocation exceeded | — | user | error | No | `MaximumAllocationExceeded()` | | DALP-4228 | Meta registry cannot provide complete answer | — | operator | error | No | `MetaRegistryCannotProvideCompleteAnswer()` | | DALP-4229 | Metadata immutable | — | user | error | No | `MetadataImmutable()` | | DALP-4230 | Missing type identifier | — | user | error | No | `MissingTypeIdentifier(address)` | | DALP-4231 | Module already added | — | operator | error | No | `ModuleAlreadyAdded()` | | DALP-4233 | This operation has already been completed | — | operator | error | No | `ModuleAlreadyRegistered(bytes32,address)` | | DALP-4234 | The requested resource could not be found | — | operator | error | No | `ModuleNotFound()` | | DALP-4236 | Module not registered | — | operator | error | No | `ModuleNotRegistered(address)` | | DALP-4237 | No approval to revoke | — | user | error | No | `NoApprovalToRevoke()` | | DALP-4238 | No bytecode | — | user | error | No | `NoBytecode(address)` | | DALP-4239 | No checkpoint at timepoint | — | user | error | No | `NoCheckpointAtTimepoint(uint256)` | | DALP-4240 | No contribution to refund | — | user | error | No | `NoContributionToRefund()` | | DALP-4241 | No initial admins | — | user | error | No | `NoInitialAdmins()` | | DALP-4242 | No local flows | — | user | error | No | `NoLocalFlows()` | | DALP-4243 | No yield available | — | user | error | No | `NoYieldAvailable()` | | DALP-4244 | Not initialized | — | user | error | No | `NotInitialized()` | | DALP-4245 | Not initializing | — | user | error | No | `NotInitializing()` | | DALP-4246 | Not installed | — | user | error | No | `NotInstalled()` | | DALP-4247 | Not matured | — | user | error | No | `NotMatured()` | | DALP-4248 | Not operation requires one operand | — | user | error | No | `NotOperationRequiresOneOperand()` | | DALP-4249 | Not registered | — | user | error | No | `NotRegistered(bytes32)` | | DALP-4250 | Not registered feature | — | user | error | No | `NotRegisteredFeature(address)` | | DALP-4251 | Not staked | — | user | error | No | `NotStaked(uint256,uint256,bool)` | | DALP-4252 | Observed at too far in future | — | user | error | No | `ObservedAtTooFarInFuture()` | | DALP-4253 | Onchain id already set | — | user | error | No | `OnchainIdAlreadySet()` | | DALP-4254 | Out of range access | — | user | error | No | `OutOfRangeAccess()` | | DALP-4255 | Owner already set | — | user | error | No | `OwnerAlreadySet()` | | DALP-4256 | Partial conversion disabled | — | user | error | No | `PartialConversionDisabled()` | | DALP-4257 | Paymaster unauthorized | — | user | error | No | `PaymasterUnauthorized(address)` | | DALP-4258 | Paymaster zero entry point | — | user | error | No | `PaymasterZeroEntryPoint()` | | DALP-4259 | Paymaster zero signer | — | user | error | No | `PaymasterZeroSigner()` | | DALP-4260 | Phase not active | — | user | error | No | `PhaseNotActive()` | | DALP-4261 | Post op reverted | — | user | error | No | `PostOpReverted(bytes)` | | DALP-4262 | Premint already completed | — | user | error | No | `PremintAlreadyCompleted()` | | DALP-4263 | Proxy creation failed | — | operator | error | No | `ProxyCreationFailed()` | | DALP-4264 | Purchase amount too low | — | user | error | No | `PurchaseAmountTooLow()` | | DALP-4265 | Query before enabled | — | user | error | No | `QueryBeforeEnabled(uint256,uint48)` | | DALP-4266 | Recipient not verified | — | user | error | No | `RecipientNotVerified()` | | DALP-4267 | Recover zero address | — | user | error | No | `RecoverZeroAddress()` | | DALP-4268 | Reentrancy | — | user | error | No | `Reentrancy()` | | DALP-4269 | Reentrant initialization | — | user | error | No | `ReentrantInitialization()` | | DALP-4270 | Refund grace period active | — | user | error | No | `RefundGracePeriodActive()` | | DALP-4271 | The requested resource could not be found | — | user | error | No | `RemapSourceNotFound(bytes32)` | | DALP-4272 | Remap target already exists | — | user | error | No | `RemapTargetAlreadyExists(bytes32)` | | DALP-4273 | Replicated execution already performed | — | user | error | No | `ReplicatedExecutionAlreadyPerformed(uint256)` | | DALP-4274 | The requested resource could not be found | — | user | error | No | `ReplicatedExecutionIdDoesNotExist(uint256)` | | DALP-4275 | Revocation not allowed after commit | — | user | error | No | `RevocationNotAllowedAfterCommit()` | | DALP-4276 | The requested resource could not be found | — | user | error | No | `RoundNotFound(uint80)` | | DALP-4277 | Sale duration must be positive | — | user | error | No | `SaleDurationMustBePositive()` | | DALP-4278 | Sale ended | — | user | error | No | `SaleEnded()` | | DALP-4279 | Sale never activated | — | user | error | No | `SaleNeverActivated()` | | DALP-4280 | Sale not active | — | user | error | No | `SaleNotActive()` | | DALP-4281 | Sale not ended | — | user | error | No | `SaleNotEnded()` | | DALP-4282 | Sale not failed | — | user | error | No | `SaleNotFailed()` | | DALP-4283 | Sale not finalized as success | — | user | error | No | `SaleNotFinalizedAsSuccess()` | | DALP-4284 | Sale not started | — | user | error | No | `SaleNotStarted()` | | DALP-4285 | Sale start must be in future | — | user | error | No | `SaleStartMustBeInFuture()` | | DALP-4286 | Salt already taken | — | user | error | No | `SaltAlreadyTaken(string)` | | DALP-4287 | Same address | — | user | error | No | `SameAddress()` | | DALP-4288 | Schedule not active | — | user | error | No | `ScheduleNotActive()` | | DALP-4289 | Schema hash mismatch | — | user | error | No | `SchemaHashMismatch()` | | DALP-4290 | Schema hash mismatch | — | user | error | No | `SchemaHashMismatch(bytes32,bytes32)` | | DALP-4291 | Secret already revealed | — | user | error | No | `SecretAlreadyRevealed()` | | DALP-4292 | Secret not revealed | — | user | error | No | `SecretNotRevealed()` | | DALP-4293 | Self transfer | — | user | error | No | `SelfTransfer()` | | DALP-4294 | Sender address result | — | user | error | No | `SenderAddressResult(address)` | | DALP-4295 | Sender lacks action key | — | user | error | No | `SenderLacksActionKey()` | | DALP-4296 | Sender lacks management key | — | user | error | No | `SenderLacksManagementKey()` | | DALP-4297 | Sender not local | — | user | error | No | `SenderNotLocal()` | | DALP-4298 | Signature unchanged | — | user | error | No | `SignatureUnchanged(string,string)` | | DALP-4299 | Signature validation failed | — | user | error | No | `SignatureValidationFailed(address)` | | DALP-4300 | Slippage exceeded | — | user | error | No | `SlippageExceeded(uint256,uint256)` | | DALP-4301 | Soft cap not reached | — | user | error | No | `SoftCapNotReached()` | | DALP-4302 | Stake not unlocked | — | user | error | No | `StakeNotUnlocked(uint256,uint256)` | | DALP-4303 | Stake withdrawal failed | — | user | error | No | `StakeWithdrawalFailed(address,address,uint256,bytes)` | | DALP-4304 | Stale observation | — | user | error | No | `StaleObservation()` | | DALP-4305 | String too long | — | user | error | No | `StringTooLong(string)` | | DALP-4306 | System access manager not set | — | operator | error | No | `SystemAccessManagerNotSet()` | | DALP-4307 | System addon implementation not set | — | operator | error | No | `SystemAddonImplementationNotSet(bytes32)` | | DALP-4308 | This operation has already been completed | — | operator | error | No | `SystemAddonTypeAlreadyRegistered(string)` | | DALP-4309 | This operation has already been completed | — | operator | error | No | `SystemAddonTypeAlreadyRegisteredV2(bytes32)` | | DALP-4310 | System already bootstrapped | — | operator | error | No | `SystemAlreadyBootstrapped()` | | DALP-4311 | System trusted issuers registry implementation not set | — | operator | error | No | `SystemTrustedIssuersRegistryImplementationNotSet()` | | DALP-4312 | Terms already set | — | user | error | No | `TermsAlreadySet()` | | DALP-4313 | Terms not accepted | — | user | error | No | `TermsNotAccepted()` | | DALP-4314 | Terms not set | — | user | error | No | `TermsNotSet()` | | DALP-4315 | Too many payment currencies | — | user | error | No | `TooManyPaymentCurrencies()` | | DALP-4316 | The requested resource could not be found | — | user | error | No | `TopicIdNotFoundInArray(uint256)` | | DALP-4317 | Topic mismatch | — | user | error | No | `TopicMismatch()` | | DALP-4318 | Topic not registered | — | user | error | No | `TopicNotRegistered(uint256)` | | DALP-4319 | Topic scheme already exists | — | user | error | No | `TopicSchemeAlreadyExists(string)` | | DALP-4320 | The requested resource could not be found | — | user | error | No | `TopicSchemeDoesNotExist(uint256)` | | DALP-4321 | The requested resource could not be found | — | user | error | No | `TopicSchemeDoesNotExistByName(string)` | | DALP-4322 | Topic scheme registry implementation not set | — | operator | error | No | `TopicSchemeRegistryImplementationNotSet()` | | DALP-4323 | Trigger already exists | — | user | error | No | `TriggerAlreadyExists(bytes32)` | | DALP-4324 | This operation can no longer be completed because the deadline has expired | — | user | error | No | `TriggerExpired(bytes32)` | | DALP-4325 | Trigger not active | — | user | error | No | `TriggerNotActive(bytes32)` | | DALP-4326 | The requested resource could not be found | — | user | error | No | `TriggerNotFound(bytes32)` | | DALP-4327 | Trusted issuers meta registry implementation not set | — | operator | error | No | `TrustedIssuersMetaRegistryImplementationNotSet()` | | DALP-4328 | The requested resource could not be found | — | user | error | No | `TxDoesNotExist(uint256,uint256)` | | DALP-4329 | Tx executed | — | user | error | No | `TxExecuted(uint256)` | | DALP-4330 | You do not have permission for this operation | — | user | error | No | `Unauthorized()` | | DALP-4331 | You do not have permission for this operation | — | user | error | No | `UnauthorizedCaller()` | | DALP-4332 | You do not have permission for this operation | — | user | error | No | `UnauthorizedCaller(address,address)` | | DALP-4333 | You do not have permission for this operation | — | user | error | No | `UnauthorizedContractOperation(address)` | | DALP-4334 | You do not have permission for this operation | — | user | error | No | `UnauthorizedConverter(address)` | | DALP-4335 | You do not have permission for this operation | — | user | error | No | `UnauthorizedFeatureCreation()` | | DALP-4336 | You do not have permission for this operation | — | user | error | No | `UnauthorizedHookCaller(address)` | | DALP-4337 | You do not have permission for this operation | — | user | error | No | `UnauthorizedOperation(address)` | | DALP-4338 | You do not have permission for this operation | — | user | error | No | `UnauthorizedSigner(address)` | | DALP-4339 | You do not have permission for this operation | — | user | error | No | `UnauthorizedVotingUnitsQuery()` | | DALP-4340 | Unknown expression type | — | user | error | No | `UnknownExpressionType()` | | DALP-4341 | Unregistered key | — | user | error | No | `UnregisteredKey(bytes32)` | | DALP-4342 | Unsupported attribute | — | user | error | No | `UnsupportedAttribute(bytes4)` | | DALP-4343 | Unsupported execution operation | — | user | error | No | `UnsupportedExecutionOperation()` | | DALP-4344 | Unsupported key operation | — | user | error | No | `UnsupportedKeyOperation()` | | DALP-4345 | Unsupported payment currency | — | user | error | No | `UnsupportedPaymentCurrency()` | | DALP-4346 | Value not positive | — | user | error | No | `ValueNotPositive()` | | DALP-4347 | This operation can no longer be completed because the deadline has expired | — | user | error | No | `VotesExpiredSignature(uint256)` | | DALP-4348 | Wallet already linked | — | user | error | No | `WalletAlreadyLinked(address)` | | DALP-4349 | Wallet already marked as lost | — | user | error | No | `WalletAlreadyMarkedAsLost(address)` | | DALP-4350 | Wallet in management keys | — | user | error | No | `WalletInManagementKeys()` | | DALP-4351 | Withdrawal already scheduled | — | user | error | No | `WithdrawalAlreadyScheduled()` | | DALP-4352 | Withdrawal not due | — | user | error | No | `WithdrawalNotDue(uint256,uint256)` | | DALP-4353 | Withdrawal not ready | — | user | error | No | `WithdrawalNotReady()` | | DALP-4354 | Withdrawal not scheduled | — | user | error | No | `WithdrawalNotScheduled()` | | DALP-4355 | Wrapped error | — | user | error | No | `WrappedError(address,bytes4,bytes,bytes)` | | DALP-4356 | Yield schedule active | — | user | error | No | `YieldScheduleActive()` | | DALP-4357 | Yield schedule already set | — | user | error | No | `YieldScheduleAlreadySet()` | | DALP-4358 | A required value cannot be zero | — | user | error | No | `ZeroAddress()` | | DALP-4359 | A required value cannot be zero | — | user | error | No | `ZeroAddress(string)` | | DALP-4360 | A required value cannot be zero | — | user | error | No | `ZeroAddressNotAllowed()` | | DALP-4361 | A required value cannot be zero | — | user | error | No | `ZeroAddressOwner()` | | DALP-4362 | A required value cannot be zero | — | user | error | No | `ZeroAmount()` | | DALP-4363 | A required value cannot be zero | — | user | error | No | `ZeroAmountToDistribute()` | | DALP-4364 | A required value cannot be zero | — | user | error | No | `ZeroAmountToTransfer()` | | DALP-4365 | A required value cannot be zero | — | user | error | No | `ZeroDenominationAsset()` | | DALP-4366 | A required value cannot be zero | — | user | error | No | `ZeroEffectivePrice()` | | DALP-4367 | A required value cannot be zero | — | user | error | No | `ZeroEntryPoint()` | | DALP-4368 | A required value cannot be zero | — | user | error | No | `ZeroFaceValue()` | | DALP-4369 | A required value cannot be zero | — | user | error | No | `ZeroRecipient()` | | DALP-4370 | A required value cannot be zero | — | user | error | No | `ZeroTargetAmount()` | | DALP-4371 | A required value cannot be zero | — | user | error | No | `ZeroTreasuryAddress()` | | DALP-4374 | Not implemented | — | user | error | No | `NotImplemented()` | | DALP-4375 | Paymaster not deployed | — | user | error | No | `PaymasterNotDeployed(address)` | | DALP-4376 | The provided input is invalid | — | user | error | No | `InvalidMaxStaleness()` | | DALP-4377 | Paymaster entry point not contract | — | user | error | No | `PaymasterEntryPointNotContract(address)` | | DALP-4378 | Asset type name required | — | user | error | No | `AssetTypeNameRequired()` | | DALP-4379 | The provided input is invalid | — | user | error | No | `InvalidParentAddress(address)` | | DALP-4380 | Implementation not registered | — | operator | error | No | `ImplementationNotRegistered(bytes32)` | | DALP-4381 | Instance deployment failed | — | user | error | No | `InstanceDeploymentFailed()` | | DALP-4382 | The provided input is invalid | — | user | error | No | `InvalidConfig(string)` | | DALP-4383 | Not module admin | — | user | error | No | `NotModuleAdmin()` | | DALP-4384 | Registry not available | — | operator | error | No | `RegistryNotAvailable()` | | DALP-4389 | Module family mismatch | — | operator | error | No | `ModuleFamilyMismatch(bool,bool)` | | DALP-4390 | Type id mismatch | — | user | error | No | `TypeIdMismatch(bytes32,bytes32)` | | DALP-4392 | Max chain depth exceeded | — | user | error | No | `MaxChainDepthExceeded()` | | DALP-4393 | Not a validator module | — | user | error | No | `NotAValidatorModule(address)` | | DALP-4394 | System registry not available | — | operator | error | No | `SystemRegistryNotAvailable(string)` | | DALP-4396 | V1 hook must bypass adapter | — | user | error | No | `V1HookMustBypassAdapter()` | | DALP-4400 | The account \{\{account}} does not have the required role to perform this action. | Contact your administrator to request the necessary permissions. | user | error | No | `AccessControlUnauthorizedAccount(address,bytes32)` | | DALP-4401 | Only the owner of this resource can perform this action. | Contact the owner or administrator for assistance. | user | error | No | `OwnableUnauthorizedAccount(address)` | | DALP-4402 | You do not have permission to perform this action. | Contact your administrator to request access. | user | error | No | `AccessManagedUnauthorized(address)` | | DALP-4404 | System already set | — | operator | error | No | `SystemAlreadySet()` | | DALP-4405 | System not set | — | operator | error | No | `SystemNotSet()` | | DALP-4406 | Scope expression too complex | — | user | error | No | `ScopeExpressionTooComplex()` | | DALP-4407 | Management keys not supported | — | user | error | No | `ManagementKeysNotSupported()` | | DALP-4408 | Config immutable | — | user | error | No | `ConfigImmutable()` | | DALP-4409 | Binding already active | — | user | error | No | `BindingAlreadyActive(address)` | | DALP-4410 | Binding already inactive | — | user | error | No | `BindingAlreadyInactive(address)` | | DALP-4411 | Binding not active | — | user | error | No | `BindingNotActive(address)` | | DALP-4412 | The requested resource could not be found | — | user | error | No | `BindingNotFound(address)` | | DALP-4413 | You do not have permission for this operation | — | user | error | No | `UnauthorizedOnchainIDSetter(address)` | | DALP-4414 | Caller \{\{caller}} is not authorized to create management keys for contract \{\{contractAddress}}. | Use an authorized contract-management signer or ask an administrator to create the contract identity. | user | error | No | `UnauthorizedContractManagementKeys(address,address)` | | DALP-4415 | Caller \{\{caller}} is not authorized to create management keys for wallet \{\{wallet}}. | Use the target wallet owner to sign identity creation, or ask an administrator to retry. | user | error | No | `UnauthorizedWalletManagementKeys(address,address)` | | DALP-4416 | Account onchain id mismatch | — | user | error | No | `AccountOnchainIDMismatch(address,address,address)` | | DALP-4417 | This operation can no longer be completed because the deadline has expired | — | user | error | No | `ExpiredAccountCreationAuthorization(uint256)` | | DALP-4418 | The provided input is invalid | — | user | error | No | `InvalidAccountCreationAuthorization(address,address)` | | DALP-4419 | Unexpected validator init data | — | user | error | No | `UnexpectedValidatorInitData()` | | DALP-4420 | Empty batch | — | user | error | No | `EmptyBatch()` | | DALP-CHAIN-EMPTY-REVERT | The blockchain rejected this transaction without giving a reason. This usually means you don't have permission for this action, the contract isn't deployed at the expected address on this network, or the chain RPC is misconfigured. | Check that you have the required role for this action and that the system is fully deployed on this chain. Share the request id with your administrator if the issue persists. | user | error | No | `EmptyRevert()` | | DALP-WORKFLOW-FAILED | The deployment workflow failed before it could finish. | Retry the deployment. If the problem persists, share the correlation id with your administrator. | user | error | Yes | `WorkflowFailed()` | | DALP-WORKFLOW-RPC-UNAVAILABLE | The blockchain RPC endpoint is temporarily unreachable, so the deployment could not be completed. | Wait a few seconds and retry. If the problem persists, contact your administrator. | user | error | Yes | `RpcUnavailable()` | *** ## Internal (OpenZeppelin / low-level) [#internal-openzeppelin--low-level] | DALP Code | Message | Suggested Action | Audience | Severity | Retryable | Solidity Error | | --------- | -------------------------------------------------------------------------- | ---------------- | -------- | -------- | --------- | ------------------------------------------------------------- | | DALP-9001 | Access control bad confirmation | — | internal | error | No | `AccessControlBadConfirmation()` | | DALP-9002 | Access control enforced default admin delay | — | internal | error | No | `AccessControlEnforcedDefaultAdminDelay(uint48)` | | DALP-9003 | Access control enforced default admin rules | — | internal | error | No | `AccessControlEnforcedDefaultAdminRules()` | | DALP-9004 | Access control invalid default admin | — | internal | error | No | `AccessControlInvalidDefaultAdmin(address)` | | DALP-9005 | Access control missing any of roles | — | internal | error | No | `AccessControlMissingAnyOfRoles(address,bytes32,bytes32)` | | DALP-9006 | Address empty code | — | internal | error | No | `AddressEmptyCode(address)` | | DALP-9007 | Checkpoint unordered insertion | — | internal | error | No | `CheckpointUnorderedInsertion()` | | DALP-9008 | ECDSA invalid signature | — | internal | error | No | `ECDSAInvalidSignature()` | | DALP-9009 | ECDSA invalid signature length | — | internal | error | No | `ECDSAInvalidSignatureLength(uint256)` | | DALP-9010 | ECDSA invalid signature s | — | internal | error | No | `ECDSAInvalidSignatureS(bytes32)` | | DALP-9011 | ERC 1155 insufficient balance | — | internal | error | No | `ERC1155InsufficientBalance(address,uint256,uint256,uint256)` | | DALP-9012 | ERC 1155 invalid approver | — | internal | error | No | `ERC1155InvalidApprover(address)` | | DALP-9013 | ERC 1155 invalid array length | — | internal | error | No | `ERC1155InvalidArrayLength(uint256,uint256)` | | DALP-9014 | ERC 1155 invalid operator | — | internal | error | No | `ERC1155InvalidOperator(address)` | | DALP-9015 | ERC 1155 invalid receiver | — | internal | error | No | `ERC1155InvalidReceiver(address)` | | DALP-9016 | ERC 1155 invalid sender | — | internal | error | No | `ERC1155InvalidSender(address)` | | DALP-9017 | ERC 1155 missing approval for all | — | internal | error | No | `ERC1155MissingApprovalForAll(address,address)` | | DALP-9018 | ERC 1967 invalid implementation | — | internal | error | No | `ERC1967InvalidImplementation(address)` | | DALP-9019 | ERC 1967 non payable | — | internal | error | No | `ERC1967NonPayable()` | | DALP-9020 | ERC 1967 proxy uninitialized | — | internal | error | No | `ERC1967ProxyUninitialized()` | | DALP-9021 | ERC 20 exceeded safe supply | — | internal | error | No | `ERC20ExceededSafeSupply(uint256,uint256)` | | DALP-9022 | ERC 20 invalid approver | — | internal | error | No | `ERC20InvalidApprover(address)` | | DALP-9023 | ERC 20 invalid receiver | — | internal | error | No | `ERC20InvalidReceiver(address)` | | DALP-9024 | ERC 20 invalid sender | — | internal | error | No | `ERC20InvalidSender(address)` | | DALP-9025 | ERC 20 invalid spender | — | internal | error | No | `ERC20InvalidSpender(address)` | | DALP-9026 | This operation can no longer be completed because the deadline has expired | — | internal | error | No | `ERC2612ExpiredSignature(uint256)` | | DALP-9027 | ERC 2612 invalid signer | — | internal | error | No | `ERC2612InvalidSigner(address,address)` | | DALP-9028 | This operation can no longer be completed because the deadline has expired | — | internal | error | No | `ERC2771ForwarderExpiredRequest(uint48)` | | DALP-9029 | ERC 2771 forwarder invalid signer | — | internal | error | No | `ERC2771ForwarderInvalidSigner(address,address)` | | DALP-9030 | ERC 2771 forwarder mismatched value | — | internal | error | No | `ERC2771ForwarderMismatchedValue(uint256,uint256)` | | DALP-9031 | ERC 2771 untrustful target | — | internal | error | No | `ERC2771UntrustfulTarget(address,address)` | | DALP-9032 | ERC 5805 future lookup | — | internal | error | No | `ERC5805FutureLookup(uint256,uint48)` | | DALP-9033 | ERC 6372 inconsistent clock | — | internal | error | No | `ERC6372InconsistentClock()` | | DALP-9034 | ERC 721 incorrect owner | — | internal | error | No | `ERC721IncorrectOwner(address,uint256,address)` | | DALP-9035 | ERC 721 insufficient approval | — | internal | error | No | `ERC721InsufficientApproval(address,uint256)` | | DALP-9036 | ERC 721 invalid approver | — | internal | error | No | `ERC721InvalidApprover(address)` | | DALP-9037 | ERC 721 invalid operator | — | internal | error | No | `ERC721InvalidOperator(address)` | | DALP-9038 | ERC 721 invalid owner | — | internal | error | No | `ERC721InvalidOwner(address)` | | DALP-9039 | ERC 721 invalid receiver | — | internal | error | No | `ERC721InvalidReceiver(address)` | | DALP-9040 | ERC 721 invalid sender | — | internal | error | No | `ERC721InvalidSender(address)` | | DALP-9041 | ERC 721 nonexistent token | — | internal | error | No | `ERC721NonexistentToken(uint256)` | | DALP-9042 | ERC 7579 already installed module | — | internal | error | No | `ERC7579AlreadyInstalledModule(uint256,address)` | | DALP-9043 | ERC 7579 cannot decode fallback data | — | internal | error | No | `ERC7579CannotDecodeFallbackData()` | | DALP-9044 | ERC 7579 decoding error | — | internal | error | No | `ERC7579DecodingError()` | | DALP-9045 | ERC 7579 mismatched module type id | — | internal | error | No | `ERC7579MismatchedModuleTypeId(uint256,address)` | | DALP-9046 | ERC 7579 missing fallback handler | — | internal | error | No | `ERC7579MissingFallbackHandler(bytes4)` | | DALP-9047 | ERC 7579 multisig already exists | — | internal | error | No | `ERC7579MultisigAlreadyExists(bytes)` | | DALP-9048 | ERC 7579 multisig invalid init data | — | internal | error | No | `ERC7579MultisigInvalidInitData()` | | DALP-9049 | ERC 7579 multisig invalid signer | — | internal | error | No | `ERC7579MultisigInvalidSigner(bytes)` | | DALP-9050 | ERC 7579 multisig invalid weight | — | internal | error | No | `ERC7579MultisigInvalidWeight(bytes,uint64)` | | DALP-9051 | ERC 7579 multisig mismatched length | — | internal | error | No | `ERC7579MultisigMismatchedLength()` | | DALP-9052 | ERC 7579 multisig nonexistent signer | — | internal | error | No | `ERC7579MultisigNonexistentSigner(bytes)` | | DALP-9053 | ERC 7579 multisig unreachable threshold | — | internal | error | No | `ERC7579MultisigUnreachableThreshold(uint64,uint64)` | | DALP-9054 | ERC 7579 multisig zero threshold | — | internal | error | No | `ERC7579MultisigZeroThreshold()` | | DALP-9055 | ERC 7579 uninstalled module | — | internal | error | No | `ERC7579UninstalledModule(uint256,address)` | | DALP-9056 | ERC 7579 unsupported call type | — | internal | error | No | `ERC7579UnsupportedCallType(bytes1)` | | DALP-9057 | ERC 7579 unsupported exec type | — | internal | error | No | `ERC7579UnsupportedExecType(bytes1)` | | DALP-9058 | ERC 7579 unsupported module type | — | internal | error | No | `ERC7579UnsupportedModuleType(uint256)` | | DALP-9059 | Expected pause | — | internal | error | No | `ExpectedPause()` | | DALP-9060 | Failed call | — | internal | error | No | `FailedCall()` | | DALP-9061 | Your account does not have enough resources for this operation | — | internal | error | No | `InsufficientBalance(uint256,uint256)` | | DALP-9062 | Ownable invalid owner | — | internal | error | No | `OwnableInvalidOwner(address)` | | DALP-9063 | Reentrancy guard reentrant call | — | internal | error | No | `ReentrancyGuardReentrantCall()` | | DALP-9064 | Safe cast overflowed uint downcast | — | internal | error | No | `SafeCastOverflowedUintDowncast(uint8,uint256)` | | DALP-9065 | Safe erc 20 failed operation | — | internal | error | No | `SafeERC20FailedOperation(address)` | | DALP-9066 | Strings insufficient hex length | — | internal | error | No | `StringsInsufficientHexLength(uint256,uint256)` | | DALP-9067 | UUPS unauthorized call context | — | internal | error | No | `UUPSUnauthorizedCallContext()` | | DALP-9068 | UUPS unsupported proxiable uuid | — | internal | error | No | `UUPSUnsupportedProxiableUUID(bytes32)` | *** ## Summary [#summary] | Metric | Count | | ------------------------------- | ----- | | Total errors | 644 | | Curated (hand-written messages) | 39 | | Templated (pattern-matched) | 192 | | Scaffolded (auto-generated) | 413 | {/* ENTRY_COUNT:644 */} # Error handling Source: https://docs.settlemint.com/docs/developer-guides/api-integration/error-handling Handle API errors with retry strategies, error code references, and clear failure handling for DALP integrations. DALP API errors include machine-readable codes. Use those codes to fix the request, retry with backoff, or show users a clear message. ## Error response format [#error-response-format] All API errors follow a consistent JSON structure: ```json { "code": "USER_NOT_AUTHORIZED", "status": 403, "message": "User does not have the required role to execute this action.", "data": { "requiredRoles": ["SUPPLY_MANAGEMENT_ROLE"] } } ``` | Field | Type | Description | | --------- | --------- | ---------------------------------------------------------- | | `code` | `string` | Machine-readable error identifier (SCREAMING\_SNAKE\_CASE) | | `status` | `number` | HTTP status code | | `message` | `string` | Human-readable error description | | `data` | `object?` | Optional additional context (varies by error type) | The `data` field provides error-specific details. For example, `USER_NOT_AUTHORIZED` includes `requiredRoles`, while `INPUT_VALIDATION_FAILED` includes an `errors` array. *** ## Quick reference [#quick-reference] | Code | Status | Retry? | Action | | --------------------------------------------------------------------------- | ------ | ------ | -------------------------------------------------------------------------------------------------- | | `BAD_REQUEST` | 400 | No | Fix request payload | | `UNAUTHORIZED` | 401 | No | Reauthenticate | | `FORBIDDEN` | 403 | No | Check role permissions | | `NOT_ONBOARDED` | 403 | No | [Complete user onboarding](/docs/user-guides/user-management/user-onboarding) | | `SYSTEM_NOT_CREATED` | 403 | No | [Initialize platform first](/docs/user-guides/platform-setup/first-admin-setup) | | `USER_NOT_AUTHORIZED` | 403 | No | Request required role | | `NOT_FOUND` | 404 | No | Verify resource exists | | `CONFLICT` | 409 | No | Resolve state conflict | | `RESTATE_CALL_CONFLICT` | 409 | No | Wait for the active workflow step, approval monitor, or quorum workflow to finish before retrying | | `RESOURCE_ALREADY_EXISTS` | 409 | No | Use existing resource | | `INPUT_VALIDATION_FAILED` | 422 | No | Fix validation errors | | `TOKEN_PRECHECKS_INVALID_ADDRESS_VALID_ETHEREUM_0X_PREFIXED_HEX_CHARACTERS` | 400 | No | Provide a valid 0x-prefixed Ethereum address for token and holder pre-checks | | `TOKEN_INTERFACE_NOT_SUPPORTED` | 422 | No | Use compatible token contract | | `CONTRACT_ERROR` | 422 | Check | Read `data.retryable` and `data.dalpCode`; fix non-retryable contract errors before retrying | | `LUNA_MOFN_QUORUM_EXPIRED` | 408 | Yes | Activate the Luna partition, then resubmit the transaction | | `LUNA_MOFN_QUORUM_CLASSIFICATION_FAILED` | 409 | No | Inspect the Luna partition state directly before retrying | | `INTERNAL_SERVER_ERROR` | 500 | Yes | Retry with exponential backoff; contact support if persistent | | `CONFIRMATION_TIMEOUT` | 504 | No | [Check transaction status](/docs/developer-guides/operations/transaction-tracking) before retrying | *** ## Retry decision flowchart [#retry-decision-flowchart] Use this flowchart to determine whether to retry a failed request: **Key principles:** * **4xx errors**: Do not retry, except for retryable `CONTRACT_ERROR` responses or `LUNA_MOFN_QUORUM_EXPIRED`. For retryable `CONTRACT_ERROR` responses, inspect `data.dalpCode` and any `data.suggestedAction`, then resolve the required condition. For Luna quorum expiry, activate the partition before resubmitting. * **5xx errors**: Retry with exponential backoff, unless it's a blockchain revert. * **Blockchain reverts**: Check the revert reason, retrying won't help if the underlying issue persists. *** ## Client errors (4xx) [#client-errors-4xx] Client errors usually indicate problems with the request itself. Retrying the same request will produce the same error unless the response is a retryable `CONTRACT_ERROR` or `LUNA_MOFN_QUORUM_EXPIRED`. Fix the underlying issue before retrying non-retryable client errors. ### Authentication errors (401) [#authentication-errors-401] **`UNAUTHORIZED`** Authentication is missing or invalid. The API key may be expired, revoked, or malformed. * Verify the API key includes the `sm_dalp_` prefix * Check the key hasn't been deleted in the API Keys page * Confirm the `X-Api-Key` header is set correctly ### Authorization errors (403) [#authorization-errors-403] **`FORBIDDEN`** The authenticated user lacks permission for this operation. * Review the user's assigned roles * Check if the operation requires admin or system-level permissions * See [Platform setup](/docs/developer-guides/platform-setup/add-admins) for role management **`NOT_ONBOARDED`** The user hasn't completed the onboarding process. * Direct the user to complete onboarding in the platform UI * Onboarding includes profile setup and wallet configuration * See [User onboarding](/docs/user-guides/user-management/user-onboarding) for the complete flow **`SYSTEM_NOT_CREATED`** The DALP platform hasn't been initialized. This occurs when accessing a fresh deployment before the first admin completes setup. * Follow the [First admin setup](/docs/developer-guides/platform-setup/first-admin-setup) guide **`USER_NOT_AUTHORIZED`** The user lacks the specific role required for this token operation. The `data.requiredRoles` field lists what's needed (e.g., `SUPPLY_MANAGEMENT_ROLE`, `USER_MANAGEMENT_ROLE`). * Check [Asset admin roles](/docs/developer-guides/asset-servicing/change-asset-admin-roles) for role assignment ### Resource errors (404, 409) [#resource-errors-404-409] **`NOT_FOUND`** The requested resource doesn't exist. * Verify the resource ID or address is correct * Check if the resource was deleted * Confirm the API path is correct (include `/api` suffix in base URL) **`CONFLICT`** The operation conflicts with the current system state. * Check if another operation is in progress * Verify the resource state hasn't changed since your last read **`RESOURCE_ALREADY_EXISTS`** Attempted to create a resource that already exists. * Query for the existing resource instead of creating * Use a unique identifier if creating a new resource ### Workflow and custody approval conflicts [#workflow-and-custody-approval-conflicts] **`RESTATE_CALL_CONFLICT`** A workflow-backed operation returned HTTP 409 because the current workflow or resource state does not allow this call yet. Custody-backed signing is one subcase: the request may be waiting for a policy approval, a nonce reservation, or a Luna m-of-n quorum workflow. * Refresh the resource, deployment, or transaction status before submitting another write * Do not retry immediately with a different idempotency key * For custody-backed signing, wait for the active approval or quorum step to finish * Retry only after the required workflow, approval, or resource state has changed **`LUNA_MOFN_QUORUM_EXPIRED`** A Thales Luna 7 partition was waiting for m-of-n approval and the configured signing window expired before the operator quorum approved it. * Activate the partition on the HSM * Resubmit the transaction after approval * If this happens repeatedly, review the configured signing window with your platform operator **`LUNA_MOFN_QUORUM_CLASSIFICATION_FAILED`** DALP saw the Luna partition report as activated, but the signing call still returned m-of-n pending. * Inspect the partition state on the HSM directly * Retry only after the operator quorum is actually active * Contact support with the request ID if the HSM state and API response disagree ### Token pre-check address errors (400) [#token-pre-check-address-errors-400] **`TOKEN_PRECHECKS_INVALID_ADDRESS_VALID_ETHEREUM_0X_PREFIXED_HEX_CHARACTERS`** Transfer and burn pre-checks validate the token address and each holder address before reading indexed balances. The API returns this error when the request contains an invalid 0x-prefixed Ethereum address. * Fix the token or holder address in the request before retrying * Do not retry the same payload with backoff * After fixing the address, handle any balance, freeze, or indexing state errors separately ### Validation errors (422) [#validation-errors-422] **`INPUT_VALIDATION_FAILED`** Request data failed schema validation. The `data.errors` array lists specific issues. ```json { "code": "INPUT_VALIDATION_FAILED", "status": 422, "message": "Input validation failed", "data": { "errors": ["amount: Expected positive number, received -100", "recipient: Invalid Ethereum address"] } } ``` Review each error and fix the corresponding field in your request. **`TOKEN_INTERFACE_NOT_SUPPORTED`** The token contract at the specified address doesn't implement the required interface. The `data.requiredInterfaces` field lists the missing interfaces (e.g., `ERC20`, `IYieldSchedule`). * Verify you're using the correct token address * Check if the token type supports the requested operation *** ## Server errors (5xx) [#server-errors-5xx] Server errors indicate temporary problems. Most can be resolved by retrying with exponential backoff. ### General server errors (500) [#general-server-errors-500] **`INTERNAL_SERVER_ERROR`** An unexpected error occurred on the server. * Retry with exponential backoff (1s, 2s, 4s delays) * Maximum 3 retry attempts * If errors persist, contact support with the request details ### Timeout errors (504) [#timeout-errors-504] **`CONFIRMATION_TIMEOUT`** A blockchain transaction was submitted but confirmation timed out. The transaction may still succeed, do not retry the original API call. **Do not retry** the original request, this may create duplicate transactions. Check the transaction status first to determine whether to retry. * The `data.transactionHash` field in the error contains the transaction hash * Query `GET /api/transaction/{transactionHash}` to check if the transaction succeeded, reverted, or was never submitted * See [Transaction tracking](/docs/developer-guides/operations/transaction-tracking) for the full timeout recovery flow *** ## Blockchain transaction errors [#blockchain-transaction-errors] When a blockchain transaction reverts, the API returns the revert information in the error details. Revert reasons come directly from smart contract custom errors. **Important:** Blockchain reverts should not be retried without fixing the underlying issue. The same transaction will revert again. ### Common revert reasons [#common-revert-reasons] These errors occur frequently during normal operations and typically require user action or data correction. #### Balance and supply errors [#balance-and-supply-errors] | Error | Description | Resolution | | -------------------------- | -------------------------------------------------- | ------------------------------------------------------------ | | `InsufficientTokenBalance` | Account lacks sufficient token balance | Query balance before retrying; ensure user has enough tokens | | `ExceededCap` | Minting would exceed the token's supply cap | Reduce mint amount or increase cap (if authorized) | | `InsufficientCollateral` | Insufficient collateral backing for mint operation | Add more collateral before minting | #### Permission and authorization errors [#permission-and-authorization-errors] | Error | Description | Resolution | | ---------------------------------- | --------------------------------------------------------- | ------------------------------------------------ | | `AccessControlUnauthorizedAccount` | Account lacks the required role for this operation | Grant the required role to the account | | `TransferNotCompliant` | Transfer failed compliance checks | Verify both parties meet compliance requirements | | `MintNotCompliant` | Mint operation failed compliance checks | Verify recipient meets compliance requirements | | `RecipientNotVerified` | Recipient doesn't meet identity verification requirements | Complete identity verification for recipient | | `ApprovalRequired` | Transfer requires pre-approval from authorized party | Request transfer approval before executing | #### Token state errors [#token-state-errors] | Error | Description | Resolution | | ------------------------ | ------------------------------------ | ------------------------------------- | | `TokenPaused` | Token operations are paused by admin | Wait for admin to unpause the token | | `SenderAddressFrozen` | Sender's address is frozen | Contact admin to unfreeze the address | | `RecipientAddressFrozen` | Recipient's address is frozen | Contact admin to unfreeze the address | #### Identity and compliance errors [#identity-and-compliance-errors] | Error | Description | Resolution | | --------------------------- | ----------------------------------------------- | ------------------------------------------------ | | `IdentityNotRegistered` | User's identity is not registered in the system | Register identity through onboarding flow | | `IdentityAlreadyRegistered` | Identity already exists for this user | Use existing identity instead of creating new | | `ComplianceCheckFailed` | Generic compliance check failure | Review compliance requirements for the operation | #### Validation errors [#validation-errors] | Error | Description | Resolution | | ----------------------- | ----------------------------------------------- | ----------------------------------- | | `ZeroAddressNotAllowed` | Zero address provided where non-zero required | Provide a valid non-zero address | | `LengthMismatch` | Array lengths don't match in batch operations | Ensure all arrays have equal length | | `InvalidDecimals` | Token decimals value is invalid (typically >18) | Use valid decimals value (0-18) | ### Deployment workflow errors [#deployment-workflow-errors] Long-running deployment workflows can fail after the initial request has been accepted. REST deployment endpoints return a `422 CONTRACT_ERROR` envelope when they need to expose a deployment workflow failure to the client. Deployment SSE streams expose the same public contract-error data when they report failed deployment steps. The envelope is designed for client handling and user-facing support flows: ```json { "code": "CONTRACT_ERROR", "status": 422, "message": "Contract operation failed", "data": { "dalpCode": "DALP-WORKFLOW-FAILED", "message": "The deployment workflow failed before it could finish.", "retryable": true, "selector": "0x00000000", "solidityError": "WorkflowFailed()", "correlationId": "deployment-id" } } ``` This is one deployment `CONTRACT_ERROR` surface exposed through both deployment transports: * **REST deployment endpoints:** return `422 CONTRACT_ERROR` with `data.dalpCode` and `data.correlationId` when a deployment workflow failure must be projected to the caller. * **SSE deployment streams:** report the same deployment error surface in failed deployment completions under `complete.failedSteps`; each failed step can include a decoded `wireError`. Use `data.dalpCode` for REST programmatic handling and `data.correlationId` when asking support to investigate a failed deployment. Each SSE failed step's `wireError` uses the same public `CONTRACT_ERROR.data` shape shown above. The older `failedSteps[].error` field is legacy text and mirrors the sanitized public `wireError.message`; new clients should read `failedSteps[].wireError` for structured handling. The deployment error payload intentionally contains public troubleshooting fields only. It does not expose raw provider responses, RPC URLs, stack traces, contract call context, or nested cause-chain diagnostics. Operators can still investigate those diagnostics through logs and traces. Client-facing API responses only include the public envelope. Typed platform errors, such as feature-gating or state conflicts, keep their normal error codes instead of being converted into `CONTRACT_ERROR`. Handle those errors according to the quick reference above. For REST responses, base retry behavior on the public fields in `data`: `data.retryable`, `data.dalpCode`, and `data.suggestedAction` when present. For SSE deployment stream failures, read those values from the failed-step payload: `complete.failedSteps[].wireError.retryable`, `complete.failedSteps[].wireError.dalpCode`, and `complete.failedSteps[].wireError.suggestedAction` when present. *** ## Retry strategies [#retry-strategies] ### Exponential backoff [#exponential-backoff] For 5xx errors (except blockchain reverts), use exponential backoff: | Attempt | Wait time | | ------- | --------- | | 1 | 1 second | | 2 | 2 seconds | | 3 | 4 seconds | | 4+ | Fail | Add jitter (random 0-500ms) to prevent thundering herd problems when multiple clients retry simultaneously. ### Transaction confirmation [#transaction-confirmation] For operations that submit blockchain transactions: 1. The API polls for confirmation automatically 2. If `CONFIRMATION_TIMEOUT` occurs, **do not retry** the original request 3. Query `GET /api/transaction/{transactionHash}` to check if the transaction was processed 4. The transaction hash (available in `X-Transaction-Hash` response header or error response) can be used to verify on-chain status 5. Only submit a new request after confirming the original transaction failed **Multi-transaction operations:** Some API calls submit more than one blockchain transaction. Raw HTTP responses emit one `X-Transaction-Hash` header per transaction hash. Typed clients should use `meta.txHashes` as the canonical list because repeated headers can be harder to consume consistently. Treat each hash as a transaction to poll. For parallel submission paths, do not infer completion from list order or assume a timeout applies only to the last hash. See [Transaction tracking](/docs/developer-guides/operations/transaction-tracking) for detailed polling patterns and response interpretation. *** ## Best practices [#best-practices] ### Log errors with context [#log-errors-with-context] Include the error code, request path, and relevant IDs in your logs. This enables faster debugging when issues occur in production. ### Set reasonable timeouts [#set-reasonable-timeouts] Configure client timeouts appropriate for the operation type: * Read operations: 10-30 seconds * Write operations: 60-90 seconds (blockchain transactions take time) ### Fail fast on client errors [#fail-fast-on-client-errors] Don't retry 4xx errors unless the response is a retryable `CONTRACT_ERROR` or `LUNA_MOFN_QUORUM_EXPIRED`. For retryable `CONTRACT_ERROR` responses, inspect `data.dalpCode` and any `data.suggestedAction`. Retry only after the required user action, approval, or external state change is complete. Surface non-retryable client errors to users or fix them programmatically. ### Circuit breaker for repeated failures [#circuit-breaker-for-repeated-failures] If a specific endpoint fails repeatedly (5+ times in a minute), pause requests to that endpoint temporarily. This prevents cascading failures and allows the service to recover. ### Idempotency for critical operations [#idempotency-for-critical-operations] For financial operations, ensure your integration can handle duplicate responses safely. Network issues may cause a successful request to appear failed, leading to retry attempts on already-completed operations. *** ## Next steps [#next-steps] * [Getting started](/docs/developer-guides/api-integration/getting-started) – Set up API authentication * [Token lifecycle](/docs/developer-guides/api-integration/token-lifecycle) – Understand operation flows * [API reference](/docs/developer-guides/api-integration/api-reference) – Complete endpoint documentation # External tokens Source: https://docs.settlemint.com/docs/developer-guides/api-integration/external-tokens Register and list tokens that were not deployed through DALP factories so they can be tracked from the external token registry. External tokens are token contracts deployed outside the active DALP system's asset factories. Registration adds an existing token to the system's external token registry. After registration, DALP can list the token, index registration metadata, and route users to token detail and event views. Use this flow when you need DALP to track an existing EVM token alongside assets created in DALP. ## Prerequisites [#prerequisites] * The active system has an external token registry configured. * The caller has the `tokenManager` system role required to register tokens. * The token contract address is on the active EVM network. * API-key requests can omit `walletVerification`; user-session requests that sign with a wallet must include wallet verification for that transaction. ## Register an external token [#register-an-external-token] Submit the token contract address and the token type you want DALP to assign to it. API-key auth callers do not need to send `walletVerification`: ```http POST /api/v2/external-tokens Content-Type: application/json { "tokenAddress": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F", "tokenType": "stablecoin" } ``` When registering through a user session that requires wallet verification, include the wallet verification payload with the request: ```json { "tokenAddress": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F", "tokenType": "stablecoin", "walletVerification": { "secretVerificationCode": "123456", "verificationType": "PINCODE" } } ``` The registration is an on-chain transaction. DALP queues the registry call and returns an asynchronous blockchain mutation response with the registered token address and transaction status information. The dApp exposes the same flow from **External Tokens**. Select **Register External Token**, enter the token address, choose a token type such as bond, equity, fund, stablecoin, deposit, cryptocurrency, or other, then complete wallet verification. ## List registered external tokens [#list-registered-external-tokens] Read registered external tokens with: ```http GET /api/v2/external-tokens ``` The response uses the standard paginated collection shape with `data`, `meta`, and `links`. Each row can include: * contract address, name, symbol, decimals, and total supply * assigned token type * paused or active status when the token exposes pausable state * registration timestamp and registering account when indexed registration events are available * detected interfaces when the token reports them, including SMART and ERC-3643 indicators The list route supports pagination, sorting, filters, type and status facets, and search across token name, symbol, and assigned type. The default dApp table sorts by registration time and lets users filter, export, copy the address, open the configured block explorer, or navigate to the external token detail page. ## Operational notes [#operational-notes] * Registration does not deploy a new token contract. It records an existing EVM token in the registry. * DALP assigns the provided token type during registration; it does not infer the asset class from the address alone. * SMART and ERC-3643 indicators are shown only when detected interface data is available. * If the external token registry is missing from the active system, registration fails with `DALP-0122`; REST clients see it as `error.id`, and oRPC JSON-RPC clients see it as `data.dapiError.id`. See the [DAPI error reference](/docs/developer-guides/api-integration/dapi-error-reference). Related pages: * [Token lifecycle](/docs/developer-guides/api-integration/token-lifecycle) * [CLI command reference](/docs/developer-guides/cli/command-reference) * [Supported networks](/docs/architecture/integrations/supported-networks) # Getting started Source: https://docs.settlemint.com/docs/developer-guides/api-integration/getting-started Create an API key and configure the OpenAPI TypeScript client to authenticate with the DALP platform and start making API calls. This guide walks you through creating an API key in the DALP UI and configuring the OpenAPI TypeScript client for programmatic access to the platform. Within 10 minutes you will authenticate, fetch your user profile, and be ready to deploy tokens. The DALP API enables you to: * **Automate issuance workflows** – Create tokens, assign roles, configure compliance modules, and mint initial supply in one script * **Integrate compliance checks** – Verify investor identities, enforce transfer restrictions, and manage allowlists from your own systems * **Build custom dashboards** – Query token holders, balances, transaction history, and compliance status for reporting * **Trigger corporate actions** – Distribute dividends, process redemptions, adjust yield schedules, and handle maturities programmatically * **Manage multi-token portfolios** – Track NAV across fund holdings, rebalance allocations, and reconcile deposits/withdrawals * **Enforce custody controls** – Freeze addresses, execute forced transfers, and recover tokens under regulatory or operational requirements ## Prerequisites [#prerequisites] Before creating an API key: 1. **DALP deployment** – Have a running DALP instance (local or hosted) 2. **DALP account** – Sign up through the platform UI with email and password 3. **Wallet verification enabled** (session-based auth only) – Required for session-cookie authentication; API key auth skips verification automatically 4. **Organization membership** – Join or create an organization to scope your API operations 5. **Admin role** (for role grants) – Your account needs `admin` role to grant system-level permissions Your API key inherits the permissions of your user account and is scoped to a single organization. If you belong to multiple organizations, create separate API keys for each one. *** ## API integration workflow [#api-integration-workflow] This diagram shows the complete flow from prerequisites through authentication to making API calls: **Key steps:** * **Generate SDK** – Run `@hey-api/openapi-ts` to create type-safe client from OpenAPI spec * **Initialize client** – Configure base URL and API key header once at startup * **Test connection** – Verify authentication with `userMe()` before proceeding * **Read operations** – Token lists, holder queries, system info (no verification required) * **Write operations** – Minting, role grants, token creation (no wallet verification required when using API key auth) *** ## Create an API key [#create-an-api-key] ### Step 1: Navigate to API Keys page [#step-1-navigate-to-api-keys-page] 1. Click your profile avatar in the top right corner 2. Select **API Keys** from the dropdown menu API Keys page ### Step 2: Generate a new key [#step-2-generate-a-new-key] 1. Click **Create API Key** 2. Enter a descriptive name (e.g., "Production Integration", "CI/CD Pipeline") 3. (Optional) Set an expiry date – keys without expiry remain valid until manually revoked 4. Click **Create** ### Step 3: Copy and secure your key [#step-3-copy-and-secure-your-key] The platform displays your API key **once**. Copy it immediately and store it securely (environment variable, secret manager, password vault). **Key format**: `sm_dalp_xxxxxxxxxxxxxxxx` **Important**: If you lose the key, you cannot recover it. Revoke the old key and create a new one. ### Step 4: Review key settings [#step-4-review-key-settings] Your API key has: * **Permissions**: Inherits your user role permissions (check with `systemAccessManagerRolesList()`) * **Organization scope**: Locked to the organization active when you created the key * **Metadata**: Stores `organizationId` for session context ### Managing API keys [#managing-api-keys] From the API Keys page you can: * **View active keys** – Name, expiry date * **Delete keys** – Permanently revoke access (cannot be undone) *** ## Configure the OpenAPI client [#configure-the-openapi-client] The recommended way to interact with the DALP API is through the **generated OpenAPI TypeScript client**, which provides full type safety and auto-completion for all API endpoints. ### Generate the SDK [#generate-the-sdk] Use `@hey-api/openapi-ts` to generate a type-safe client from the OpenAPI specification: ```bash npm install @hey-api/client-fetch npx @hey-api/openapi-ts -i https://your-platform.example.com/openapi.json -o ./generated -c @hey-api/client-fetch ``` Or with Bun: ```bash bun add @hey-api/client-fetch bunx @hey-api/openapi-ts -i https://your-platform.example.com/openapi.json -o ./generated -c @hey-api/client-fetch ``` This generates three files in `./generated/`: * `client.gen.ts` – HTTP client configuration * `sdk.gen.ts` – Type-safe API functions for each endpoint * `types.gen.ts` – TypeScript types for all request/response schemas ### Create the client wrapper [#create-the-client-wrapper] Create a `client.ts` file that wraps the generated SDK with initialization and helper functions: ```ts file=/src/examples/client.ts import { createDalpClient } from "@settlemint/dalp-sdk"; // Create the SDK client — replace with your actual values const dalp = createDalpClient({ url: "https://your-platform.example.com", apiKey: "sm_dalp_xxxxxxxxxxxxxxxx", }); // All methods are fully typed with auto-complete const me = await dalp.user.me({}); console.log("Wallet:", me.data.wallet); const tokens = await dalp.token.list({ query: {} }); console.log("Tokens:", tokens.data.length); ``` **What this provides**: * **`initializeClient(baseUrl, apiKey)`** – One-time setup to configure the base URL and API key header * **SDK functions** – All API endpoints as importable functions (e.g., `userMe`, `tokenCreate`, `tokenMint`) * **BigDecimal helpers** – `toBigDecimal()` and `fromBigDecimal()` for precise numeric values * **Type exports** – All TypeScript types for request/response schemas ### Initialize the client [#initialize-the-client] ```ts import { initializeClient, userMe, tokenList } from "./client"; const platformUrl = "https://your-platform.example.com/api"; const apiKey = "sm_dalp_xxxxxxxxxxxxxxxx"; // From Step 3 // Initialize once at app startup initializeClient(platformUrl, apiKey); ``` Replace: * `platformUrl` – Your DALP deployment URL + `/api` path * `apiKey` – The key you copied in Step 3 *** ## Test your connection [#test-your-connection] Verify authentication by fetching your user profile: ```ts const meResponse = await userMe(); console.log("User ID:", meResponse.data?.id); console.log("Email:", meResponse.data?.email); console.log("Wallet:", meResponse.data?.wallet); console.log("Organization:", meResponse.data?.organization?.name); ``` **Expected output**: ```json { "id": "clxxx...", "email": "you@example.com", "wallet": "0x1234...", "organization": { "id": "org_xxx", "name": "Acme Corp" } } ``` If you receive this response, your API key is working correctly. ### Common errors [#common-errors] For complete error handling guidance, see the [Error handling guide](/docs/developer-guides/api-integration/error-handling). **Quick fixes:** * **401 Unauthorized** – API key is invalid or expired. Check the key includes the `sm_dalp_` prefix and is enabled in the API Keys page. * **403 Forbidden** – Your user account lacks permissions. See [Platform setup](/docs/developer-guides/platform-setup/add-admins) for role management. * **404 Not Found** – Check the `platformUrl` includes `/api` suffix and your DALP deployment is running. *** ## Authentication header formats [#authentication-header-formats] DALP accepts API keys in following header format: **X-Api-Key** (recommended): ```http X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx ``` *** ## Wallet verification for write operations [#wallet-verification-for-write-operations] When authenticating with an **API key**, wallet verification is **not required**. You can omit the `walletVerification` field entirely: ```ts import { tokenMint, toBigDecimal } from "./client"; // API key auth — no walletVerification needed await tokenMint({ path: { tokenAddress: "0xABCD..." }, body: { recipients: ["0x1234..."], amounts: [toBigDecimal("1000", 18)], }, }); ``` When authenticating with a **session cookie** (browser-based), write operations still require wallet verification: ```ts // Session-based auth — walletVerification required await tokenMint({ path: { tokenAddress: "0xABCD..." }, body: { recipients: ["0x1234..."], amounts: [toBigDecimal("1000", 18)], walletVerification: { verificationType: "PINCODE", secretVerificationCode: "123456", // Your 6-digit PINCODE }, }, }); ``` **Why the difference?** API keys are scoped, rate-limited credentials designed for machine-to-machine use. The key is the authorization factor, so an interactive second challenge is unnecessary. Session-based authentication still requires wallet verification as a second factor to prevent unauthorized transactions from a compromised session. *** ## API architecture [#api-architecture] The DALP API is built on **oRPC**, which provides: * **Type-safe client generation** – Auto-generated TypeScript clients with full IntelliSense support * **Automatic OpenAPI documentation** – Explore endpoints, schemas, and examples at `/api/` * **Schema validation** – Requests and responses validated against Zod schemas * **Custom serializers** – BigInt, BigDecimal, and Timestamp types serialized correctly over JSON * **Transaction headers** – Blockchain mutations return `X-Transaction-Hash` in response headers for easy tracking * **Streaming support** – Long-running operations return progress updates via server-sent events All endpoints follow RESTful conventions with `POST` for mutations, `GET` for queries, and standardized error codes (401 for auth failures, 403 for permission denials, 404 for missing resources). *** ## Next steps [#next-steps] Now that your client is configured: 1. **[Review the token lifecycle](/docs/developer-guides/api-integration/token-lifecycle)** to understand operation flows 2. **[Set up roles](/docs/developer-guides/platform-setup/change-admin-roles)** to grant yourself system and token permissions 3. **Choose an asset guide** and deploy your first token: * [Bonds](/docs/developer-guides/runbooks/create-mint-bonds) * [Deposits](/docs/developer-guides/runbooks/create-mint-deposits) * [Equities](/docs/developer-guides/runbooks/create-mint-equities) * [Funds](/docs/developer-guides/runbooks/create-mint-funds) * [Stablecoins](/docs/developer-guides/runbooks/create-mint-stablecoins) For API reference documentation and OpenAPI spec, see [API reference](/docs/developer-guides/api-integration/api-reference). # Identity recovery API Source: https://docs.settlemint.com/docs/developer-guides/api-integration/identity-recovery Preview, start, and monitor identity recovery workflows for users who lost access to a wallet. Use the identity recovery API when an integration needs to help an Identity manager recover a user's wallet access. Recovery creates replacement wallet and identity records, moves the user's active identity to the new wallet path, and reports token recovery progress through a workflow status endpoint. Identity recovery is an operator action for lost or compromised wallet access. It is not a general token transfer API, custody service, or claim migration tool. Trusted issuers may need to issue new claims for the recovered identity after the workflow completes. ## Endpoints [#endpoints] The identity recovery API exposes three endpoints: | Endpoint | Use it for | | -------------------------------------------------- | ----------------------------------------------------------------------------------- | | `GET /api/v2/identity-recoveries/{userId}/preview` | Check whether a user's wallet can be recovered and see the affected token balances. | | `POST /api/v2/identity-recoveries` | Submit the identity recovery workflow for a user. | | `GET /api/v2/identity-recoveries/{userId}/status` | Poll the workflow phase, recovered-token count, and any token recovery failures. | Preview and execute require a caller with the Identity manager recovery permission. Status polling is available to callers with that permission and to the caller that initiated the recovery workflow in the same active organisation. ## Preview recovery impact [#preview-recovery-impact] Call the preview endpoint before submitting recovery. If you know the lost wallet address, pass it as the optional `wallet` query parameter. When the request omits `wallet`, DALP uses the user's personal identity wallet. ```bash curl --globoff "$DALP_API_URL/api/v2/identity-recoveries/user_123/preview?wallet=0x1000000000000000000000000000000000000001" \ --header "X-Api-Key: $DALP_API_TOKEN" ``` The response shows the user, the wallet being recovered, the current identity status, token balances that need recovery, and blocking reasons when recovery cannot proceed. ```json { "data": { "user": { "id": "user_123", "email": "operator@example.com", "name": "Platform operator" }, "lostWallet": "0x1000000000000000000000000000000000000001", "identity": { "id": "0x2000000000000000000000000000000000000002", "status": "registered", "isMarkedAsLost": false }, "tokenBalances": [ { "tokenAddress": "0x3000000000000000000000000000000000000003", "tokenName": "Example Bond", "tokenSymbol": "EXB", "balance": "10.5", "balanceExact": "10500000000000000000", "decimals": 18 } ], "canRecover": true, "blockingReasons": [] } } ``` Do not execute recovery when `canRecover` is `false`. Resolve the listed blocking reasons first. ## Submit recovery [#submit-recovery] Submit recovery with the `userId` and, when needed, the specific lost wallet address. The executing administrator may also need to provide wallet verification. ```bash curl "$DALP_API_URL/api/v2/identity-recoveries" \ --request POST \ --header "X-Api-Key: $DALP_API_TOKEN" \ --header "Content-Type: application/json" \ --header "Prefer: respond-async" \ --data '{ "userId": "user_123", "wallet": "0x1000000000000000000000000000000000000001" }' ``` With `Prefer: respond-async`, a successful response means DALP accepted the workflow request. Use the returned `statusUrl` to track the transaction request. Continue to use the identity recovery status endpoint to track recovery phases. ```json { "transactionId": "txreq_123", "status": "QUEUED", "statusUrl": "/api/v2/transaction-requests/txreq_123" } ``` Without `Prefer: respond-async`, the request starts in synchronous mode. If the recovery finishes within the wait window, DALP returns the standard mutation envelope. ```json { "data": { "success": true }, "meta": { "txHashes": [] }, "links": { "self": "/v2/identity-recoveries" } } ``` Long-running recoveries can still continue asynchronously. If the synchronous wait times out, DALP returns the same accepted workflow shape as an explicit async request. Treat both response shapes as valid and poll the returned `statusUrl`, then use the identity recovery status endpoint for recovery phases. ## Poll recovery status [#poll-recovery-status] Poll the status endpoint until the workflow reaches `completed`, `completed-with-token-failures`, or `failed`. ```bash curl "$DALP_API_URL/api/v2/identity-recoveries/user_123/status" \ --header "X-Api-Key: $DALP_API_TOKEN" ``` The status response includes the current phase, token progress, the replacement wallet and identity when available, and a per-token failure manifest when recovery finished with token-level failures. ```json { "data": { "phase": "completed-with-token-failures", "tokensRecovered": 2, "totalTokens": 3, "error": null, "newWallet": "0x4000000000000000000000000000000000000004", "newIdentity": "0x5000000000000000000000000000000000000005", "tokenRecoveryFailures": [ { "tokenAddress": "0x3000000000000000000000000000000000000003", "holderAddress": "0x1000000000000000000000000000000000000001", "reason": "TOKEN_PAUSED", "message": "Token recovery failed because the token is paused.", "rawError": "execution reverted" } ] } } ``` Treat `completed-with-token-failures` as a partial success. The identity recovery link is in place, but the listed token balances still need operator follow-up. Typical token failure reasons are `TOKEN_PAUSED`, `MISSING_CUSTODIAN_ROLE`, `NO_TOKENS`, `RPC_ERROR`, and `UNKNOWN`. ## Operational notes [#operational-notes] * Use preview first so operators can see the affected wallet, identity status, balances, and blockers before submitting recovery. * Poll status after submit; the execute endpoint returns after the workflow is accepted. * Recheck identity and claim state after completion. KYC claims do not migrate automatically to the replacement identity. * A missing active workflow returns a not-found response to authorised callers. Unauthorised callers receive an authorisation error instead of a workflow existence signal. ## Related [#related] * [API reference](/docs/developer-guides/api-integration/api-reference) * [Error handling](/docs/developer-guides/api-integration/error-handling) * [User management](/docs/developer-guides/user-management/create-users) # KYC document uploads Source: https://docs.settlemint.com/docs/developer-guides/api-integration/kyc-document-uploads Upload, confirm, list, download, and delete KYC documents through the DALP API and SDK. KYC document uploads use a two-step file flow. Your integration first requests an upload URL for a draft KYC version, uploads the file to that URL, and then confirms the upload in DALP so the document is attached to the KYC version. Use this flow when investors or operators need to attach passports, identity cards, proof-of-address files, or other supporting evidence to a KYC submission. The document record belongs to a KYC version, not directly to the user profile. Submit the version only after the required profile fields and documents are in place. ## Supported document inputs [#supported-document-inputs] A document upload requires: * `versionId`: the KYC version that receives the document * `documentType`: one of `passport`, `drivers_license`, `national_id`, `proof_of_address`, or `other` * `fileName`: the original file name you want stored with the document record * `fileSize`: file size in bytes, up to 10 MiB * `mimeType`: one of `application/pdf`, `image/jpeg`, `image/png`, or `image/webp` If the latest KYC version is already submitted or under review, create a new draft version before requesting an upload URL. ## Upload flow [#upload-flow] The upload flow has three calls: 1. Request an upload URL with `user.kyc.documents.getUploadUrl`. 2. Upload the file bytes to the returned `uploadUrl` using the returned HTTP method. 3. Confirm the upload with `user.kyc.documents.confirmUpload` and the returned `objectKey`. Use the returned method and headers for the file upload. Some storage backends include provider-specific headers in the upload URL response. ```ts const upload = await client.user.kyc.documents.getUploadUrl({ params: { versionId: "ver_123" }, body: { documentType: "passport", fileName: "passport.pdf", fileSize: 204800, mimeType: "application/pdf", }, }); await fetch(upload.data.uploadUrl, { method: upload.data.method, headers: upload.data.headers, body: passportBytes, }); const document = await client.user.kyc.documents.confirmUpload({ params: { versionId: "ver_123" }, body: { objectKey: upload.data.objectKey, documentType: "passport", fileName: "passport.pdf", fileSize: 204800, mimeType: "application/pdf", }, }); ``` The confirmation call creates the DALP document record. Until confirmation succeeds, the uploaded object should not be treated as part of the KYC version. ## List documents [#list-documents] Use `user.kyc.documents.list` to read documents attached to a KYC version. Filters can narrow the result set, for example to one document type. ```ts const documents = await client.user.kyc.documents.list({ params: { versionId: "ver_123" }, query: { filters: [{ id: "documentType", operator: "eq", value: "passport" }], }, }); ``` The response includes paginated document records and metadata, so integrations can build review screens without fetching every document at once. ## Download a document [#download-a-document] Use `user.kyc.documents.getDownloadUrl` with the KYC version and document ID. DALP returns a download URL plus document metadata such as the file name and MIME type. ```ts const download = await client.user.kyc.documents.getDownloadUrl({ params: { versionId: "ver_123", documentId: document.data.id, }, }); ``` Use the returned URL to retrieve the file. Do not persist the URL as a permanent file reference; request a fresh download URL when an operator needs to view or process the document. ## Delete a document [#delete-a-document] Use `user.kyc.documents.delete` when an uploaded document was attached to the wrong version, has the wrong type, or needs to be replaced before submission. ```ts await client.user.kyc.documents.delete({ params: { versionId: "ver_123", documentId: document.data.id, }, }); ``` Deleting a document removes the DALP document record from that KYC version. It does not approve, reject, or submit the KYC version. ## CLI equivalents [#cli-equivalents] The DALP CLI exposes the same flow for operator scripts: * `kyc document-upload-url` * `kyc document-confirm-upload` * `kyc documents` * `kyc document-download-url` * `kyc document-delete` Use the CLI commands for back-office scripts and the SDK calls for application integrations. Both paths follow the same model: request an upload URL, upload the file, confirm the object key, then manage the document record on the KYC version. ## Related [#related] * [Developer guides](/docs/developer-guides) * [Compliance provider subjects](/docs/developer-guides/compliance/compliance-provider-subjects) # Operational integration patterns Source: https://docs.settlemint.com/docs/developer-guides/api-integration/operational-integration-patterns Answer common integration questions about DALP event access, token discovery, upgrade operations, operational monitoring, and self-hosted deployment responsibilities. Use this page when you are connecting DALP to an off-chain ledger, cap table service, analytics store, operations console, or customer platform. DALP exposes integration surfaces through REST APIs, the generated OpenAPI specification, indexed token events, account activity reads, blockchain monitoring endpoints, and deployment operations. Event and query consumers should build from the public API contract rather than reading internal databases directly. ## Event access model [#event-access-model] For token operations, use the token events collection: ```bash curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?page[offset]=0&page[limit]=50&sort=-blockTimestamp" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` The endpoint returns the canonical collection envelope: * `data`: event items * `meta`: total count and facet counts * `links`: pagination links for the current query The default sort is newest first by `blockTimestamp`. You can also sort by `blockNumber`. Supported filters include `eventName`, `senderAddress`, `accountAddress`, `walletAddress`, `transactionHash`, and `blockTimestamp` ranges. Use pagination to backfill or replay reads for a token. DALP does not document Kafka as a public token-event delivery interface. If you need event-driven processing, use the REST collection for durable reads and the OpenAPI specification to generate a typed client. Live operation screens may use server-sent events where a specific endpoint documents a stream, such as blockchain monitoring snapshots or migration progress, but token events are consumed through the REST event collection. Related pages: * [Token holders and transfers](/docs/developer-guides/api-integration/token-holders-transfers#list-token-events) * [API reference](/docs/developer-guides/api-integration/api-reference) ## Event payload fields [#event-payload-fields] Token event items include the operational fields needed for ledger reconciliation, including block number, block timestamp, transaction hash, event name, emitting contract, sender, related account, amount, and event values when present. Example shape: ```json { "id": "evt_01j...", "eventName": "TransferCompleted", "blockNumber": "8154321", "blockTimestamp": "2026-05-01T11:59:30.000Z", "transactionHash": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "txIndex": "0", "emitter": { "id": "0x2000000000000000000000000000000000000002" }, "sender": { "id": "0x3000000000000000000000000000000000000003" }, "values": [ { "id": "evt_01j...-account", "name": "account", "value": "0x3000000000000000000000000000000000000003" }, { "id": "evt_01j...-amount", "name": "amount", "value": "500" } ] } ``` Consumers should treat the OpenAPI response schema as the contract. Use exact transaction-hash filtering when reconciling one operation: ```bash curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[transactionHash][eq]=0xTRANSACTION_HASH" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` ## Ordering, idempotency, and replay [#ordering-idempotency-and-replay] Token event reads are queryable, paginated collections. The default event order is newest first by `blockTimestamp`, with block and log metadata available for deterministic reconciliation. For deterministic replay jobs: 1. Scope each reader to one token address. 2. Use `blockTimestamp` windows or pagination for REST replay. The token events API includes `blockNumber` in each event item, but block-number range replay is an indexer capability rather than a public token-event query parameter. 3. Persist the last processed event identifier, timestamp, transaction hash, block number, and transaction index. 4. On resume, reread inclusively from the last processed timestamp or a bounded timestamp window. 5. Dedupe by event identifier plus transaction hash, block number, and transaction index. 6. Store processed transaction hash, block number, event name, event identifier, transaction index, and token contract address in your own ledger. For mutation APIs that submit transactions, pass an `Idempotency-Key` header. DALP uses that key when queueing blockchain transactions so a retry can return the existing request or completed result instead of submitting the same transaction twice. Read-only event collection calls do not need an idempotency key; they should be replay-safe through persisted checkpoints and deduplication. Mutation APIs can return transaction metadata synchronously or an async `statusUrl`, depending on the operation. If a confirmation timeout occurs, check transaction status before retrying. A successful on-chain transaction must not be submitted again. Related pages: * [Token holders and transfers](/docs/developer-guides/api-integration/token-holders-transfers#list-token-events) * [Transaction tracking](/docs/developer-guides/operations/transaction-tracking) ## Chain finality, indexer health, and reindexing [#chain-finality-indexer-health-and-reindexing] Use blockchain monitoring endpoints to verify whether DALP can read the chain reliably. The API reports chain RPC and indexer health, including sync lag, block age, finality lag, stall time, reindex status, and recent service state. The indexer records block hashes and detects chain reorganizations by comparing indexed blocks with the canonical RPC chain. When a reorg is detected, DALP rolls indexed state back to the fork block and reprocesses affected blocks. Consumers should still keep their own ingestion idempotent because a previously read event can disappear or be replaced after rollback and reprocessing. Operational endpoints include: * `GET /api/v2/blockchain-monitoring/health-metrics/summary` * `GET /api/v2/blockchain-monitoring/health-metrics/timeline` * `GET /api/v2/blockchain-monitoring/service-health-metrics` * `GET /api/v2/blockchain-monitoring/health-snapshots` * `GET /api/v2/blockchain-monitoring/health-snapshots/stream` The stream endpoint uses server-sent events for live operations screens. Snapshot events include `eventType`, `serviceType`, `chainId`, `networkName`, `status`, `blockHeight`, `chainHeadBlock`, `syncLag`, `finalityLagBlocks`, `stallSeconds`, and optional deployment state. Related page: * [Blockchain monitoring](/docs/developer-guides/operations/blockchain-monitoring) ## Token and class discovery [#token-and-class-discovery] Use the API reference and token lifecycle guides for token creation and discovery. Token creation returns the deployed contract address. After issuance, integrations usually work from a known token contract address and then use token-specific endpoints for details, holders, events, features, metadata, compliance modules, transfer approvals, and denomination assets. Relevant endpoints include: * `GET /api/v2/tokens` for token discovery across the platform, including free-text list search with `filter[q]=...` and field filters such as token factory, token type, name, symbol, and creation date * `GET /api/v2/tokens/{tokenAddress}` for token details * `GET /api/v2/tokens/{tokenAddress}/holders` for the current holder balance collection * `GET /api/v2/tokens/{tokenAddress}/holder-balances` with `holderAddress` for one holder's balance * `GET /api/v2/tokens/{tokenAddress}/events` for indexed token events * `GET /api/v2/tokens/{tokenAddress}/features` for attached token features * `GET /api/v2/tokens/{tokenAddress}/metadata` for token metadata entries * `GET /api/v2/tokens/{tokenAddress}/compliance-modules` for compliance configuration * `GET /api/v2/tokens/{tokenAddress}/transfer-approvals` for transfer approval records * `GET /api/v2/system/factories` for token factory discovery on the active system * `GET /api/v2/settings/asset-class-definitions` for configured asset class definitions DALP does not expose a single public issuer-to-contract registry endpoint in the current API. If an integration models each share class as a separate token contract, keep the issuer-to-token mapping in the integrating system and reconcile it with token and factory discovery endpoints. Class metadata can be collected during asset creation through instrument templates, asset class definitions, and token metadata fields. Metadata mutability depends on permissions and the supported metadata update flow. Related pages: * [API reference](/docs/developer-guides/api-integration/api-reference) * [Token lifecycle](/docs/developer-guides/api-integration/token-lifecycle) * [Instrument templates](/docs/user-guides/asset-creation/instrument-templates) * [Asset detail workspace](/docs/user-guides/asset-servicing/asset-detail-workspace) ## Historical balances, snapshots, and servicing records [#historical-balances-snapshots-and-servicing-records] Use holder, event, and feature reads to reconcile cap-table and servicing systems. DALP exposes current token holders and indexed token events through the token API. The current public API does not expose a token balance-at-block endpoint or a dividend-specific record-date snapshot endpoint. For dividend or record-date workflows, store the record date and block reference in the off-chain servicing system, then build the required snapshot from token events and current holder reconciliation. Keep the snapshot inputs and replay checkpoint so the calculation can be reproduced and audited. Related pages: * [Lifecycle after issuance](/docs/architecture/start-here/lifecycle-after-issuance) * [Token lifecycle](/docs/developer-guides/api-integration/token-lifecycle#feature-operations-runbook) * [Token holders and transfers](/docs/developer-guides/api-integration/token-holders-transfers) ## Upgrade operations and compatibility [#upgrade-operations-and-compatibility] DALP includes a guided system upgrade workflow for keeping deployed system contracts aligned with the latest implementations available for the active network. The workflow compares deployed system components with the network directory, shows which components differ, and runs the upgrade with live progress. Relevant endpoints include: * `GET /api/v2/system/migration/compare` for directory-versus-deployed comparison * `POST /api/v2/system/migration/start` to start a migration or upgrade workflow * `GET /api/v2/system/migration/active` to check active migration state * `GET /api/v2/system/migration/{migrationId}/stream` for live migration progress Only accounts with the required system-management permission can run upgrades. The API accepts platform admins or wallets with the system manager or admin role on the indexed system. Completed on-chain steps remain applied if a later step fails, so operators review the comparison before starting and use the retry flow after fixing the reported issue. System contract upgrades emit implementation-update events such as `ImplementationUpdated` and `BatchImplementationsSet` for system implementation changes. Integrations should still rely on the API and OpenAPI schema as the compatibility contract, because contract events are low-level operational evidence rather than a substitute for the API contract. For integration compatibility: * Generate clients from the current OpenAPI specification. * Treat OpenAPI response schemas as the public API contract. * Read token features before calling feature-specific routes. * Reconcile events and transaction status after upgrade or migration work. * Use blockchain monitoring deployment state when an indexer is rebuilding. Related pages: * [System upgrades](/docs/developer-guides/operations/system-upgrades) * [API reference](/docs/developer-guides/api-integration/api-reference) * [Blockchain monitoring](/docs/developer-guides/operations/blockchain-monitoring) ## Self-hosted deployment and operations [#self-hosted-deployment-and-operations] Self-hosted DALP deployments run on Kubernetes or OpenShift through Helm charts. For Azure AKS, the self-hosting prerequisites call for managed PostgreSQL, managed Redis, object storage, backups, and managed observability unless an approved self-hosted fallback is used. The Helm charts expose replica counts and placement controls for API and worker services. The indexer runs as its own workload and is intentionally single-replica with a recreate update strategy, so scaling and isolation should be designed around workload placement, database capacity, RPC capacity, queue throughput, and operational monitoring rather than horizontal indexer replicas. SettleMint leads the initial installation when the prerequisites are complete. Long-term ownership of Helm upgrades, vulnerability patching, uptime monitoring, backups, and platform operations should be agreed during the deployment handover. SettleMint can also operate the environment through an agreed control-plane-managed model, depending on the commercial and operational scope. The environment must provide metrics, logs, traces, and alerting through the cloud provider or an approved managed observability stack. Related pages: * [Self-hosting prerequisites](/docs/architecture/self-hosting/prerequisites) * [Blockchain monitoring](/docs/developer-guides/operations/blockchain-monitoring) ## Private keys and secrets [#private-keys-and-secrets] DALP uses Key Guardian for private-key protection. Key Guardian supports multiple storage tiers, including encrypted database storage, cloud secret managers, hardware security modules, and third-party custody providers such as DFNS and Fireblocks. Production deployments should use the storage tier approved for the asset value and regulatory posture. Key Guardian receives signing requests without exposing raw key material, routes the request to the configured backend, and logs key generation, signature requests, rotation, and access denials for security review. Related pages: * [Key Guardian](/docs/architecture/components/infrastructure/key-guardian) * [Custody providers](/docs/architecture/integrations/custody-providers) * [Signing flow](/docs/architecture/flows/signing-flow) ## Rate limits and throughput [#rate-limits-and-throughput] Authentication endpoints and API-key authentication have explicit rate-limit controls. Current defaults include these authentication limits: | Surface | Default limit | | -------------------------------------------------- | ------------------------------ | | Email sign-in | 5 requests per 60 seconds | | Email sign-up | 3 requests per 60 seconds | | Password reset request (`/request-password-reset`) | 3 requests per 60 seconds | | Other core authentication endpoints | 100 requests per 60 seconds | | Wallet verification endpoints | 100 requests per 10 seconds | | API-key authentication | 10,000 requests per 60 seconds | Authentication rate limits use shared database storage so counters apply across multiple API replicas. DALP trusts `x-real-ip` for rate-limit attribution; the accepted header is fixed to `x-real-ip` in the auth server configuration. Configure real-client-IP attribution before exposing authentication endpoints: * For nginx-ingress, enable the real-IP module in the controller ConfigMap, set the real-IP source header from the trusted upstream edge proxy, keep the trusted proxy CIDR list current, and verify the controller overwrites `X-Real-IP` before forwarding to DALP. A typical checklist is `enable-real-ip: "true"`, `real-ip-header` set to the trusted upstream header, and `proxy-real-ip-cidr` restricted to the load balancer or upstream proxy ranges. * For Traefik, configure trusted forwarded-header sources only for the entry point that receives traffic from the managed load balancer, then add a router middleware or upstream edge rule that sets `X-Real-IP` to the validated client IP before the request reaches the DALP service. Do not pass through an existing client-supplied `X-Real-IP` value. * For Gateway API, Envoy, or OpenShift Routes, do not assume the DALP route object rewrites `X-Real-IP`. Add an equivalent trusted header rewrite in the Gateway policy, Envoy filter, OpenShift router configuration, or upstream edge proxy before forwarding traffic to DALP. If that rewrite is not configured, authentication rate-limit counters can be attributed to the gateway, router, or proxy IP instead of the client IP. * Validate the deployment by sending requests through the public route and confirming the API service receives `x-real-ip` as the original client IP. Do not rely on other forwarded headers, or client-supplied values, for rate-limit attribution because those values can be missing, incorrect, or spoofable. DALP does not publish a universal transactions-per-second number for every deployment. Throughput depends on the selected chain, RPC provider, custody backend, queue configuration, infrastructure sizing, bundler settings, and operation mix. Use load testing against the target environment and agree production rate limits during implementation. For self-hosted environments, scale decisions should follow the Kubernetes, database, Redis, ingress, and observability baselines in the self-hosting prerequisites. Related pages: * [API reference](/docs/developer-guides/api-integration/api-reference) * [Self-hosting prerequisites](/docs/architecture/self-hosting/prerequisites) ## SLA and operational addenda [#sla-and-operational-addenda] The API and charts provide operational health surfaces, probes, telemetry, and monitoring endpoints, including `/health`, `/livez`, service readiness endpoints, OpenTelemetry export, blockchain monitoring health metrics, service health metrics, health snapshots, and snapshot streams. SLA terms are not defined by the public API documentation. Treat uptime, support response times, maintenance windows, backup responsibilities, and incident processes as part of the SLA addendum or managed-service agreement for the deployment. Related pages: * [Blockchain monitoring](/docs/developer-guides/operations/blockchain-monitoring) * [Self-hosting prerequisites](/docs/architecture/self-hosting/prerequisites) ## Recommended off-chain ledger pattern [#recommended-off-chain-ledger-pattern] Use an append-only mirror in the integrating system. Store enough identifiers to make ingestion idempotent and auditable: * token contract address * event identifier * event name * block number * block timestamp * transaction hash * sender address * account or wallet address * amount and value fields * ingestion timestamp * source API timestamp window and replay checkpoint Rebuild the mirror from the token events collection when needed, and use holder reads for current balance reconciliation. Do not treat the mirror as the source of truth for token ownership. DALP and the chain remain the authoritative execution layer. # Organization and system scope Source: https://docs.settlemint.com/docs/developer-guides/api-integration/organization-system-scope Understand how DALP API requests are bounded by organization membership, API key scope, active system context, and resource visibility. DALP evaluates API access in the caller's organization and system context. Use this page when you design integrations that run across multiple organizations, systems, or environments. ## Scope layers [#scope-layers] | Layer | What it controls | What integrators should do | | -------------------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | API key or session | Who is making the request and which platform permissions apply | Create machine-to-machine API keys from the organization that owns the integration workload. Use separate keys for separate organizations. | | Organization | Which off-chain records, users, API keys, and organization-owned resources are visible | Confirm the API key was created while the intended organization was active. If a user belongs to more than one organization, do not reuse one organization key for another organization. | | System | Which deployed system, chain, and system-owned resources are in scope | Use the system address or system-specific endpoint required by the API operation. Treat system addresses as part of the integration configuration. | | Wallet ownership and roles | Which wallet-backed actions the caller can perform | Assign the required platform role and on-chain role before calling write operations such as issuance, role changes, minting, transfers, or administrative actions. | ## API keys are organization-scoped [#api-keys-are-organization-scoped] Newly created API keys are tied to the organization active when the key is created. Requests made with that key run with the associated user's role in that organization. Legacy API keys that do not carry an organization scope may be rejected by organization-scoped API operations. DALP does not fall back to a global view when a key cannot resolve an active organization. Rotate or recreate those keys from the intended organization before using them for organization-scoped integration traffic. For integrations: 1. Create a separate key per organization. 2. Store the key with the organization and environment it belongs to. 3. Rotate or revoke a key from the same organization context that created it. 4. Rotate or recreate legacy keys that fail because they do not resolve an active organization. 5. Avoid sharing a key between customer environments, test environments, or operating teams. ## System-scoped requests [#system-scoped-requests] Many DALP resources belong to a specific deployed system on a specific chain. Examples include issued tokens, token events, holders, system roles, factories, add-ons, and system-level settings. For those requests, the API combines the caller's organization context with the active system context. A request can only read or act on resources that match both contexts and the caller's permissions. Practical integration pattern: ```ts const organization = "acme-production"; const systemAddress = "0x1234..."; // Store both values in your integration configuration. // Use the organization-specific API key with endpoints that target this system. ``` When you move the same integration between test and production, update both the API key and the system address. Do not point a production API key at a test system or reuse a test key against production resources. ## Missing resources and unauthorized resources [#missing-resources-and-unauthorized-resources] For organization- and system-scoped resources, DALP avoids exposing whether a resource exists outside the caller's allowed scope. A request for a resource the caller cannot access can return the same not-found style response as a request for a resource that does not exist. This means a `404` can mean any of the following: * the resource identifier is wrong, * the resource belongs to another organization, * the resource belongs to another system, * the caller does not have permission to view it. When debugging a `404`, verify the organization-specific API key, the system address, the chain or environment, and the caller's roles before assuming the resource is absent. ## Integration checklist [#integration-checklist] Before deploying an integration, confirm: * the API key was created in the intended organization, * the key is stored separately for each organization and environment, * the integration is configured with the correct system address, * the caller has the required platform role and on-chain role for the operation, * read paths handle not-found responses without treating them as proof that a resource does not exist globally, * write paths use the same organization and system configuration as the read paths they depend on. ## Related guides [#related-guides] * [Getting started](/docs/developer-guides/api-integration/getting-started) * [Operational integration patterns](/docs/developer-guides/api-integration/operational-integration-patterns) * [Error handling](/docs/developer-guides/api-integration/error-handling) * [Add administrators](/docs/developer-guides/platform-setup/add-admins) # Portfolio statistics Source: https://docs.settlemint.com/docs/developer-guides/api-integration/portfolio-statistics Query portfolio value time series and portfolio breakdowns for the active DALP system through the statistics API. Use the portfolio statistics API when an integration needs reporting data for the current user's portfolio in a DALP system. The endpoints return historical value points, a current asset-type breakdown, and the resolved range DALP used for time series queries. Portfolio statistics are scoped to the active system from the request context. If the same wallet has holdings in more than one DALP system, the result for one system does not include portfolio history, hourly fallback deltas, or breakdown rows from another system. ## Endpoints [#endpoints] The portfolio statistics API exposes three read endpoints: | Endpoint | Use it for | | ---------------------------------------------------------------- | ------------------------------------------------------------------------- | | `GET /api/v2/system/stats/portfolio-stat-ranges` | A custom `from` and `to` time window. | | `GET /api/v2/system/stats/portfolio-stat-range-presets/{preset}` | A predefined trailing window. | | `GET /api/v2/system/stats/portfolio-breakdowns` | The current portfolio value and holdings grouped by asset type and class. | All three endpoints return a JSON:API single-resource envelope with `data` and `links.self`. ## Query a custom range [#query-a-custom-range] Use the range endpoint when your dashboard controls the interval and timestamps. `interval` accepts `hour` or `day`. `from` and `to` are timestamps, and `from` must be before or equal to `to`. ```bash curl --request GET \ "$DALP_API_URL/api/v2/system/stats/portfolio-stat-ranges?interval=hour&from=2026-03-24T13:00:00.000Z&to=2026-03-24T16:00:00.000Z" \ --header "X-Api-Key: $DALP_API_TOKEN" ``` ## Query a preset range [#query-a-preset-range] Use the preset endpoint when DALP should resolve the window from the current time. Supported presets are: | Preset | Interval | | ----------------- | -------- | | `trailing24Hours` | `hour` | | `trailing7Days` | `day` | ```bash curl --request GET \ "$DALP_API_URL/api/v2/system/stats/portfolio-stat-range-presets/trailing7Days" \ --header "X-Api-Key: $DALP_API_TOKEN" ``` ## Query the current breakdown [#query-the-current-breakdown] Use the breakdown endpoint when a dashboard needs the current portfolio total and a grouped view of holdings. ```bash curl --request GET \ "$DALP_API_URL/api/v2/system/stats/portfolio-breakdowns" \ --header "X-Api-Key: $DALP_API_TOKEN" ``` The endpoint has no query parameters. DALP returns the current breakdown for the authenticated wallet and active system. Responses can be cached for that wallet and system, so a repeated request after switching organisations can reuse previously returned asset-type or asset-class labels until the statistics cache refreshes. ## Time-series response shape [#time-series-response-shape] The time-series response body contains the resolved range, portfolio value points, and a currency-conversion reliability flag. ```json { "data": { "range": { "interval": "hour", "from": "2026-03-24T13:00:00.000Z", "to": "2026-03-24T16:00:00.000Z", "isPreset": false }, "data": [ { "timestamp": "2026-03-24T13:00:00.000Z", "totalValueInBaseCurrency": 1000 }, { "timestamp": "2026-03-24T14:00:00.000Z", "totalValueInBaseCurrency": 1200 } ], "conversionReliable": true }, "links": { "self": "/v2/system/stats/portfolio-stat-ranges" } } ``` `data.data` is the time series. Each point includes: * `timestamp`: the bucket timestamp for the returned point. * `totalValueInBaseCurrency`: the portfolio value at that point, rounded for the API response. `conversionReliable` is `false` when one or more FX rates needed for the conversion path are unavailable and DALP had to use its fallback conversion behavior. ## Breakdown response shape [#breakdown-response-shape] The breakdown response returns current totals, grouped values, grouped holdings, and the same conversion reliability flag. ```json { "data": { "totalValue": "5000000.00", "totalAssetTypes": 2, "totalAssetsHeld": 15, "typeBreakdown": [ { "assetType": "bond", "totalValue": "3000000.00", "tokenBalancesCount": 5, "percentage": 60 }, { "assetType": "equity", "totalValue": "2000000.00", "tokenBalancesCount": 10, "percentage": 40 } ], "valueBreakdown": { "bond": "3000000.00", "equity": "2000000.00" }, "holdingsBreakdown": { "bond": 5, "equity": 10 }, "valueBreakdownByClass": { "fixed-income": "3000000.00", "flexible-income": "2000000.00" }, "holdingsBreakdownByClass": { "fixed-income": 5, "flexible-income": 10 }, "conversionReliable": true }, "links": { "self": "/v2/system/stats/portfolio-breakdowns" } } ``` Use `typeBreakdown` when you need a sortable list with percentages. Use the `valueBreakdown` and `holdingsBreakdown` maps when your application already knows which asset-type keys it wants to display. Asset-type keys can be system types such as `bond` or `equity`, or custom template slugs. Asset-class keys can be system class slugs such as `fixed-income`, or custom organisation class slugs. `conversionReliable` is `false` when one or more FX rates needed for the breakdown are unavailable and DALP had to use its fallback conversion behavior. ## System scoping [#system-scoping] The portfolio time-series result is calculated for the authenticated user's wallet inside the active system. DALP filters portfolio snapshots and hourly fallback deltas by chain, system, wallet account, and requested time range before building the response. The current breakdown response also uses the authenticated wallet and active system. Tenant scope can affect the organisation-specific asset templates and classes used to label grouped rows, but the response should still be treated as wallet-and-system scoped unless your API environment partitions the endpoint by organisation. That means an integration can safely show the time-series output as the user's portfolio history for the selected system. Treat breakdown output as current wallet-and-system reporting, not as independent organisation-level evidence and not as a response your application can reuse across organisations. Do not add results from another system unless your application is intentionally building a cross-system report. ## Related [#related] * [Developer guides](/docs/developer-guides) * [Token holders and transfers](/docs/developer-guides/api-integration/token-holders-transfers) # TypeScript SDK Source: https://docs.settlemint.com/docs/developer-guides/api-integration/sdk Install and use the @settlemint/dalp-sdk package for fully typed, auto-completed access to the DALP API from any TypeScript project. The `@settlemint/dalp-sdk` package is the recommended way to interact with the DALP API from TypeScript. It provides a fully typed client with auto-complete for every API namespace — tokens, identities, compliance, transactions, and more — without code generation or OpenAPI tooling. **Why use the SDK over the OpenAPI client?** * **Zero code generation** — install the package and start calling methods immediately * **Contract-first types** — auto-complete for every route, input, and output derived from the API contract * **Custom serializers** — `BigInt`, `BigDecimal`, and `Timestamp` values serialize correctly out of the box * **Opt-in client-side validation** — enable request validation to check outgoing requests against the API schema before sending * **Tree-shakeable** — import only what you need; unused namespaces are removed at build time *** ## Installation [#installation] ```bash # npm npm install @settlemint/dalp-sdk # bun bun add @settlemint/dalp-sdk ``` **Peer dependency**: the SDK requires `zod >= 4.0.0`. Most projects already have it — if not, install it alongside the SDK. *** ## Create a client [#create-a-client] ```typescript import { createDalpClient } from "@settlemint/dalp-sdk"; const dalp = createDalpClient({ url: "https://your-platform.example.com", apiKey: "sm_dalp_xxxxxxxxxxxxxxxx", }); ``` That's it. `dalp` is now a fully typed client with auto-complete for every API namespace. ### Configuration options [#configuration-options] | Option | Type | Default | Description | | -------------------- | --------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------- | | `url` | `string` | — | Base URL of your DALP deployment (e.g., `"https://dalp.example.com"`) | | `apiKey` | `string` | — | API key from the DALP dashboard (Settings → API Keys) or `dalp login`. Required for authenticated routes. | | `organizationId` | `string` | — | Organization ID for multi-org setups. Required when the key spans multiple organizations. | | `headers` | `Record` or `() => ...` | — | Additional headers merged into every request. Can override `User-Agent` but not security headers. | | `idempotencyKey` | `string` | — | Sent as `Idempotency-Key` header on **every** request. Only use for single-mutation clients (see warning below). | | `requestValidation` | `boolean` | `false` | Validate outgoing requests against the API contract before sending. Useful during development. | | `responseValidation` | `boolean` | `false` | Validate incoming responses against the API contract. Useful during development. | | `fetch` | `typeof globalThis.fetch` | — | Custom `fetch` implementation for proxies, logging, or test doubles. | ### Authenticated and public clients [#authenticated-and-public-clients] Pass an `apiKey` for authenticated routes. The SDK sends it as the `x-api-key` request header and trims surrounding whitespace before use. You may omit `apiKey` when you only call public DALP API routes: ```typescript const publicDalp = createDalpClient({ url: "https://your-platform.example.com", }); ``` If you provide an empty or whitespace-only API key, the SDK raises a configuration error instead of sending an invalid credential. When `apiKey` is omitted, the SDK does not send the `x-api-key` header. *** ## Usage examples [#usage-examples] ### List tokens [#list-tokens] ```typescript const tokens = await dalp.token.list({ query: {} }); for (const token of tokens.items) { console.log(token.name, token.symbol, token.tokenAddress); } ``` ### Get system info [#get-system-info] ```typescript const system = await dalp.system.read({}); console.log("Platform:", system.name); console.log("Version:", system.version); ``` ### Create a token [#create-a-token] ```typescript const token = await dalp.token.create({ body: { name: "Acme Bond", symbol: "ABOND", assetType: "bond", decimals: 18, }, }); console.log("Created:", token.tokenAddress); ``` ### Mint tokens [#mint-tokens] ```typescript import { from as dnumFrom } from "dnum"; await dalp.token.mint({ body: { tokenAddress: "0xABCD...", recipients: ["0x1234..."], amounts: [dnumFrom("1000", 18)], }, }); ``` ### Search [#search] ```typescript const results = await dalp.search.query({ query: { q: "Acme" }, }); for (const hit of results.items) { console.log(hit.type, hit.name); } ``` *** ## Type-only imports [#type-only-imports] When you only need types for function signatures (not runtime code), import from the `/types` subpath to keep your bundle lean: ```typescript import type { DalpClient, DalpClientConfig } from "@settlemint/dalp-sdk/types"; function createMyClient(config: DalpClientConfig): DalpClient { // ... } ``` *** ## Plugins [#plugins] The SDK ships optional oRPC link plugins for advanced use cases: ```typescript import { BatchLinkPlugin, RequestValidationPlugin } from "@settlemint/dalp-sdk/plugins"; ``` These are re-exports from `@orpc/client` and `@orpc/contract` — use them if you need to customize the link pipeline beyond the defaults. *** ## Error handling [#error-handling] The SDK uses oRPC's error model. All errors are instances of `ORPCError` with a `code` property: ```typescript import { ORPCError } from "@orpc/client"; import { CUSTOM_ERROR_CODES } from "@settlemint/dalp-sdk"; try { await dalp.token.read({ query: { address: "0x0000..." } }); } catch (error) { if (error instanceof ORPCError) { if (error.code === "NOT_FOUND") { console.log("Token does not exist"); } else if (error.code === CUSTOM_ERROR_CODES.USER_NOT_AUTHORIZED) { console.log("This operation requires elevated permissions"); } else { throw error; } } else { throw error; } } ``` For the complete list of error codes and handling strategies, see [Error handling](/docs/developer-guides/api-integration/error-handling). *** ## Using with the DALP CLI [#using-with-the-dalp-cli] The DALP CLI uses the SDK internally. If you already have the CLI installed, you can reuse its authentication: ```bash # Authenticate and get an API key dalp login # Show your credentials dalp whoami --format json ``` Then use the API key from `dalp login` in your SDK client configuration. *** ## Next steps [#next-steps] * **[API reference](/docs/developer-guides/api-integration/api-reference)** — explore all available endpoints and schemas * **[Error handling](/docs/developer-guides/api-integration/error-handling)** — handle errors gracefully in production * **[Token lifecycle](/docs/developer-guides/api-integration/token-lifecycle)** — understand the full token operation flow * **[Asset decimals](/docs/developer-guides/api-integration/asset-decimals)** — work correctly with token precision and BigDecimal values # Smart wallet multisig thresholds Source: https://docs.settlemint.com/docs/developer-guides/api-integration/smart-wallet-thresholds Update and validate the approval threshold for a DALP smart wallet that uses weighted multisig signing. Use the smart wallet threshold API when an integration needs to change how much signer weight is required before a multisig smart wallet operation can be approved. The threshold is a weighted approval value, not a signer count. A smart wallet with signer weights of `3`, `2`, and `1` can use threshold `4` so the signer with weight `3` must approve with at least one other signer. ## How to update a threshold [#how-to-update-a-threshold] Before sending the update, read the wallet's current signer configuration and choose a threshold that can be met by the configured signer weights. Request validation accepts a `threshold` only when it is: * a decimal string * at least `1` * no more than `18446744073709551615` (`2^64-1`) Choose a value that is no more than the sum of all signer weights for the wallet. If the value cannot be reached by the configured signer weights, that failure is reported through the asynchronous blockchain mutation status rather than by the initial request validation. Submit the update using credentials for the wallet owner: ```bash curl -X PUT https://your-platform.example.com/api/v2/smart-wallets/0x1234567890AbcdEF1234567890aBcdef12345678/threshold \ -H "X-Api-Key: YOUR_DALP_API_KEY" \ -H "Content-Type: application/json" \ -H "Prefer: respond-async" \ -d '{ "threshold": "4" }' ``` The endpoint submits an asynchronous blockchain mutation. Treat the response as the start of an on-chain operation: persist the returned status information and poll the status URL when the response includes one. If confirmation times out, check transaction status before retrying the same threshold update. Co-signers do not call this endpoint directly. They approve pending multisig operations through the multisig approval flow. ## Endpoint reference [#endpoint-reference] `PUT /api/v2/smart-wallets/{address}/threshold` Sets the multisig approval threshold for a smart wallet. ### Path parameters [#path-parameters] | Parameter | Type | Description | | --------- | ---------------- | ----------------------------- | | `address` | Ethereum address | Smart wallet contract address | ### Request body [#request-body] | Field | Type | Required | Description | | ----------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | `threshold` | string | Yes | Positive decimal integer string from `1` through `18446744073709551615`. Choose a value that can be met by the configured signer weights. | ### Authorisation [#authorisation] Only the wallet owner can call this endpoint. Co-signers use the multisig approval flow to approve operations that require their signature weight. ### Behaviour [#behaviour] * The endpoint targets smart wallets with an installed weighted multisig validator. * The threshold update is submitted as an asynchronous blockchain mutation. * Synchronous request validation checks only the positive decimal-string and `uint64` bounds. * A threshold that exceeds available signer weight is not a valid multisig configuration; if the chain rejects it, track that failure through the queued transaction or on-chain status instead of expecting pre-submission rejection. ## Related operations [#related-operations] * Use `GET /api/v2/smart-wallets/{address}/signers` to inspect signer weights before choosing a threshold. * Use `GET /api/v2/smart-wallets/{address}/approvals` to list pending multisig approvals for a wallet. * Use `POST /api/v2/smart-wallets/{address}/approvals/{userOpHash}/sign` when a co-signer needs to sign a pending approval. ## Related [#related] * [Developer guides](/docs/developer-guides) * [Organization system scope](/docs/developer-guides/api-integration/organization-system-scope) # System paymasters Source: https://docs.settlemint.com/docs/developer-guides/api-integration/system-paymasters List system paymasters, check funding, manage sponsorship configuration, and rotate paymaster signer keys through the DALP API. System paymaster endpoints let integrations inspect and operate Account Abstraction paymasters for the active system. Use them when your integration needs to monitor gas sponsorship, fund the paymaster EntryPoint deposit, or rotate the sponsorship ticket signer key. For the operator view in the dapp, see [Account Abstraction Control Center](/docs/user-guides/platform-setup/account-abstraction-control-center). For transaction status polling after a queued mutation, see [Transaction tracking](/docs/developer-guides/operations/transaction-tracking). ## Endpoints [#endpoints] | Endpoint | Use it for | | ------------------------------------------------------------ | ------------------------------------------------------------- | | `GET /api/v2/system/paymasters` | List paymasters registered for the active system. | | `GET /api/v2/system/paymasters/{address}/balance` | Read the paymaster EntryPoint deposit balance. | | `POST /api/v2/system/paymasters/{address}/deposits` | Fund the paymaster EntryPoint deposit. | | `GET /api/v2/system/paymasters/{address}/signer-key/status` | Read the current signer address and last rotation timestamp. | | `POST /api/v2/system/paymasters/{address}/signer-key/rotate` | Rotate the sponsorship ticket signer key. | | `GET /api/v2/system/paymasters/config` | Read whether paymaster sponsorship is enabled. | | `PUT /api/v2/system/paymasters/config` | Enable or disable paymaster sponsorship for the organization. | Responses use the DALP single-resource or collection envelope with `data` and `links.self`. Mutations that submit on-chain work use the standard asynchronous blockchain mutation response. ## List paymasters [#list-paymasters] List paymasters before calling address-specific endpoints. The list supports collection filters for `address`, `factory`, and `createdAt`. The default sort is newest first by `createdAt`. ```bash curl --request GET \ "$DALP_API_URL/api/v2/system/paymasters" \ --header "X-Api-Key: $DALP_API_TOKEN" ``` Each item includes: | Field | Meaning | | ----------- | ----------------------------------------------------- | | `address` | Paymaster contract address. | | `system` | System address the paymaster belongs to, or `null`. | | `factory` | Factory address that created the paymaster. | | `createdAt` | Registration timestamp for the indexed paymaster row. | Address-specific endpoints return a not-found error when the supplied address is not indexed for the active system. If you just installed a paymaster, list paymasters again after indexing has caught up. ## Check funding [#check-funding] Use the balance endpoint to read the paymaster EntryPoint deposit. The returned `depositBalance` is a wei-denominated integer string. ```bash curl --request GET \ "$DALP_API_URL/api/v2/system/paymasters/$PAYMASTER_ADDRESS/balance" \ --header "X-Api-Key: $DALP_API_TOKEN" ``` Example response: ```json { "data": { "address": "0x1111111111111111111111111111111111111111", "depositBalance": "1000000000000000000" }, "links": { "self": "/v2/system/paymasters/0x1111111111111111111111111111111111111111/balance" } } ``` ## Fund the paymaster [#fund-the-paymaster] Deposit requests fund the paymaster EntryPoint deposit. Send `amount` as a positive wei-denominated integer string. DALP rejects zero, negative, non-numeric, and scientific-notation values. ```bash curl --request POST \ "$DALP_API_URL/api/v2/system/paymasters/$PAYMASTER_ADDRESS/deposits" \ --header "X-Api-Key: $DALP_API_TOKEN" \ --header "Content-Type: application/json" \ --data '{ "amount": "1000000000000000000" }' ``` Funding requires the paymaster to be indexed for the active system and requires wallet verification before DALP queues the on-chain deposit transaction. ## Manage sponsorship configuration [#manage-sponsorship-configuration] The paymaster configuration endpoint controls whether Account Abstraction user operations attach paymaster sponsorship for the organization. Read the current setting: ```bash curl --request GET \ "$DALP_API_URL/api/v2/system/paymasters/config" \ --header "X-Api-Key: $DALP_API_TOKEN" ``` Update the setting: ```bash curl --request PUT \ "$DALP_API_URL/api/v2/system/paymasters/config" \ --header "X-Api-Key: $DALP_API_TOKEN" \ --header "Content-Type: application/json" \ --data '{ "enabled": true }' ``` `GET` returns `enabled: false` when no organization configuration row exists yet. `PUT` stores the explicit setting for the organization. Changing the sponsorship setting does not rotate the paymaster signer key. Use the signer-key endpoint when the signing key itself must change. ## Rotate the signer key [#rotate-the-signer-key] The signer key signs sponsorship tickets for one paymaster. Use the status endpoint to inspect the signer address and rotation timestamp: ```bash curl --request GET \ "$DALP_API_URL/api/v2/system/paymasters/$PAYMASTER_ADDRESS/signer-key/status" \ --header "X-Api-Key: $DALP_API_TOKEN" ``` Example response: ```json { "data": { "signerAddress": "0x2222222222222222222222222222222222222222", "rotatedAt": "2026-05-12T00:00:00.000Z" }, "links": { "self": "/v2/system/paymasters/0x1111111111111111111111111111111111111111/signer-key/status" } } ``` Both fields can be `null` when no signer key has been set or the current signer address cannot be resolved from the configured signer provider. DALP does not expose private key material in the status response. Rotate the signer key when you need to replace the sponsorship ticket signer for a paymaster: ```bash curl --request POST \ "$DALP_API_URL/api/v2/system/paymasters/$PAYMASTER_ADDRESS/signer-key/rotate" \ --header "X-Api-Key: $DALP_API_TOKEN" \ --header "Content-Type: application/json" \ --data '{}' ``` Signer key rotation requires wallet verification. After the on-chain signer update succeeds, DALP stores the new paymaster-scoped signing key and returns the new signer address and rotation timestamp. In-flight user operations signed with the old key can be rejected after the signer switches. ## Access and permissions [#access-and-permissions] The API follows the same paymaster role boundaries as the dapp control center. The paymaster list is available to authenticated organization members. Balance and configuration reads require an authenticated organization member with a wallet. Signer-key status, transaction changes, and configuration changes require an operator role. | Operation | Roles | | --------------------------------------- | ----------------------------------------------- | | List paymasters | Any authenticated organization member | | Read paymaster balance | Authenticated organization member with a wallet | | Read sponsorship configuration | Authenticated organization member with a wallet | | Read signer key status | Admin, System manager, Auditor, and Gas manager | | Fund the paymaster EntryPoint deposit | Admin, System manager, and Gas manager | | Enable or disable paymaster sponsorship | Admin, System manager, and Gas manager | | Rotate the signer key | Admin and System manager | Use the dapp control center when an operator needs to confirm role access visually before calling the API from an integration. # Token document uploads Source: https://docs.settlemint.com/docs/developer-guides/api-integration/token-documents Upload, confirm, list, download, and delete token or asset documents through the DALP API, SDK, and CLI. DALP token document APIs manage files that belong to an asset, such as a prospectus, term sheet, regulatory filing, compliance report, certificate, reserve audit, or other asset evidence. The API uses the same two-step upload pattern as other document flows: 1. Request a presigned upload URL for a token document. 2. Upload the file directly to storage using the returned method and headers. 3. Confirm the upload so DALP records the document against the token. 4. List, download, or delete the document later through the token document API. This flow is for token or asset documents. Use the KYC document upload guide when the file belongs to a user's KYC profile instead of an asset. ## Endpoints [#endpoints] The token document API exposes these operations: * `GET /api/v2/tokens/{tokenAddress}/documents` lists token documents with pagination, sorting, and filtering. * `POST /api/v2/tokens/{tokenAddress}/document-uploads` returns a presigned upload URL. * `POST /api/v2/tokens/{tokenAddress}/documents` confirms an uploaded file and creates the document record. * `POST /api/v2/tokens/{tokenAddress}/documents/{documentId}/downloads` returns a secure download URL. * `DELETE /api/v2/tokens/{tokenAddress}/documents/{documentId}` deletes a token document. ## Request an upload URL [#request-an-upload-url] Request an upload URL before sending the file bytes. The request describes the file and how it should be classified on the asset. ```ts const upload = await client.token.documents.getUploadUrl({ params: { tokenAddress: "0xTOKEN", }, body: { documentType: "prospectus", fileName: "bond-prospectus.pdf", fileSize: 2_400_000, mimeType: "application/pdf", visibility: "public", title: "Bond prospectus", description: "Published prospectus for investor review", }, }); ``` Supported MIME types are: * `application/pdf` * `image/jpeg` * `image/png` * `image/webp` * `application/vnd.openxmlformats-officedocument.wordprocessingml.document` * `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` `fileSize` must be positive and no larger than 50 MiB. ## Upload the file bytes [#upload-the-file-bytes] Upload the file directly to the returned URL. Use the returned method and headers. Some storage backends include provider-specific headers in the upload URL response. ```ts await fetch(upload.data.uploadUrl, { method: upload.data.method, headers: upload.data.headers, body: fileBytes, }); ``` Do not replace the returned headers with only `Content-Type`. Provider-specific headers are part of the upload contract. ## Confirm the uploaded document [#confirm-the-uploaded-document] After the file upload succeeds, confirm the upload with the returned `objectKey`. The confirmation creates the token document record and returns the stored document metadata. ```ts const document = await client.token.documents.confirmUpload({ params: { tokenAddress: "0xTOKEN", }, body: { objectKey: upload.data.objectKey, documentType: "prospectus", fileName: "bond-prospectus.pdf", fileSize: 2_400_000, mimeType: "application/pdf", visibility: "public", title: "Bond prospectus", description: "Published prospectus for investor review", }, }); ``` The response includes the document `id`, `groupId`, `versionNumber`, `isLatest`, `fileHash`, `uploadedAt`, and uploader metadata. DALP calculates `fileHash` from the uploaded file bytes when the upload is confirmed. Store that value in downstream systems when you need to reconcile that an asset document record still points to the expected file. Use `replaceGroupId` when a new upload should replace a previous document in the same document group. The new record becomes the latest version for that group. ## Record document integrity claims [#record-document-integrity-claims] Token document upload records and asset-level claims serve different purposes: * The token document upload flow stores the file metadata, version group, and `fileHash` that belongs to the uploaded document record. * An asset-level document-hash claim can reference a SHA-256 hash, document type, and file name without placing the document contents on-chain. Use the upload flow to manage access, download links, and document versions. Use an asset-level document-hash claim when an asset also needs an on-chain integrity reference for a specific document. ## Choose document type and visibility [#choose-document-type-and-visibility] Document types include asset evidence such as: * `prospectus` * `term_sheet` * `legal_opinion` * `regulatory_filing` * `compliance_report` * `annual_report` * `financial_statement` * `credit_rating` * `covenant_agreement` * `shareholder_agreement` * `fund_fact_sheet` * `subscription_agreement` * `nav_report` * `reserve_audit` * `attestation_report` * `reserve_composition` * `certificate` * `interest_schedule` * `property_deed` * `survey_report` * `appraisal` * `environmental_assessment` * `title_insurance` * `assay_certificate` * `storage_receipt` * `chain_of_custody` * `insurance_certificate` * `other` Visibility controls who can access the document: * `public`: visible to anyone who can access the token document surface. * `holders`: visible to token holders. * `restricted`: limited to explicitly allowed access paths. Choose the narrowest visibility that fits the operating process and regulatory basis for the document. ## List documents [#list-documents] Use the list endpoint to reconcile published documents or populate an asset document table. ```bash curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/documents?page[limit]=50&sort=-uploadedAt" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` The list endpoint supports pagination, sorting, filtering, and facets. Sortable fields include `fileName`, `documentType`, `visibility`, `fileSize`, and `uploadedAt`. Filterable fields are `fileName`, `documentType`, `visibility`, `mimeType`, `isLatest`, `fileSize`, and `uploadedAt`. Filter with the same collection filter shape used by the current list endpoints. For example, request the latest public prospectuses uploaded after a cutoff time: ```bash curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/documents?filter[documentType][eq]=prospectus&filter[visibility][eq]=public&filter[uploadedAt][gte]=2026-01-01T00:00:00.000Z" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` The API only returns the latest non-deleted document records visible to the caller. Public documents are visible through the token document surface. Holder visibility requires a caller with an asset role, and restricted visibility requires governance or admin rights. ## Download or delete a document [#download-or-delete-a-document] Request a secure download URL when a user needs to retrieve the file: ```ts const download = await client.token.documents.getDownloadUrl({ params: { tokenAddress: "0xTOKEN", documentId: document.data.id, }, }); ``` Delete a document only when it should no longer be available from the asset's document record: ```ts await client.token.documents.delete({ params: { tokenAddress: "0xTOKEN", documentId: document.data.id, }, }); ``` Keep the business reason and approval evidence for deletes in your operating records. The delete call removes the document record from the API surface; it is not a substitute for your regulated record-retention process. ## CLI commands [#cli-commands] The DALP CLI exposes the same token document lifecycle: ```bash dalp tokens documents list 0xTOKEN dalp tokens documents get-upload-url \ --address 0xTOKEN \ --fileName bond-prospectus.pdf \ --fileSize 2400000 \ --mimeType application/pdf \ --documentType prospectus \ --visibility public dalp tokens documents confirm-upload \ --address 0xTOKEN \ --objectKey uploads/token-documents/example-object-key \ --documentType prospectus \ --fileName bond-prospectus.pdf \ --fileSize 2400000 \ --mimeType application/pdf \ --visibility public dalp tokens documents get-download-url \ --address 0xTOKEN \ --documentId doc_123 dalp tokens documents delete \ --address 0xTOKEN \ --documentId doc_123 ``` Use the API or SDK for the direct file upload step because the CLI returns the presigned URL and object key; it does not upload the local file bytes for you. ## See also [#see-also] * [KYC document uploads](/docs/developer-guides/api-integration/kyc-document-uploads) * [Token lifecycle](/docs/developer-guides/api-integration/token-lifecycle) * [Token holders and transfers](/docs/developer-guides/api-integration/token-holders-transfers) # Token holders and transfers Source: https://docs.settlemint.com/docs/developer-guides/api-integration/token-holders-transfers Query token holders, inspect balances, execute transfers, and understand the controls around standard, allowance-based, forced, and pre-approved transfer workflows. DALP exposes holder and transfer APIs for day-two asset operations after a token is live. Use them to reconcile holder balances, execute transfers, inspect allowances, and operate governed exception workflows such as forced transfers or pre-approved transfers. These APIs do not bypass asset controls. Transfers still execute against the asset's configured identity, compliance, freeze, role, allowance, and approval rules. ## Endpoint summary [#endpoint-summary] The token API exposes the main holder and transfer operations: * `GET /api/v2/tokens/{tokenAddress}/holders` lists holder balances for a token. * `GET /api/v2/tokens/{tokenAddress}/holder-balances` reads one holder balance. * `GET /api/v2/tokens/{tokenAddress}/events` lists indexed token events. * `GET /api/v2/tokens/{tokenAddress}/historical-balances` lists indexed balance checkpoints for tokens with the historical balances feature attached. * `POST /api/v2/tokens/{tokenAddress}/transfers` executes standard or allowance-based transfers. * `POST /api/v2/tokens/{tokenAddress}/burns` burns tokens from one or more holder addresses. * `POST /api/v2/tokens/{tokenAddress}/forced-transfers` executes custodian forced transfers. * `PUT /api/v2/tokens/{tokenAddress}/address-freezes` sets or clears an address freeze. * `POST /api/v2/tokens/{tokenAddress}/partial-freezes` freezes part of a holder balance. * `POST /api/v2/tokens/{tokenAddress}/partial-unfreezes` releases part of a frozen holder balance. * `POST /api/v2/tokens/{tokenAddress}/recoveries` recovers tokens from a lost wallet to the caller's wallet. * `POST /api/v2/tokens/{tokenAddress}/forced-recoveries` recovers tokens from a lost wallet to a specified replacement wallet. * `GET /api/v2/tokens/{tokenAddress}/transfer-approvals` lists transfer approval records. * `POST /api/v2/tokens/{tokenAddress}/transfer-approvals` creates a pre-approved from-to transfer approval. * `POST /api/v2/tokens/{tokenAddress}/transfer-approval-revocations` revokes a transfer approval. Older endpoints also exist for legacy integrations, including `/api/token/{tokenAddress}/holders` and `/api/token/{tokenAddress}/holder`. Use the `/api/v2/tokens/...` endpoints for new integrations because they use path-based token addresses and collection-style pagination. ## List token holders [#list-token-holders] Use the holders endpoint to power cap-table style views, reconciliation jobs, and post-operation checks. ```bash curl "https://your-platform.example.com/api/v2/tokens/0xTOKEN/holders?limit=50&sortBy=-lastUpdatedAt" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` Holder collection items include: * `account.id`: the holder wallet address * `value`: the indexed token balance * `frozen`: the frozen amount in the holder balance * `available`: the spendable balance after frozen amounts and address-level freezes are applied * `isFrozen`: whether the holder address is frozen * `lastUpdatedAt`: the indexed balance update time The collection supports pagination, filtering, and sorting. `lastUpdatedAt` is the default sort field, descending. You can filter by holder address, update time, and frozen-address state. ## Read one holder balance [#read-one-holder-balance] Use the holder-balance endpoint when you need to verify one address before or after an operation. ```bash curl "https://your-platform.example.com/api/v2/tokens/0xTOKEN/holder-balances?holderAddress=0xHOLDER" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` The response contains the same holder balance fields as the holders collection, wrapped in `data.holder`. If the address has no positive indexed balance, the holder field can be `null`. ## List token events [#list-token-events] Use the token events endpoint to read indexed on-chain events for one token. ```bash curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[walletAddress]=0xHOLDER" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` The response uses the canonical collection envelope: * `data`: event items * `meta`: total count and facet counts * `links`: pagination links for the current query The default sort is `-blockTimestamp`, so the newest events are returned first. Supported sortable fields are `blockTimestamp` and `blockNumber`. Supported filters are: * `eventName` * `senderAddress` * `accountAddress` * `walletAddress`, which matches `senderAddress`, `accountAddress`, or the event emitter address; supports only `eq` and `inArray` * `transactionHash`, which uses case-insensitive substring matching by default; use `eq` for exact matches * `blockTimestamp` date range The token address in the path still scopes the result set. A `walletAddress` filter only narrows events for that token. It does not return activity from other tokens, even when the same wallet or feature contract address appears there. Wallet address filters must use the supported operator format: ```bash filter[walletAddress][eq]=0xHOLDER filter[walletAddress][inArray]=0xHOLDER1,0xHOLDER2 ``` Transaction hash shorthand uses substring matching. For an exact transaction hash match, use: ```bash filter[transactionHash][eq]=0xTRANSACTION_HASH ``` Filter by event name: ```bash curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[eventName]=TransferCompleted" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` Filter by sender address: ```bash curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[senderAddress]=0xSENDER" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` Filter by transaction hash: ```bash curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[transactionHash][eq]=0xTRANSACTION_HASH" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` Filter by block timestamp range: ```bash curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?filter[blockTimestamp][gte]=2026-01-01T00:00:00Z&filter[blockTimestamp][lte]=2026-01-31T23:59:59Z" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` Paginate through the token events collection: ```bash curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/events?page[offset]=50&page[limit]=50&sort=-blockTimestamp" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` ## List historical balance checkpoints [#list-historical-balance-checkpoints] Use the historical balances endpoint when you need a block-by-block balance trail for a token that has the historical balances feature attached. The endpoint returns account checkpoint rows by default. Total-supply checkpoints are available with the `kind` filter. ```bash curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/historical-balances?filter[account][eq]=0xHOLDER&sort=-blockNumber" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` Historical balance items include: * `account`: the holder address for account checkpoints, or the zero address for total-supply checkpoints * `kind`: `account` or `totalSupply` * `sender`: the address that triggered the checkpoint * `oldBalance` and `newBalance`: display balance strings * `oldBalanceExact` and `newBalanceExact`: exact smallest-unit values for filtering and reconciliation * `blockNumber`, `blockTimestamp`, `txHash`, and `logIndex`: chain position fields for ordering and replay The endpoint uses the canonical collection envelope with `data`, `meta`, and `links`. The default sort is newest block first. Supported filters include `account`, `kind`, `blockNumber`, `blockTimestamp`, `oldBalance`, and `newBalance`. Use equality filters for the checkpoint discriminators: `filter[account][eq]=0xHOLDER` and `filter[kind][eq]=totalSupply` are the only operator forms for `account` and `kind`. Balance filters use exact smallest-unit values, while the response also includes display balance strings. To include total-supply checkpoints, filter by `kind`: ```bash curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/historical-balances?filter[kind][eq]=totalSupply" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` If the token does not have the historical balances feature attached, the endpoint returns an empty collection envelope. ## Read account activity [#read-account-activity] Use account activity endpoints when you need the event history or activity metrics for one address in the active system: * `GET /api/v2/system/accounts/{accountAddress}/activities` lists indexed events where the address is involved. * `GET /api/v2/system/accounts/{accountAddress}/activity-metrics` returns the activity time series and count for the address. Account activity reads are visibility-scoped. The API returns activity for the caller's own wallet set, participant wallet or identity targets that the caller's role can inspect, active-system feed addresses, and configured account-abstraction infrastructure addresses that the caller's role can inspect. Requests for other arbitrary addresses return an empty collection or zero-count metrics instead of exposing unrelated activity. ## Execute standard transfers [#execute-standard-transfers] Use standard transfers when the authenticated signer is moving its own balance to one or more recipients. ```bash curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/transfers \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "transferType": "standard", "transfers": [ { "recipient": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F", "amount": "1000000000000000000" } ] }' ``` Each request accepts between 1 and 10,000 transfer items. For standard transfers, do not include `from` addresses. Before the API queues a standard transfer, it checks the sender's indexed available balance for the total requested amount. Available balance excludes frozen amounts and returns zero for frozen holder addresses. If the token metadata or latest holder state is not indexed yet, the pre-check can also see zero available balance. If the pre-check rejects the request, reduce the amount or wait for recent token, balance, or freeze changes to index before retrying. ## Execute allowance-based transfers [#execute-allowance-based-transfers] Use `transferFrom` when the operation spends from another address using an allowance. ```bash curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/transfers \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "transferType": "transferFrom", "transfers": [ { "from": "0x8ba1f109551bD432803012645Ac136ddd64DBA72", "recipient": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F", "amount": "1000000000000000000" } ] }' ``` For `transferFrom`, every transfer item must include a `from` address. ## Burn holder balances [#burn-holder-balances] Use burns when an authorized operator needs to remove tokens from one or more holder addresses. The token must support burning, and the signer must have the required token role. ```bash curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/burns \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "addresses": ["0x8ba1f109551bD432803012645Ac136ddd64DBA72"], "amounts": ["1000000000000000000"] }' ``` `addresses` and `amounts` must have the same number of items. A burn request can include up to 100 holder addresses. Amounts use the token's raw base units. Before the API queues a burn, it checks each holder's indexed available balance against the requested amount for that holder. Available balance excludes frozen amounts and returns zero for frozen holder addresses. If token metadata or holder state is not indexed yet, the pre-check can see zero available balance. If a pre-check fails, adjust the burn amount or retry after the indexer reflects the latest token, balance, and freeze state. ## Execute forced transfers [#execute-forced-transfers] Forced transfers are governed exception operations. Use them only when the institution has the proper operating basis and the signer has the required asset role. ```bash curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/forced-transfers \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "transfers": [ { "from": "0x8ba1f109551bD432803012645Ac136ddd64DBA72", "recipient": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F", "amount": "1000000000000000000" } ] }' ``` Forced transfers require matching `from`, `recipient`, and `amount` values for each transfer item. Store the business reason, approval evidence, and resulting transaction hash outside the API call as part of your exception workflow. ## Freeze holder addresses and balances [#freeze-holder-addresses-and-balances] Use freeze operations when a custodian needs to stop or limit transfers for a specific holder address. Address freezes set or clear the holder-level freeze flag. Partial freezes lock a positive amount of one holder's balance, and partial unfreezes release a positive amount that was previously frozen. Set an address freeze: ```bash curl -X PUT https://your-platform.example.com/api/v2/tokens/0xTOKEN/address-freezes \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "userAddress": "0x8ba1f109551bD432803012645Ac136ddd64DBA72", "freeze": true }' ``` Clear an address freeze by sending the same holder address with `freeze` set to `false`: ```bash curl -X PUT https://your-platform.example.com/api/v2/tokens/0xTOKEN/address-freezes \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "userAddress": "0x8ba1f109551bD432803012645Ac136ddd64DBA72", "freeze": false }' ``` The CLI `dalp tokens freeze-address` command sets the address freeze flag. Use the API example above to clear the holder-level flag; partial unfreezes remain a separate operation for releasing a frozen balance amount. Freeze part of a holder balance: ```bash curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/partial-freezes \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "userAddress": "0x8ba1f109551bD432803012645Ac136ddd64DBA72", "amount": "1000000000000000000" }' ``` When the indexer already has a token-holder balance row, the partial-freeze API checks the holder's indexed available balance before queueing the transaction. Available balance excludes amounts that are already frozen. If that pre-check rejects the request, reduce the amount or wait for the indexer to reflect a recent transfer before retrying. If the holder row is not indexed yet, the API can still queue the transaction and the on-chain freeze call enforces the balance constraint. Release part of a frozen balance: ```bash curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/partial-unfreezes \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "userAddress": "0x8ba1f109551bD432803012645Ac136ddd64DBA72", "amount": "1000000000000000000" }' ``` Freeze and unfreeze operations require the custodian role on the token. Use the holder and event endpoints before and after the mutation when your operating process requires evidence of the affected address, amount, and resulting transaction. ## Recover tokens from a lost wallet [#recover-tokens-from-a-lost-wallet] Use recovery operations when a holder has lost access to a wallet and the institution's recovery process has approved a replacement path. Recover tokens from a lost wallet to the caller's wallet: ```bash curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/recoveries \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "lostWallet": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F" }' ``` Force recover tokens from a lost wallet to a specified replacement wallet: ```bash curl -X POST https://your-platform.example.com/api/v2/tokens/0xTOKEN/forced-recoveries \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "lostWallet": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F", "newWallet": "0x8ba1f109551bD432803012645Ac136ddd64DBA72" }' ``` Standard recovery requires the emergency role. Forced recovery requires the custodian role because it specifies both the lost wallet and the replacement wallet. Store the recovery approval, identity evidence, and transaction outcome in your operating record. ## Transfer approval workflows [#transfer-approval-workflows] For assets that use the TransferApproval compliance module, an approval authority can pre-approve transfers from one identity to another for a specific approved amount. The token's configured approval mode determines how that approved amount can be consumed. Use these operations when your transfer process requires explicit maker-checker style approval before a holder initiates the transfer: 1. Configure the token's TransferApproval module with the correct parameter schema for the installed module. Use either exemption/one-time-use settings or approval-mode settings; do not mix them. 2. Create the transfer approval for the source identity, recipient identity, and approved amount. 3. Let the holder initiate a transfer that fits the configured approval mode. 4. List transfer approvals to inspect pending, consumed, or revoked approvals. 5. Revoke stale approvals that should no longer execute. Any configured approval authority can revoke a pending approval. Approval modes apply when the installed TransferApproval module uses approval-mode settings. They determine how the approved amount can be consumed: * `0`: exact amount: one transfer must match the approved value exactly. After that transfer succeeds, the approval is used and cannot be reused. * `1`: up to once: one transfer can use any amount up to the approved value. Transferring less than the approved value still uses the approval. * `2`: up to total: multiple transfers can spend against the approval until the approved total is exhausted. Further transfers require a new approval or a higher approved amount. Expiry still applies in every mode. Approval expiry is configured in seconds, from 1 to 31,536,000 seconds. If the approval expires before it is consumed, create a new approval instead of retrying the stale one. To update an approval, treat the change as a revoke-and-recreate operation: 1. List the approval and confirm it is still `pending`. 2. Revoke the stale approval using the same token, source identity, recipient identity, and amount. The API accepts wallet addresses, or identity address overrides when the identities are already known from the approvals list. 3. Create a new approval with the corrected amount or operating evidence. 4. Re-read the approvals list and store the new approval status in your workflow record. The approval mode is fixed after the module is configured. To change from exact amount to an up-to mode, or between up-to modes, deploy a new TransferApproval module with the desired mode. ## Controls and failure handling [#controls-and-failure-handling] For standard transfers, `transferFrom`, and burns, DALP checks indexed available balances before it submits the on-chain transaction. Available balance excludes frozen amounts. If the requested amount is higher than the available balance, the API returns an error before the transaction is queued. The same pre-queue check normalizes the token and holder addresses. Send valid Ethereum addresses in `0x` format for the token, source holder, and burn holder fields; malformed addresses fail before DALP submits the operation. DALP also rejects transfer or burn requests while the token is paused for that operation. Fix the address, holder balance, frozen amount, or paused token state before retrying. Transfer and burn mutations may fail when DALP or the underlying contracts reject the operation. Common categories include: * missing or insufficient role permissions * paused token state * frozen sender, recipient, or balance state * failed identity or compliance checks * missing allowance for `transferFrom` * stale, revoked, or missing transfer approval * insufficient available balance after frozen amounts are excluded When a transfer or burn fails for insufficient available balance, re-read the holder balance and token events before retrying. The indexed balance may still be catching up after a recent operation, or another operation may have spent or frozen part of the balance since your last read. When a transfer mutation returns a blockchain transaction hash, use the transaction-tracking guide to verify confirmation and recover from timeout cases. See [Transaction tracking](/docs/developer-guides/operations/transaction-tracking). ## Operational guidance [#operational-guidance] For regulated operations, treat holder and transfer APIs as part of the evidence chain: 1. Read holder state before the operation when the workflow requires a balance check. 2. Execute the transfer using the narrowest operation that fits the case: standard, allowance-based, forced, or pre-approved. 3. Capture the transaction hash or action status. 4. Read holder state again after confirmation. 5. Attach approvals, exception reasons, or reconciliation notes to your operating record outside DALP when required by policy. This separates execution from governance evidence while keeping the on-chain operation enforceable by the asset's configured controls. # Token lifecycle Source: https://docs.settlemint.com/docs/developer-guides/api-integration/token-lifecycle Visual flowcharts showing the complete token lifecycle from creation through minting, transfers, and burns with required API calls and permissions. This guide visualizes the token lifecycle through flowcharts for each major operation. Use these diagrams to understand the sequence of API calls, required roles, and decision points when building automated workflows. Asset creation begins the token lifecycle *** ## Create token operation [#create-token-operation] Creating a token deploys a new smart contract and configures its initial parameters. **Prerequisites**: * [API key](/docs/developer-guides/api-integration/api-reference) for authentication * `tokenManager` system role * Registered identity for your wallet **Required inputs**: * **Common**: `type`, `name`, `symbol`, `decimals`, `countryCode`, `initialModulePairs`, `walletVerification` * **Template-created assets**: `templateId`, optional `metadataValues`, optional `featureConfigs` * **Bond-specific**: `faceValue`, `maturityDate`, `denominationAsset` * **Stablecoin-specific**: `priceCurrency`, `basePrice` * **Fund-specific**: `priceCurrency`, `basePrice` When an asset is created from an instrument template, `metadataValues` can fill the template's metadata fields at deployment time. It is only required when the selected template defines required metadata fields; otherwise omitted metadata is treated as an empty object. Fields configured as immutable in the template are locked on the deployed token. Restricted-mutable fields are submitted without that on-chain lock and remain editable through the supported metadata update route, subject to the token `setMetadata` governance permission. Wallet verification for metadata updates follows the specific route and authentication flow: the public input schema models `walletVerification` as optional, while routes that sign transactions may still require a verification payload at runtime. **Auto-granted roles**: When you create a token, you automatically receive: * `admin` – Allows granting other roles on this token * `governance` – Allows configuring compliance modules and token parameters **Creator in API reads**: Token read responses include `createdBy.id`. For DALP-created tokens, this normally identifies the wallet address that submitted the factory creation event. Older token records may return the token factory contract address instead when the creator wallet was not captured. Treat `createdBy.id` as creation attribution for audit views, and check whether the value is a wallet or factory address before assigning human ownership. Do not treat it as the current admin, owner, or only account allowed to manage the token. Token permissions are governed by token roles and wallet verification for each mutation. **Next steps**: 1. Grant `supplyManagement` role (for minting) 2. Grant `emergency` role (for unpausing) 3. Unpause the token 4. Add collateral (stablecoins only) 5. Mint initial supply **Example guides**: * [Bonds](/docs/developer-guides/runbooks/create-mint-bonds) * [Deposits](/docs/developer-guides/runbooks/create-mint-deposits) * [Equities](/docs/developer-guides/runbooks/create-mint-equities) * [Funds](/docs/developer-guides/runbooks/create-mint-funds) * [Stablecoins](/docs/developer-guides/runbooks/create-mint-stablecoins) *** ## V2 scoped compliance modules [#v2-scoped-compliance-modules] V2 tokens can install multiple instances of the same compliance module type when each instance is installed through `POST /api/v2/tokens/{tokenAddress}/compliance-modules/scoped` with both `params` and `scope`. V1 tokens use the legacy single-instance compliance routes instead; calls to scoped install, scoped params-and-scope update, or scope-only update endpoints are rejected for V1 tokens with the scoped-compliance V2-engine errors (`DALP-0434`, `DALP-0435`, or `DALP-0436`). Scoped module requests use the same compliance `params` object as other module configuration calls and add a token-level `scope`. The scope can target senders and receivers by claim expressions (`senderInclusion`, `senderExemption`, `receiverInclusion`, `receiverExemption`) and by ISO 3166-1 numeric country include/exclude arrays (`senderCountryInclusion`, `senderCountryExclusion`, `receiverCountryInclusion`, `receiverCountryExclusion`). The `executionMode` field accepts `0` or `1`. When every scope array is empty and `executionMode` is `0`, all transfers go through the module. Compliance responses can include scoped binding fields: `instanceAddress`, `isActive`, and `scope`. These fields are only present on scoped compliance responses and may be omitted by older or legacy token compliance responses. SDK and UI consumers should guard these fields before reading them for legacy tokens. Use `instanceAddress` when updating a specific instance. To update parameters and scope together under one wallet verification, call `PATCH /api/v2/tokens/{tokenAddress}/compliance-modules/{instanceAddress}/scoped-parameters` instead of chaining a params update with a separate scope update. *** ## Feature operations runbook [#feature-operations-runbook] Some token features add day-two servicing operations after issuance. Use feature endpoints only after the features read endpoint shows the matching feature attached. The legacy bond redemption pool row is the exception: use that top-up only for legacy bonds without the maturity-redemption feature attached. The generated SDK exposes the same token routes; use the SDK operation that corresponds to the endpoint below when you prefer typed calls over direct HTTP. Read feature state before submitting a mutation: | Read purpose | Endpoint | Use before | | ------------------------------ | -------------------------------- | ----------------------------------------------------------------------- | | Attached token features | `GET /features` | Feature mutations; confirm the token actually has the required feature. | | Published conversion triggers | `GET /conversion/triggers` | Holder conversion, forced conversion, or trigger disablement. | | Token events and action status | `GET /events` and `GET /actions` | Operational audit trails after feature mutations. | The features response is returned in `data.configurable`. Check whether `data.configurable` is `null` before reading the feature list. DALP returns `null` when it cannot build a configurable feature block for the token. For example, the token may not be available in the indexed token set yet. When `data.configurable` is present, `features` contains one item per feature contract and `featuresCount` reports the total. Each item includes: * `featureAddress`, `typeId`, and `featureFactory` * `isAttached`, `attachedAt`, and `detachedAt` * feature-specific state blocks, such as `aumFee`, `maturityRedemption`, `fixedTreasuryYield`, `conversion`, or `conversionMinter`, when the feature exposes readable configuration or operational state Feature-specific blocks that do not apply are `null`. Some attached features can have `isAttached: true` without a populated state block when there is no additional read model for that feature. Treat the block as optional feature state, not proof that the feature is attached. When `data.configurable` is present but no feature contracts are discovered for the token, `features` is an empty array and `featuresCount` is `0`. Skip feature routes until the array contains a matching attached feature. Use the feature-specific blocks only for the state fields those routes need to display or prefill. If a feature is created again for the same token, read `GET /api/v2/tokens/{tokenAddress}/features` again before you prefill forms or submit holder actions. DALP exposes the current feature configuration and current read state for the active feature. Do not reuse cached totals, checkpoints, schedules, triggers, or delegation state from the previous feature instance. Run feature operations in this order: 1. Read `GET /api/v2/tokens/{tokenAddress}/features` and skip unsupported feature routes. For legacy bond redemption pool top-ups, use the legacy route only when the maturity-redemption feature is not attached. 2. Check treasury-backed features before execution: confirm the treasury address is configured, verify the treasury has enough denomination-asset balance for the intended claim or redemption, and top up before holders submit payout calls. 3. For configurable features, update governance-controlled rates, recipients, windows, triggers, or exemptions before opening holder operations. 4. Submit the holder, custodian, or governance mutation. Synchronous responses include `data`, `meta.txHashes`, and `links`; async responses return `transactionId`, `status`, and `statusUrl`. 5. Poll `statusUrl` for async requests, or use the token events and actions reads to reconcile the transaction hash and resulting token state. | Feature area | Operation | Endpoint | Required role or signer condition | | --------------------------- | ------------------------------------ | -------------------------------------------------- | ----------------------------------------------------------------------- | | AUM fee | Set rate | `PATCH /aum-fee/bps` | `governance` | | AUM fee | Set recipient | `PATCH /aum-fee/recipient` | `governance` | | AUM fee | Collect accrued fee | `POST /aum-fee/collections` | No token role required | | AUM fee | Permanently freeze rate | `POST /aum-fee/rate-freezes` | `governance` | | Fixed treasury yield | Claim accrued yield | `POST /fixed-treasury-yield/claims` | Wallet-verified caller; holder accrual is enforced on-chain | | Fixed treasury yield | Set treasury | `PATCH /fixed-treasury-yield/treasury` | `governance` | | Fixed treasury yield | Top up treasury | `POST /fixed-treasury-yield/top-ups` | Caller funds the transfer from their own wallet; no token role required | | Fixed treasury yield | Approve treasury allowance | `POST /fixed-treasury-yield/treasury-allowance` | Treasury wallet signs; wallet treasuries only | | Maturity redemption | Mature the asset | `POST /maturity-redemption/maturations` | `governance` | | Maturity redemption | Trigger early maturity | `POST /maturity-redemption/early-maturations` | `emergency` | | Maturity redemption | Set treasury | `PATCH /maturity-redemption/treasury` | `governance` | | Maturity redemption | Top up treasury | `POST /maturity-redemption/top-ups` | Caller funds the transfer from their own wallet; no token role required | | Maturity redemption | Redeem matured tokens | `POST /maturity-redemption/redemptions` | Wallet-verified caller; holder balance is enforced on-chain | | Transaction fee | Read collection history | `GET /transaction-fee/collections` | API key | | Transaction fee | Set mint, burn, and transfer rates | `PATCH /transaction-fee/rates` | `governance` | | Transaction fee | Set recipient | `PATCH /transaction-fee/recipient` | `governance` | | Transaction fee | Freeze rates | `POST /transaction-fee/rate-freezes` | `governance` | | External transaction fee | Set mint, burn, and transfer amounts | `PATCH /external-transaction-fee/amounts` | `governance` | | External transaction fee | Set recipient | `PATCH /external-transaction-fee/recipient` | `governance` | | External transaction fee | Set fee token | `PATCH /external-transaction-fee/token` | `governance` | | External transaction fee | Freeze external fees | `POST /external-transaction-fee/rate-freezes` | `governance` | | Transaction fee accounting | Set accounting rates | `PATCH /transaction-fee-accounting/rates` | `governance` | | Transaction fee accounting | Set accounting recipient | `PATCH /transaction-fee-accounting/recipient` | `governance` | | Transaction fee accounting | Freeze accounting rates | `POST /transaction-fee-accounting/rate-freezes` | `governance` | | Transaction fee accounting | Reconcile accrued fees | `POST /transaction-fee-accounting/reconciliations` | `governance` | | Transaction fee accounting | Set or remove account exemption | `PUT /transaction-fee-accounting/exemptions` | `governance` | | Conversion | Publish trigger | `POST /conversion/triggers` | `governance` | | Conversion | Disable trigger | `POST /conversion/trigger-disablements` | `governance` | | Conversion | Set conversion window | `PATCH /conversion/window` | `governance` | | Conversion | Convert holder tokens | `POST /conversion/conversions` | Wallet-verified caller; holder balance is enforced on-chain | | Conversion | Force convert holder tokens | `POST /conversion/forced-conversions` | `custodian` | | Conversion | Add authorized converter | `POST /conversion/authorized-converters` | `governance` | | Conversion | Remove authorized converter | `DELETE /conversion/authorized-converters` | `governance` | | Legacy bond redemption pool | Top up legacy denomination pool | `POST /redemptions/denomination-top-ups` | Caller funds the transfer from their own wallet; no token role required | All endpoints in the table are under `/api/v2/tokens/{tokenAddress}`. Treasury top-ups transfer denomination asset from the caller's wallet to the configured feature treasury or legacy redemption pool; they do not mint new payout assets. Collection reads return paginated `data`, `meta`, and `links` responses and do not submit transactions. For wallet treasuries, the fixed treasury yield allowance endpoint approves the yield schedule to spend denomination asset from the treasury when holders claim yield. Contract treasuries do not use that wallet approval flow. Holder-bound claim, redemption, and conversion endpoints verify the caller wallet before queue submission, but the eligible balance, principal, or accrual check happens in the feature contract. A non-holder or holder without an eligible amount can reach the queue and then fail or revert during on-chain execution. User-visible failures usually mean the feature is not attached, the caller lacks the listed role, wallet verification is missing or expired, a holder-bound operation has no eligible on-chain balance, accrual, or principal, a treasury-backed payout has insufficient denomination-asset funding, a conversion trigger is inactive or outside its window, a fee rate has been frozen, or the transaction queue accepted the request but later reports `failed`. Treat timeout responses as unknown status: check the returned transaction status or token events before retrying to avoid duplicate operations. *** ## Mint tokens operation [#mint-tokens-operation] Minting increases the token supply and sends tokens to specified recipients. **Prerequisites**: * [API key](/docs/developer-guides/api-integration/api-reference) for authentication * `supplyManagement` token role * Token must be unpaused (requires `emergency` role) * Recipients must have registered identities * For stablecoins: sufficient collateral must be added (requires trusted issuer status) **Required inputs**: * `tokenAddress` – Contract address from token creation * `recipients` – Array of wallet addresses * `amounts` – Array of amounts in smallest unit (BigInt) * `walletVerification` – PINCODE verification **Validation checks**: 1. Token is unpaused 2. Caller has `supplyManagement` role 3. Minting does not exceed cap (if set) 4. Recipients have registered identities 5. Stablecoins: sufficient collateral exists **Amount calculation**: All tokens use 18 decimals. To mint 100 tokens: ```ts import { from } from "dnum"; const amount = from("100", 18); // 100 tokens ``` **Example**: ```ts import { from } from "dnum"; await client.token.mint({ tokenAddress: "0xABCD...", recipients: ["0x1234...", "0x5678..."], amounts: [from("100", 18), from("200", 18)], walletVerification: { verificationType: "PINCODE", secretVerificationCode: "123456", }, }); ``` *** ## Burn tokens operation [#burn-tokens-operation] Burning permanently reduces the token supply by destroying tokens from the caller's balance. Before DALP queues a burn, the API checks the holder's indexed available balance. Frozen balance is not spendable. If the requested raw amount is greater than the holder's available balance, the API rejects the request before submitting an on-chain transaction. **Prerequisites**: * [API key](/docs/developer-guides/api-integration/api-reference) for authentication * `supplyManagement` token role * Sufficient available token balance to burn **Required inputs**: * `tokenAddress` – Contract address * `amount` – Amount to burn in smallest unit (BigInt) * `walletVerification` – PINCODE verification **Validation checks**: 1. Caller has `supplyManagement` role 2. Caller has sufficient available balance to burn 3. Amount is greater than zero **Example**: ```ts import { from } from "dnum"; await client.token.burn({ tokenAddress: "0xABCD...", amount: from("50", 18), walletVerification: { verificationType: "PINCODE", secretVerificationCode: "123456", }, }); ``` **Use cases**: * Reduce supply after redemptions * Adjust stablecoin supply to match collateral * Retire tokens from circulation *** ## Transfer tokens operation [#transfer-tokens-operation] Transfers send tokens from the caller's balance to a recipient. All transfers undergo compliance checks unless bypassed with a forced transfer. Before DALP queues a standard transfer or `transferFrom`, the API checks the indexed available balance for the source address. For standard transfers, the source is the authenticated sender. For `transferFrom`, the source is the `from` address. Frozen balance is not spendable. **Prerequisites**: * [API key](/docs/developer-guides/api-integration/api-reference) for authentication * Token holder with sufficient available balance * Recipient must have registered identity * Token must not be paused * Sender and recipient addresses must not be frozen **Required inputs**: * `tokenAddress` – Contract address * `to` – Recipient wallet address * `amount` – Amount to transfer in smallest unit (BigInt) * `walletVerification` – PINCODE verification **Compliance checks** (automatic): 1. Sender has registered identity 2. Recipient has registered identity 3. Transfer satisfies all active compliance modules (e.g., allowlist, country restrictions, lock-up periods) 4. Token is not paused 5. Sender and recipient addresses are not frozen **Example**: ```ts import { from } from "dnum"; await client.token.transfer({ tokenAddress: "0xABCD...", to: "0x1234...", amount: from("25", 18), walletVerification: { verificationType: "PINCODE", secretVerificationCode: "123456", }, }); ``` **Use cases**: * Send tokens to another investor * Distribute tokens to multiple recipients * Transfer tokens to a custody wallet *** ## Forced transfer operation [#forced-transfer-operation] Forced transfers bypass compliance checks and move tokens between addresses. This is a custodian operation used for regulatory interventions, court orders, or operational recovery. **Prerequisites**: * [API key](/docs/developer-guides/api-integration/api-reference) for authentication * `custodian` token role * Source wallet must have sufficient balance * Destination wallet must not be frozen (source can be frozen) **Required inputs**: * `tokenAddress` – Contract address * `from` – Source wallet address * `to` – Recipient wallet address * `amount` – Amount to transfer in smallest unit (BigInt) * `walletVerification` – PINCODE verification **Compliance bypass**: Forced transfers **skip all compliance checks** including: * Identity verification * Allowlist restrictions * Country/jurisdiction rules * Lock-up periods * Frozen sender addresses (but not frozen recipient addresses) **Example**: ```ts import { from } from "dnum"; await client.token.forcedTransfer({ tokenAddress: "0xABCD...", from: "0x1234...", // Source wallet (can be frozen) to: "0x5678...", // Destination (must not be frozen) amount: from("100", 18), walletVerification: { verificationType: "PINCODE", secretVerificationCode: "123456", }, }); ``` **Use cases**: * Regulatory seizure or forfeiture * Court-ordered asset recovery * Operational recovery from compromised wallets * Resolving stuck transfers due to compliance failures **Audit trail**: Every forced transfer emits a `ForcedTransfer` event on-chain with: * `from` – Source address * `to` – Destination address * `amount` – Transferred amount * `executor` – Custodian who executed the transfer * `timestamp` – Block timestamp Review forced transfers in the platform's audit log or via `token.events`. *** ## Next steps [#next-steps] Token analytics track performance throughout the lifecycle * **Asset-specific guides** – Follow step-by-step tutorials for each asset type: * [Bonds](/docs/developer-guides/runbooks/create-mint-bonds) * [Deposits](/docs/developer-guides/runbooks/create-mint-deposits) * [Equities](/docs/developer-guides/runbooks/create-mint-equities) * [Funds](/docs/developer-guides/runbooks/create-mint-funds) * [Stablecoins](/docs/developer-guides/runbooks/create-mint-stablecoins) * **[API reference](/docs/developer-guides/api-integration/api-reference)** – Explore the OpenAPI spec and generate clients # Token volume statistics Source: https://docs.settlemint.com/docs/developer-guides/api-integration/token-volume-statistics Read indexed transfer-volume history for a token through the DALP API. Token volume statistics return the transfer volume history for one token. Use this endpoint when an integration needs chart data or a reconciliation check for token activity over time. ## Read token volume history [#read-token-volume-history] Call the token statistics endpoint with the token address in the path. The optional `days` query parameter selects the trailing window. ```bash curl "https://your-platform.example.com/api/v2/tokens/0xTOKEN/stats/volume?days=30" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` The response uses the single-resource envelope. The `volumeHistory` array contains one point per period: ```json { "data": { "volumeHistory": [ { "timestamp": "2026-03-24T00:00:00.000Z", "totalVolume": "1250000000000000000000" } ] }, "links": { "self": "/v2/tokens/0xTOKEN/stats/volume" } } ``` ## Parameters and fields [#parameters-and-fields] | Field | Type | Notes | | ----------------------------- | ------------ | ------------------------------------------------------------------------------- | | `tokenAddress` | path string | Token contract address. | | `days` | query number | Optional trailing range in days. Defaults to 30. Minimum 1, maximum 365. | | `volumeHistory[].timestamp` | timestamp | Period timestamp in UTC. | | `volumeHistory[].totalVolume` | string | Total transferred amount for the period, returned as a raw-unit decimal string. | For short ranges up to two days, DALP returns hourly points. For longer ranges, DALP returns daily points. The current incomplete hour or day is included when it has transfer volume. ## How the value is calculated [#how-the-value-is-calculated] The statistic is indexer-backed. DALP sums indexed `TransferCompleted` events for the requested token and returns the amount as a string so clients do not lose precision when token amounts exceed JavaScript number limits. The endpoint is for reading indexed activity, not for executing transfers. To inspect the underlying events or reconcile holder balances, use the [token holders and transfers API](/docs/developer-guides/api-integration/token-holders-transfers). # Webhook endpoints Source: https://docs.settlemint.com/docs/developer-guides/api-integration/webhook-endpoints Configure DALP webhook endpoints, delivery privacy, signing secrets, retries, and chain-of-custody proofs. Webhook endpoints deliver selected DALP events to an external HTTPS URL. Use them when an integration needs pushed event delivery instead of polling token or account collections. ## Endpoint model [#endpoint-model] Create endpoints with `POST /api/v2/webhooks`. The request includes: | Field | Behaviour | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | `url` | Required HTTPS target URL for delivery. | | `displayName` | Optional label, up to 200 characters. | | `subscriptions` | Event patterns to deliver. Defaults to `*.final`, `*.retracted`, and `*.recalled`. | | `defaultPayloadShape` | Must be `thin` when creating an endpoint. Switch to `fat` later with a `PATCH` request and the required field acknowledgement. | | `counterSignedReceipts` | Optional flag for endpoints that return signed delivery receipts. | The create and rotate-secret responses reveal the signing secret once. Later reads return endpoint metadata and secret status, not the cleartext signing secret. ## Payload privacy [#payload-privacy] DALP delivers thin payloads by default. Thin payloads omit configured personal-data fields for event types such as identity registration, access-control role changes, asset issuance, compliance freeze recalls, and token transfers. Create the endpoint as `thin` first. Switching an endpoint to `fat` requires a later `PATCH /api/v2/webhooks/{id}` request with a `fatEventsAcknowledgment.fieldsAcknowledged` list that covers every additional field implied by the endpoint's subscriptions. DALP rejects the update when the acknowledgement does not match the subscription set. ## Delivery operations [#delivery-operations] | Operation | API route | | ------------------------------------------------------------------------- | ------------------------------------------------------------ | | List endpoints | `GET /api/v2/webhooks` | | Read endpoint metadata | `GET /api/v2/webhooks/{id}` | | Update URL, subscriptions, payload shape, receipt mode, or disabled state | `PATCH /api/v2/webhooks/{id}` | | Disable an endpoint | `DELETE /api/v2/webhooks/{id}` | | Enqueue a test event | `POST /api/v2/webhooks/{id}/test-events` | | List delivery attempts | `GET /api/v2/webhooks/{id}/deliveries` | | Read one delivery attempt | `GET /api/v2/webhooks/{id}/deliveries/{deliveryId}` | | Retry one delivery event | `POST /api/v2/webhooks/{id}/deliveries/{deliveryId}/retries` | | Replay historical events | `POST /api/v2/webhooks/{id}/replays` | | Recall an event | `POST /api/v2/webhooks/events/{evtId}/recall` | | Get chain-of-custody proof | `GET /api/v2/webhooks/events/{evtId}/chain-of-custody` | | Rotate the signing secret | `POST /api/v2/webhooks/{id}/rotate-secret` | | Revoke the previous signing secret | `POST /api/v2/webhooks/{id}/revoke-previous-secret` | | Read delivery statistics | `GET /api/v2/webhooks/stats` | When updating an endpoint URL while deliveries are pending, pass `acknowledgePending=true` only when you intend DALP to retarget those queued attempts to the new URL. Secret rotation keeps the previous signing secret valid for a 24-hour overlap. Revoke the previous secret after DALP has observed delivery under the new secret. ## Audit proof [#audit-proof] DALP records delivery rows with the fields that were redacted during delivery preparation. It also records hop hashes for the prepared payload. Chain-of-custody proofs return the event's hop hashes, Merkle root, and platform signature. Downstream systems can use that proof to verify what DALP delivered. Related pages: * [Operational integration patterns](/docs/developer-guides/api-integration/operational-integration-patterns) * [Compliance providers](/docs/architecture/integrations/compliance-providers) * [API reference](/docs/developer-guides/api-integration/api-reference) # XvP settlement flows Source: https://docs.settlemint.com/docs/developer-guides/api-integration/xvp-settlement-flows Create, approve, execute, and monitor XvP settlement flows through DALP APIs, SDKs, and CLI commands. # XvP settlement flows [#xvp-settlement-flows] XvP settlements coordinate value-transfer legs between participants. Use this flow to create a settlement, collect approvals, execute the settlement, or recover funds. DALP exposes XvP through the API, SDK, and CLI. The public API and SDK share the same settlement model. Integrations can create settlements programmatically and reconcile them through the platform UI or CLI. ## When to use this flow [#when-to-use-this-flow] Use an XvP settlement when: * a system has the XvP settlement add-on factory available, * at least one transfer leg is a local on-chain flow managed by the active DALP system, * participants need to approve the settlement before execution, * your integration needs to track approval, cancellation, execution, withdrawal, or secret-reveal state. Do not use XvP documentation as a substitute for the API Reference. Use the [API Reference](/docs/developer-guides/api-integration/api-reference) for exact request and response fields, and the [CLI Command Reference](/docs/developer-guides/cli/command-reference) for command syntax. ## Create a settlement [#create-a-settlement] Create a settlement with the XvP add-on factory address, a name, a future cutoff date, and an array of flows. Each flow includes an asset address, sender, recipient, and amount. DALP supports two flow types: | Flow type | What it represents | Extra fields | | ---------- | --------------------------------------------------------------------------------- | ------------------------------------------------- | | `local` | A transfer leg on the active DALP-managed chain. | None beyond asset, sender, recipient, and amount. | | `external` | A transfer leg that references an external chain asset as part of the settlement. | External chain ID and external asset decimals. | Every settlement must include at least one `local` flow. The factory address must belong to an XvP settlement add-on registered for the active system. If any flow is `external`, provide either a raw secret or a precomputed hashlock. Local-only settlements do not require a secret or hashlock. For systems using the current XvP settlement factory identity model, include the ISO 3166-1 numeric country code required by the factory. ```ts const created = await client.addons.xvp.create({ body: { factoryAddress: "0xFACTORY", name: "Primary sale settlement", autoExecute: false, cutoffDate: new Date("2026-06-30T17:00:00Z"), country: 756, flows: [ { type: "local", assetId: "0xASSET", from: "0xSENDER", to: "0xRECIPIENT", amount: "1000000", }, ], walletVerification, }, }); const settlementAddress = created.data.settlementId; ``` The create response returns the queued transaction hash and the settlement contract address after DALP resolves the created settlement. When you provide a raw secret, DALP stores the encrypted secret so authorized decrypt flows can retrieve it later; when you provide only a hashlock, your integration keeps the matching secret. ## List, approve, and execute [#list-approve-and-execute] After creation, participants can list settlements and submit approvals from their own wallet context. The list operation applies participant visibility. The read operation fetches a known settlement by address within the active system and chain; it is not restricted to the caller's participant membership, so do not treat read as a participant-visibility check. ```ts const settlements = await client.addons.xvp.list({ query: { page: { limit: 10, offset: 0 } }, }); const approval = await client.addons.xvp.approve({ body: { settlementAddress, walletVerification }, }); ``` Approval can complete synchronously or return an async acceptance with a `statusUrl`. When token allowance must be queued first, the first async acceptance can represent the token approval step, not the final settlement approval. Wait for the status URL to reach a terminal state, then call `approve` again if settlement approval still needs to be submitted. Read the settlement after the approval flow completes. ```ts const settlement = await client.addons.xvp.read({ params: { settlementAddress }, }); console.log(settlements.meta.total, settlement.data.userApproved); ``` Use the read response to check: * whether the current user has approved, * each recorded approval account and timestamp, * settlement flags such as executed, cancelled, withdrawn, and secret-revealed, * the settlement flows and their local or external status. When the settlement is ready, execute it: ```ts await client.addons.xvp.execute({ body: { settlementAddress, walletVerification }, }); ``` If a participant needs to back out before execution, use approval revocation: ```ts await client.addons.xvp.revokeApproval({ body: { settlementAddress, walletVerification }, }); ``` If the settlement should not proceed, use cancellation: ```ts await client.addons.xvp.cancel({ body: { settlementAddress, walletVerification }, }); ``` Use `withdraw-cancel` to withdraw a cancellation proposal before the final cancellation state. Use `withdraw-expired` for expired settlement recovery. ## Secret and hashlock handling [#secret-and-hashlock-handling] External-flow settlements require either a raw secret or a hashlock when they are created. * If you provide a raw secret, DALP derives the hashlock and stores the encrypted secret for later retrieval. * If you provide a hashlock, your integration is responsible for managing the matching secret. * Use the decrypt operation only when your integration needs to retrieve a stored secret through the API. * Use the reveal-secret operation to publish the secret for hashlock-based settlement completion when that path applies. Local-only settlements do not use hashlock enforcement. ## CLI coverage [#cli-coverage] The DALP CLI exposes XvP create, read, and lifecycle operations under `dalp xvp-settlements`: ```bash dalp xvp-settlements list dalp xvp-settlements read 0xSETTLEMENT dalp xvp-settlements create --factory-address 0xFACTORY --name "Primary sale settlement" --cutoff-date 2026-06-30T17:00:00Z --flows '[{"type":"local","assetId":"0xASSET","from":"0xSENDER","to":"0xRECIPIENT","amount":"1000000"}]' dalp xvp-settlements approve 0xSETTLEMENT dalp xvp-settlements revoke-approval 0xSETTLEMENT dalp xvp-settlements execute 0xSETTLEMENT dalp xvp-settlements cancel 0xSETTLEMENT dalp xvp-settlements withdraw-cancel 0xSETTLEMENT dalp xvp-settlements withdraw-expired 0xSETTLEMENT dalp xvp-settlements decrypt 0xSETTLEMENT dalp xvp-settlements reveal-secret --address 0xSETTLEMENT --secret "shared-secret-value-at-least-32-chars" ``` The CLI create command accepts the factory address, name, cutoff date, and flows JSON. Use the API or SDK for creation scenarios that require a V3 country code, a raw secret, or a precomputed hashlock. ## Related references [#related-references] * [API integration getting started](/docs/developer-guides/api-integration/getting-started) * [API Reference](/docs/developer-guides/api-integration/api-reference) * [CLI Command Reference](/docs/developer-guides/cli/command-reference) # Yield coverage statistics Source: https://docs.settlemint.com/docs/developer-guides/api-integration/yield-coverage-statistics Read indexed yield funding and allowance coverage for a fixed-treasury-yield token through the DALP API. Yield coverage statistics show whether a token's configured yield schedule has enough denomination asset balance and wallet allowance for holder claims. Use this endpoint before prompting a treasury wallet to approve allowance or when building monitoring around fixed treasury yield programmes. ## Read yield coverage [#read-yield-coverage] Call the token statistics endpoint with the token address in the path. ```bash curl "https://your-platform.example.com/api/v2/tokens/0xTOKEN/stats/yield-coverage" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` The response uses the single-resource envelope: ```json { "data": { "yieldCoverage": 150, "hasYieldSchedule": true, "isRunning": true, "totalUnclaimedYield": "500.000000000000000000", "denominationAssetBalance": "750.000000000000000000", "denominationAssetTreasuryAllowance": "2000.000000000000000000", "allowanceCoveredPercentage": "100", "requiredAllowance": "2000.000000000000000000", "treasuryIsContract": false, "treasuryAddress": "0x1111111111111111111111111111111111111111", "scheduleAddress": "0x2222222222222222222222222222222222222222" }, "links": { "self": "/v2/tokens/0xTOKEN/stats/yield-coverage" } } ``` ## Fields [#fields] | Field | Type | Notes | | ------------------------------------ | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | `tokenAddress` | path string | Token contract address. | | `yieldCoverage` | number | Percentage of total unclaimed yield covered by the available denomination asset balance. Values can exceed 100 when the treasury is overfunded. | | `hasYieldSchedule` | boolean | Whether DALP has indexed a yield schedule for the token. | | `isRunning` | boolean | Whether the current time is between the indexed yield schedule start and end dates. | | `totalUnclaimedYield` | decimal string | Total indexed unclaimed yield amount. | | `denominationAssetBalance` | decimal string | Available denomination asset balance used for the yield coverage calculation. | | `denominationAssetTreasuryAllowance` | decimal string | ERC-20 allowance granted by the treasury wallet to the yield schedule contract. | | `allowanceCoveredPercentage` | decimal string | Percentage of `requiredAllowance` covered by `denominationAssetTreasuryAllowance`. The percentage is capped at full coverage. | | `requiredAllowance` | decimal string | Suggested allowance for keeping holder claims unblocked. DALP sizes it as indexed unclaimed yield plus one period of headroom. | | `treasuryIsContract` | boolean or null | `true` for a contract treasury, `false` for a wallet treasury, and `null` when the treasury has not been classified yet. | | `treasuryAddress` | address or null | Configured yield treasury address, or `null` when no fixed-treasury-yield feature row is indexed. | | `scheduleAddress` | address or null | Yield schedule contract address. For wallet treasuries, this is the spender that needs the denomination asset allowance. | Amount fields are serialized as locale-independent decimal strings. Preserve them as strings or decimal-safe values in clients so large token amounts do not lose precision. ## Empty and indexing states [#empty-and-indexing-states] If the token has no indexed yield schedule, DALP returns `hasYieldSchedule: false`, zero balance and allowance fields, and `null` treasury and schedule addresses. If the schedule exists but the treasury feature row has not indexed yet, DALP can return `hasYieldSchedule: true` and a `scheduleAddress` while `treasuryAddress` and `treasuryIsContract` are `null`. Treat that as an indexing catch-up state and retry later instead of prompting for allowance. ## Wallet allowance guidance [#wallet-allowance-guidance] Only prompt for an allowance transaction when `treasuryIsContract` is `false` and the allowance is below the required amount. Prefer the direct amount comparison: * `denominationAssetTreasuryAllowance` is lower than `requiredAllowance` You can also use `allowanceCoveredPercentage` for display, but only treat it as undercovered when `requiredAllowance` is greater than zero. Do not ask a contract treasury to approve wallet allowance. When `treasuryIsContract` is `true`, contract-specific treasury logic supplies the payout path. When `treasuryIsContract` is `null`, wait for indexing to classify the treasury before deciding which action to show. ## How values are calculated [#how-values-are-calculated] The statistic is indexer-backed. DALP reads the indexed yield schedule, unclaimed yield totals, denomination asset balance, fixed-treasury-yield feature row, and ERC-20 allowance row for the active chain. `yieldCoverage` compares the available denomination asset balance with total unclaimed yield. `allowanceCoveredPercentage` compares the indexed allowance with `requiredAllowance` and caps the percentage at full coverage, while still returning the actual allowance amount. ## Related [#related] * [Fixed Treasury Yield](/docs/architecture/components/token-features/fixed-treasury-yield) * [Token volume statistics](/docs/developer-guides/api-integration/token-volume-statistics) * [Token holders and transfers](/docs/developer-guides/api-integration/token-holders-transfers) * [Asset decimals](/docs/developer-guides/api-integration/asset-decimals) # Create asset Source: https://docs.settlemint.com/docs/developer-guides/asset-creation/create-asset Deploy a new tokenized asset using the API. Creating an asset through the API deploys a new tokenized security on the blockchain. Asset Designer 7-step creation wizard For the web interface approach, see the [user guide](/docs/user-guides/asset-creation/create-asset). **Common use cases:** * **Bond issuance** - Create fixed-income securities with maturity dates and coupon payments * **Equity tokenization** - Issue shares with voting rights and dividend distribution * **Fund launch** - Create investment fund units with NAV tracking * **Stablecoin creation** - Deploy collateral-backed stable value instruments * **Deposit certificates** - Tokenize bank deposits or cash equivalents * **Real estate fractionalization** - Divide property ownership into tradeable fractions * **Precious metal tokenization** - Configure metal-backed assets with weight, purity, storage, and valuation fields ## Prerequisites [#prerequisites] * Platform URL (e.g., `https://your-platform.example.com`) * API key from a user with the **Token Manager** role (see [Getting Started](/docs/developer-guides/api-integration/getting-started)) * Token factory for your desired asset type must be deployed * Wallet verification method enabled on your account (pincode or 2FA) ## Asset types [#asset-types] The platform supports seven asset types organized into four classes: | Asset class | Asset types | Description | | ---------------- | --------------------------- | ------------------------------------------------------- | | Fixed Income | Bond | Debt securities with maturity dates and fixed payments | | Flexible Income | Equity, Fund | Variable return assets like shares and investment funds | | Cash Equivalent | Stablecoin, Deposit | Stable value assets pegged to fiat currencies | | Real World Asset | Real Estate, Precious Metal | Tokenized physical assets and property ownership | ## Create an asset from an instrument template [#create-an-asset-from-an-instrument-template] Use `/api/v2/tokens` for the Asset Designer flow. Templated assets use `"type": "dalp-asset"` and must include the selected instrument template ID. The template supplies the asset class and required features; your payload supplies the asset identity, valuation fields, template metadata values, optional feature configuration, and wallet verification. ```bash curl -X POST "https://your-platform.example.com/api/v2/tokens" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "dalp-asset", "templateId": "system-precious-metal", "name": "ACME Gold Token", "symbol": "ACMEG", "decimals": 3, "priceCurrency": "USD", "basePrice": "100.00", "countryCode": "756", "metadataValues": { "metalType": "gold", "purityGrade": "999.9", "vaultLocation": "Zurich", "custodian": "ACME Vault Services", "weightUnit": "troy-oz", "weightPerToken": 1, "spotPrice": 100 }, "featureConfigs": {}, "initialModulePairs": [], "walletVerification": { "secretVerificationCode": "123456", "verificationType": "PINCODE" } }' ``` If your operator selected a compliance template in the wizard, send the chosen template ID in `complianceTemplateSelection`, include the template's declared compliance type IDs in `complianceTemplateExpectedTypeIds`, and submit every declared control in `initialModulePairs`. DALP rejects templated asset creation when the template signal is present but the payload omits the required compliance pairs, submits only part of the expected set, or sends an empty country allow list. ### Request parameters [#request-parameters] | Parameter | Type | Required | Description | | ------------------------------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------- | | `type` | string | Yes | Use `"dalp-asset"` for assets created from instrument templates. Legacy token types remain supported. | | `templateId` | string | Yes | Instrument template ID selected for the asset. Required when `type` is `"dalp-asset"`. | | `name` | string | Yes | Full name (1-50 characters). | | `symbol` | string | Yes | Trading symbol (1-24 characters, uppercase letters and numbers only). | | `decimals` | number | Yes | Decimal precision (0-18). | | `countryCode` | string | Yes | ISO 3166 numeric country code for the asset jurisdiction. | | `priceCurrency` | string | No | ISO 4217 currency code (`"USD"`, `"EUR"`, `"GBP"`) for valuation. | | `basePrice` | string | No | Fiat value per token in the selected price currency. | | `metadataValues` | object | No | Values for metadata fields exposed by the selected instrument template. | | `featureConfigs` | object | No | Configuration for template-enabled token features that need operator inputs. | | `initialModulePairs` | array | Yes | Compliance module pairs submitted at creation. Use an empty array only when no creation-time pairs apply. | | `complianceTemplateSelection` | string | No | Compliance template selected in the wizard, when a compliance template applies. | | `complianceTemplateExpectedTypeIds` | array | No | Compliance type IDs declared by the selected template; used to verify every required control was submitted. | | `walletVerification` | object | Yes | Your wallet verification. | | `walletVerification.secretVerificationCode` | string | Yes | Your 6-digit PINCODE or TOTP code. | | `walletVerification.verificationType` | string | Yes | `"PINCODE"`, `"OTP"`, or `"SECRET_CODES"`. | ### Compliance modules [#compliance-modules] Enable on-chain compliance rules that are automatically enforced: | Module | ID | Description | | -------------------------------------------------------------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | [Identity verification](/docs/developer-guides/compliance/smart-identity-verification) | `identityVerification` | Requires verified OnchainID for all transfers. [Learn how to configure →](/docs/developer-guides/compliance/smart-identity-verification) | | Country allow list | `countryAllowList` | Only specified jurisdictions can hold the asset | | Country block list | `countryBlockList` | Blocked jurisdictions cannot hold the asset | | Address block list | `addressBlockList` | Explicitly block specific wallet addresses | | Investor count limit | `investorCountLimit` | Cap maximum number of unique holders | | Time lock | `timeLock` | Enforce minimum holding period | | Transfer approval | `transferApproval` | Require manual approval for each transfer | | [Collateral requirement](/docs/developer-guides/compliance/collateral) | `collateralRequirement` | Requires collateral backing before minting. [Learn how to configure →](/docs/developer-guides/compliance/collateral) | When an API payload uses a compliance template, include all controls declared by that template in the submitted compliance module configuration. DALP rejects template-selected asset creation when no module pairs are submitted, when the submitted controls do not cover the template, or when a country allow-list control has an empty values array. Each `initialModulePairs` entry uses the compliance type ID, deployed module address, and values for that control: ```json { "typeId": "country-allow-list-v2", "module": "0x...", "values": ["756"] } ``` Use the compliance type IDs returned by your compliance template or module discovery response. Compliance module configuration ### Response [#response] A successful response returns the created asset data (showing key fields): ```json { "data": { "id": "0x9459D52E60edBD3178f00F9055f6C117a21b4220", "type": "precious-metal", "createdAt": "2025-01-15T10:30:00.000Z", "name": "ACME Gold Token", "symbol": "ACMEG", "decimals": 3, "basePrice": "[\"100\",0]", "basePriceCurrencyCode": "USD", "totalSupply": "[\"0\",0]", "pausable": { "paused": true } }, "meta": { "txHashes": ["0xabc123..."] }, "links": { "self": "/v2/tokens" } } ``` The response includes the full asset object with additional fields like `identity`, `accessControl`, `complianceModuleConfigs`, `userPermissions`, and `stats`. Note that the asset starts paused by default. Review and deploy confirmation ## After deployment [#after-deployment] Once deployed, your asset is **paused by default**. To activate it for transfers and minting: 1. **Unpause the asset** - See [Pause/Unpause Assets](/docs/developer-guides/asset-servicing/pause-unpause-asset) 2. **Add supply managers** - Grant roles for minting (see [Change Asset Admin Roles](/docs/developer-guides/asset-servicing/change-asset-admin-roles)) 3. **Mint initial supply** - Issue assets to investors (see [Mint Assets](/docs/developer-guides/asset-servicing/mint-assets)) ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | "Token factory not found" | Deploy the factory for your asset type first | | "Symbol already exists" | Choose a unique symbol not used by other assets | | "Insufficient gas funds" | Ensure your wallet has enough native currency for gas | | "Invalid country code" | Use ISO 3166-1 numeric codes (e.g., 840=USA, 276=Germany) | | "Compliance module not found" | Check available modules with `GET /api/system/compliance-modules` | | "Compliance template requires controls" | Submit every compliance control declared by the selected template, or switch to manual configuration. | | "country-allow-list requires at least one country code" | Add at least one country code, remove the country allow-list control, or choose a different template. An empty allow list would block every transfer. | ## Related guides [#related-guides] * [Pause/Unpause Assets](/docs/developer-guides/asset-servicing/pause-unpause-asset) - Activate the asset via API * [Change Asset Admin Roles](/docs/developer-guides/asset-servicing/change-asset-admin-roles) - Grant roles for minting * [Mint Assets](/docs/developer-guides/asset-servicing/mint-assets) - Issue assets to investors # Change asset admin roles Source: https://docs.settlemint.com/docs/developer-guides/asset-servicing/change-asset-admin-roles Grant or revoke administrator roles for specific assets via API. This guide explains how to programmatically modify administrator roles for specific assets using the DALP API. Use these endpoints to grant or revoke asset-level permissions for managing individual assets. For the web interface approach, see the [user guide](/docs/user-guides/asset-servicing/change-asset-admin-roles). ## Prerequisites [#prerequisites] * Platform URL (e.g., `https://your-platform.example.com`) * API key from a user with the **admin** role on the target asset (see [Getting Started](/docs/developer-guides/api-integration/getting-started) for API key setup) * Wallet verification method enabled on your account (e.g., pincode or 2FA) * Asset contract address of the asset to modify ## When to change asset admin roles [#when-to-change-asset-admin-roles] ### Common scenarios [#common-scenarios] * **Initial deployment** — Assign operators after asset creation * **Role expansion** — Operator taking on additional responsibilities * **Role reduction** — Removing permissions no longer needed * **Team changes** — Transferring responsibilities between operators * **Automation** — Integrating role management into provisioning workflows ### Security considerations [#security-considerations] * Follow principle of least privilege * Remove roles when no longer needed * Document role changes for compliance * Coordinate with affected operators ## About asset admin roles [#about-asset-admin-roles] Each asset has its own set of administrators with specific roles: | Role | Description | Common use cases | | ------------------ | -------------------------------------------------------- | ------------------------------------------------------- | | `admin` | Permission management for the asset | Manage other administrators' roles and permissions | | `custodian` | Freeze addresses, force transfers, and asset recovery | Custody/operations teams handling interventions | | `emergency` | Pause/unpause and ERC20 recovery actions | Incident response team, post-deploy unpausing | | `governance` | Asset policy, verification, and compliance settings | Team tuning compliance modules or governance parameters | | `supplyManagement` | Mint and burn permissions via `/api/token/:address/mint` | Operators issuing, redeeming, or retiring supply | Asset roles are specific to individual assets. Each asset has its own access control. Platform system roles (like `tokenManager`) control platform-wide capabilities. See [Change Admin Roles](/docs/developer-guides/platform-setup/change-admin-roles) for system roles. ## Changing roles [#changing-roles] ### Get asset details [#get-asset-details] Query the asset to review current role assignments: ```bash curl -X GET "https://your-platform.example.com/api/token/0x9459D52E60edBD3178f00F9055f6C117a21b4220" \ -H "X-Api-Key: YOUR_API_KEY" ``` The asset contract address is part of the URL path: `/api/token/{assetAddress}` **Response (relevant fields):** ```json { "id": "0x9459D52E60edBD3178f00F9055f6C117a21b4220", "name": "Example Asset", "symbol": "EXA", "decimals": 18, "accessControl": { "id": "0x1234567890AbCdEf1234567890AbCdEf12345678", "admin": [{ "id": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" }], "custodian": [], "emergency": [], "governance": [{ "id": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" }], "supplyManagement": [] } } ``` Review the `accessControl` field to see current role assignments. Each role contains an array of accounts with that role. ### Grant a role [#grant-a-role] Grant one or more roles to a wallet address on the asset: ```bash curl -X POST "https://your-platform.example.com/api/token/0x9459D52E60edBD3178f00F9055f6C117a21b4220/grant-role" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "account": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "roles": ["supplyManagement"], "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` **Response:** ```json { "accounts": ["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"] } ``` #### Grant multiple roles [#grant-multiple-roles] Assign multiple roles to one wallet in a single transaction: ```bash curl -X POST "https://your-platform.example.com/api/token/0x9459D52E60edBD3178f00F9055f6C117a21b4220/grant-role" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "account": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "roles": ["supplyManagement", "custodian"], "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` #### Batch grant to multiple wallets [#batch-grant-to-multiple-wallets] Grant the same role to multiple wallets efficiently: ```bash curl -X POST "https://your-platform.example.com/api/token/0x9459D52E60edBD3178f00F9055f6C117a21b4220/grant-role" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "accounts": ["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890"], "role": "supplyManagement", "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` You cannot grant multiple roles to multiple addresses in a single transaction. Use separate requests for each address or each role combination. ### Revoke a role [#revoke-a-role] Remove one or more roles from a wallet address: ```bash curl -X DELETE "https://your-platform.example.com/api/token/0x9459D52E60edBD3178f00F9055f6C117a21b4220/revoke-role" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "account": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "roles": ["supplyManagement"], "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` **Response:** ```json { "accounts": ["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"] } ``` #### Revoke multiple roles [#revoke-multiple-roles] Remove multiple roles from one wallet in a single transaction: ```bash curl -X DELETE "https://your-platform.example.com/api/token/0x9459D52E60edBD3178f00F9055f6C117a21b4220/revoke-role" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "account": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "roles": ["supplyManagement", "custodian"], "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` #### Batch revoke from multiple wallets [#batch-revoke-from-multiple-wallets] Revoke the same role from multiple wallets efficiently: ```bash curl -X DELETE "https://your-platform.example.com/api/token/0x9459D52E60edBD3178f00F9055f6C117a21b4220/revoke-role" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "accounts": ["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890"], "role": "supplyManagement", "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` ### Verify changes [#verify-changes] Confirm role changes by fetching updated asset details: ```bash curl -X GET "https://your-platform.example.com/api/token/0x9459D52E60edBD3178f00F9055f6C117a21b4220" \ -H "X-Api-Key: YOUR_API_KEY" ``` The response shows all current role assignments in the `accessControl` field. Verify the changes were applied correctly. ## Request parameters [#request-parameters] | Parameter | Type | Required | Description | | -------------------- | --------- | -------- | ---------------------------------------------------------------- | | `account` | string | Yes\* | Single wallet address for role assignment | | `accounts` | string\[] | Yes\* | Multiple wallet addresses (alternative to `account`) | | `role` | string | Yes\* | Single role to grant/revoke (use with `accounts`) | | `roles` | string\[] | Yes\* | Multiple roles to grant/revoke (use with `account`) | | `walletVerification` | object | Yes | Your wallet verification to authorize the blockchain transaction | \*Use either `account` + `roles` OR `accounts` + `role` ### Wallet verification object [#wallet-verification-object] | Field | Type | Description | | ------------------------ | ------ | ---------------------------------------------- | | `secretVerificationCode` | string | 6-digit pincode or TOTP code | | `verificationType` | string | "PINCODE" (default), "SECRET\_CODES", or "OTP" | ## Response fields [#response-fields] | Field | Type | Description | | ---------- | ----- | ----------------------------------- | | `accounts` | array | Wallet addresses that were modified | ## Best practices [#best-practices] ### Role assignment [#role-assignment] * **Principle of least privilege** — Grant only necessary roles * **Separation of duties** — Divide critical functions among multiple administrators * **Regular review** — Audit role assignments when responsibilities change * **Documentation** — Record role changes and business rationale ### Security [#security] * Limit the **admin** role to trusted individuals * Separate operational roles from governance roles * Maintain backup administrators for critical roles * Use different wallets for different administrative functions ### Operations [#operations] * Assign **Supply Management** to asset operators who mint/burn * Give **Emergency** role to operations team for incident response * Separate **Custodian** role for transfer management and compliance * Reserve **Governance** for strategic configuration decisions ## Troubleshooting [#troubleshooting] | Issue | Solution | | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | Permission denied | Verify you have the `admin` role on this specific asset. | | Asset not found | Check the asset contract address is correct. Ensure the asset is deployed on this platform. | | Role not found | Check the role name matches exactly (case-sensitive). Valid roles: admin, custodian, emergency, governance, supplyManagement. | | Transaction fails | Ensure wallet has sufficient gas. Verify PIN/OTP is correct. Check network connectivity. | | Changes not visible | Wait for blockchain confirmation. Refresh asset details. Check transaction was successful. | | Cannot revoke own role | Have another user with the `admin` role to revoke your role if needed. | ## Related guides [#related-guides] * [Change Admin Roles](/docs/developer-guides/platform-setup/change-admin-roles) — Manage platform-level system roles # Forced transfer Source: https://docs.settlemint.com/docs/developer-guides/asset-servicing/forced-transfer Administratively move assets between wallets for compliance or recovery via API. A forced transfer moves assets between accounts without the holder's consent or signature. This replicates the authority that traditional custodians and transfer agents have to move securities under specific legal circumstances. For the web interface approach, see the [user guide](/docs/user-guides/asset-servicing/forced-transfer). ## Prerequisites [#prerequisites] * Platform URL (e.g., `https://your-platform.example.com`) * API key from a user with **Custodian** role on this asset (see [Getting Started](/docs/developer-guides/api-integration/getting-started) for API key setup) * Wallet verification method enabled on your account (e.g., pincode or 2FA) * Source address must hold sufficient balance Forced transfers bypass normal wallet authorization. Only use for legitimate compliance, legal, or recovery situations. All forced transfers are permanently recorded on-chain. ## Executing a forced transfer [#executing-a-forced-transfer] ### Prepare API request [#prepare-api-request] Construct the API request with source, destination, and amount: ```bash curl -X POST "https://your-platform.example.com/api/token/0x9459D52E60edBD3178f00F9055f6C117a21b4220/transfer/forced" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "from": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890", "recipients": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "amounts": ["100000000000000000000", 18], "walletVerification": { "secretVerificationCode": "123456", "verificationType": "PINCODE" } }' ``` The asset contract address is part of the URL path: `/api/token/{tokenAddress} /transfer/forced` Asset amounts depend on the asset's decimal configuration. See [Asset Decimals](/docs/developer-guides/api-integration/asset-decimals) for formatting guidance. ### Execute forced transfer [#execute-forced-transfer] When you execute the request, the platform: 1. **Validates your permissions** - Confirms you have Custodian role on this asset 2. **Verifies your wallet** - Uses your pincode/2FA to authorize the blockchain transaction 3. **Checks balances** - Ensures source address has sufficient balance 4. **Executes the transfer** - Moves assets from source to destination ### Handle response [#handle-response] A successful response returns the updated asset data (showing key fields): ```json { "id": "0x9459D52E60edBD3178f00F9055f6C117a21b4220", "type": "equity", "createdAt": "2025-01-15T10:30:00.000Z", "name": "ACME Holdings Common Stock", "symbol": "ACME", "decimals": 18, "basePrice": "[\"10000\",2]", "basePriceCurrencyCode": "USD", "totalSupply": "[\"1000000000000000000000\",18]", "pausable": { "paused": false } } ``` The response includes the full asset object with additional fields like `identity`, `accessControl`, `complianceModuleConfigs`, `userPermissions`, and `stats`. ### Document the transfer [#document-the-transfer] Maintain off-chain documentation including: * Legal authorization or compliance justification * Approval from appropriate authority * Date and time of authorization * Affected parties notification ## Batch forced transfers [#batch-forced-transfers] Move assets from multiple sources to multiple destinations in one transaction: ### Prepare batch request [#prepare-batch-request] Construct the request with arrays of sources, destinations, and amounts: ```bash curl -X POST "https://your-platform.example.com/api/token/0x9459D52E60edBD3178f00F9055f6C117a21b4220/transfer/forced" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "from": [ "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890", "0x1234567890AbCdEf1234567890AbCdEf12345678" ], "recipients": [ "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "0xabcdef1234567890abcdef1234567890abcdef12" ], "amounts": [ ["100000000000000000000", 18], ["50000000000000000000", 18] ], "walletVerification": { "secretVerificationCode": "123456", "verificationType": "PINCODE" } }' ``` The `from`, `recipients`, and `amounts` arrays must all have the same length. Each transfer is: `from[i]` → `recipients[i]` for `amounts[i]`. ### Execute batch transfer [#execute-batch-transfer] The platform validates all transfers before executing. If any transfer fails validation, the entire batch is rejected. **Batch limits:** * The forced-transfer API accepts up to 10,000 transfer items per request. * Each item must include a source address, recipient address, and amount. * For larger exception batches, split the work into multiple requests and keep the approval evidence for each request. ## Request parameters [#request-parameters] | Parameter | Type | Required | Description | | ------------------------------------------- | --------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- | | `from` | string or array | Yes | Source wallet address(es) (assets seized from) | | `recipients` | string or array | Yes | Destination wallet address(es) (assets sent to) | | `amounts` | string or array | Yes | Amount(s) as dnum tuple `[scaled_value, decimals]`. See [Asset Decimals](/docs/developer-guides/api-integration/asset-decimals) | | `walletVerification` | object | Yes | Your wallet verification | | `walletVerification.secretVerificationCode` | string | Yes | Your 6-digit PINCODE or TOTP code | | `walletVerification.verificationType` | string | Yes | `"PINCODE"`, `"OTP"`, or `"SECRET_CODES"` | ## Troubleshooting [#troubleshooting] | Issue | Solution | | --------------------------------- | ---------------------------------------------------------- | | `401 Unauthorized` | API key is invalid, expired, or disabled | | `403 USER_NOT_AUTHORIZED` | Ensure your account has `custodian` role on this asset | | `Asset is paused` | Unpause the asset first | | `Source has insufficient balance` | Check balance of `from` address | | `Recipient not compliant` | Recipient must meet compliance requirements | | `Array length mismatch` | from, recipients, and amounts arrays must have same length | ## Related guides [#related-guides] * [Pause/Unpause Assets](/docs/developer-guides/asset-servicing/pause-unpause-asset) - Enable/disable transfers * [Mint Assets](/docs/developer-guides/asset-servicing/mint-assets) - Issue assets to investors # Mint assets Source: https://docs.settlemint.com/docs/developer-guides/asset-servicing/mint-assets Issue new units to investors via API. Minting creates new units of an asset and assigns them to specified wallet addresses. This guide covers how to mint assets programmatically using the API. For the web interface approach, see the [user guide](/docs/user-guides/asset-servicing/mint-assets). ## Prerequisites [#prerequisites] * Platform URL (e.g., `https://your-platform.example.com`) * API key from a user with **Supply Management** role on this asset (see [Getting Started](/docs/developer-guides/api-integration/getting-started) for API key setup) * Wallet verification method enabled on your account (e.g., pincode or 2FA) * Asset must be unpaused * Recipients must have registered identities with required compliance verifications ## Minting assets [#minting-assets] ### Prepare API request [#prepare-api-request] Construct the API request with recipient address and amount: ```bash curl -X POST "https://your-platform.example.com/api/token/0x9459D52E60edBD3178f00F9055f6C117a21b4220/mint" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "recipients": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "amounts": ["1000000000000000000000", 18], "walletVerification": { "secretVerificationCode": "123456", "verificationType": "PINCODE" } }' ``` The asset contract address is part of the URL path: `/api/token/{tokenAddress}/mint` ### Execute mint transaction [#execute-mint-transaction] When you execute the request, the platform: 1. **Validates your permissions** - Confirms you have Supply Management role on this asset 2. **Verifies your wallet** - Uses your pincode/2FA to authorize the blockchain transaction 3. **Checks recipient compliance** - Ensures recipient has required verifications 4. **Executes the mint** - Creates new units and assigns to recipient Asset amounts depend on the asset's decimal configuration. See [Asset Decimals](/docs/developer-guides/api-integration/asset-decimals) for formatting guidance. ### Handle response [#handle-response] A successful response returns the updated asset data (showing key fields): ```json { "id": "0x9459D52E60edBD3178f00F9055f6C117a21b4220", "type": "equity", "createdAt": "2025-01-15T10:30:00.000Z", "name": "ACME Holdings Common Stock", "symbol": "ACME", "decimals": 18, "basePrice": "[\"10000\",2]", "basePriceCurrencyCode": "USD", "totalSupply": "[\"1000000000000000000000\",18]", "pausable": { "paused": false }, "capped": { "cap": "[\"1000000000000000000000000\",18]" } } ``` The response includes the full asset object with the updated `totalSupply` reflecting the newly minted units, along with additional fields like `identity`, `accessControl`, `complianceModuleConfigs`, `userPermissions`, and `stats`. ## Batch minting [#batch-minting] Issue units to multiple wallets in a single transaction: ### Prepare batch request [#prepare-batch-request] Construct the request with arrays of recipients and amounts: ```bash curl -X POST "https://your-platform.example.com/api/token/0x9459D52E60edBD3178f00F9055f6C117a21b4220/mint" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "recipients": [ "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890", "0x1234567890AbCdEf1234567890AbCdEf12345678" ], "amounts": [ ["500000000000000000000", 18], ["300000000000000000000", 18], ["200000000000000000000", 18] ], "walletVerification": { "secretVerificationCode": "123456", "verificationType": "PINCODE" } }' ``` The `recipients` and `amounts` arrays must have the same length. Each recipient gets the corresponding amount at the same index. ### Execute batch mint [#execute-batch-mint] The platform validates all recipients before executing. If any recipient fails compliance checks, the entire batch is rejected. **Batch limits:** * Maximum 100 recipients per API call * For larger batches, split into multiple calls ## Request parameters [#request-parameters] | Parameter | Type | Required | Description | | ------------------------------------------- | --------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- | | `recipients` | string or array | Yes | Recipient wallet address(es) (0x...) | | `amounts` | string or array | Yes | Amount(s) as dnum tuple `[scaled_value, decimals]`. See [Asset Decimals](/docs/developer-guides/api-integration/asset-decimals) | | `walletVerification` | object | Yes | Your wallet verification | | `walletVerification.secretVerificationCode` | string | Yes | Your 6-digit PINCODE or TOTP code | | `walletVerification.verificationType` | string | Yes | `"PINCODE"`, `"OTP"`, or `"SECRET_CODES"` | ## Troubleshooting [#troubleshooting] | Issue | Solution | | --------------------------------- | --------------------------------------------------------------------------------------------------------- | | `401 Unauthorized` | API key is invalid, expired, or disabled | | `403 USER_NOT_AUTHORIZED` | Ensure your account has `supplyManagement` role on this asset | | `Asset is paused` | Unpause the asset first (see [Pause/Unpause](/docs/developer-guides/asset-servicing/pause-unpause-asset)) | | `Recipient identity not found` | Recipient must create an account and register their identity | | `Exceeds supply cap` | Asset has reached maximum supply | | `Insufficient collateral` | Deposit more collateral before minting | | `Recipient not compliant` | Recipient missing required claims (KYC, accredited investor, etc) | | `Recipients and amounts mismatch` | Array lengths must be equal | | `Amount must be positive` | All amounts must be greater than 0 | ## Related guides [#related-guides] * [Pause/Unpause Assets](/docs/developer-guides/asset-servicing/pause-unpause-asset) - Enable/disable transfers * [Forced Transfer](/docs/developer-guides/asset-servicing/forced-transfer) - Transfer assets administratively # Pause and unpause assets Source: https://docs.settlemint.com/docs/developer-guides/asset-servicing/pause-unpause-asset Enable or disable all transfers for an asset via API. Pausing an asset temporarily disables all transfers, minting, and burning operations. This is used for emergency stops, maintenance, or compliance holds. For the web interface approach, see the [user guide](/docs/user-guides/asset-servicing/pause-unpause-asset). ## Prerequisites [#prerequisites] * Platform URL (e.g., `https://your-platform.example.com`) * API key from a user with **Emergency** role on this asset (see [Getting Started](/docs/developer-guides/api-integration/getting-started) for API key setup) * Wallet verification method enabled on your account (e.g., pincode or 2FA) Assets are automatically paused upon creation. You must unpause them before any transfers can occur. ## Pausing an asset [#pausing-an-asset] ### Prepare pause request [#prepare-pause-request] Construct the API request to pause the asset: ```bash curl -X PATCH "https://your-platform.example.com/api/v2/tokens/0x9459D52E60edBD3178f00F9055f6C117a21b4220/pause-state" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "walletVerification": { "secretVerificationCode": "123456", "verificationType": "PINCODE" } }' ``` The asset contract address is part of the URL path: `/api/v2/tokens/{tokenAddress}/pause-state`. ### Execute pause [#execute-pause] When you execute the request, the platform: 1. **Validates your permissions** - Confirms you have Emergency role on this asset 2. **Verifies your wallet** - Uses your pincode/2FA to authorize the blockchain transaction 3. **Pauses the asset** - Sets the pause flag on-chain, blocking all transfers ### Handle response [#handle-response] A synchronous response wraps the updated asset data in `data` and includes transaction metadata: ```json { "data": { "id": "0x9459D52E60edBD3178f00F9055f6C117a21b4220", "name": "ACME Holdings Common Stock", "symbol": "ACME", "decimals": 0, "totalSupply": "1000", "type": "equity", "pausable": { "paused": true } }, "meta": { "txHashes": ["0x..."] }, "links": { "self": "/v2/tokens/0x9459D52E60edBD3178f00F9055f6C117a21b4220/pause-state" } } ``` The `data.pausable.paused` field confirms the asset is now paused. The response includes the full asset object with additional fields like `accessControl`, `identity`, and `userPermissions`. If you request asynchronous processing, the API returns a `transactionId`, `status`, and `statusUrl` instead. ## Unpausing an asset [#unpausing-an-asset] ### Prepare unpause request [#prepare-unpause-request] Construct the API request to unpause the asset: ```bash curl -X DELETE "https://your-platform.example.com/api/v2/tokens/0x9459D52E60edBD3178f00F9055f6C117a21b4220/pause-state" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "walletVerification": { "secretVerificationCode": "123456", "verificationType": "PINCODE" } }' ``` ### Execute unpause [#execute-unpause] When you execute the request, the platform: 1. **Validates your permissions** - Confirms you have Emergency role on this asset 2. **Verifies your wallet** - Uses your pincode/2FA to authorize the blockchain transaction 3. **Unpauses the asset** - Clears the pause flag, enabling all transfers ### Handle response [#handle-response-1] A synchronous response wraps the updated asset data in `data` and includes transaction metadata: ```json { "data": { "id": "0x9459D52E60edBD3178f00F9055f6C117a21b4220", "name": "ACME Holdings Common Stock", "symbol": "ACME", "decimals": 0, "totalSupply": "1000", "type": "equity", "pausable": { "paused": false } }, "meta": { "txHashes": ["0x..."] }, "links": { "self": "/v2/tokens/0x9459D52E60edBD3178f00F9055f6C117a21b4220/pause-state" } } ``` The `data.pausable.paused` field confirms the asset is now active. The response includes the full asset object with additional fields like `accessControl`, `identity`, and `userPermissions`. If you request asynchronous processing, the API returns a `transactionId`, `status`, and `statusUrl` instead. ## Request parameters [#request-parameters] | Parameter | Type | Required | Description | | ------------------------------------------- | ------ | -------- | ----------------------------------------- | | `walletVerification` | object | Yes | Your wallet verification | | `walletVerification.secretVerificationCode` | string | Yes | Your 6-digit PINCODE or TOTP code | | `walletVerification.verificationType` | string | Yes | `"PINCODE"`, `"OTP"`, or `"SECRET_CODES"` | ## What pausing affects [#what-pausing-affects] When an asset is paused, the following operations are **blocked**: * **User transfers** - Normal wallet-to-wallet transfers * **Minting** - Creating new units * **Burning** - Destroying existing units * **Forced transfers** - Administrative transfers These operations **continue to work** while paused: * **Viewing balances** - Read-only operations * **Checking compliance** - Identity and claim verification * **Role management** - Adding/removing administrators * **Pause/unpause** - Can toggle pause state Pausing affects ALL asset holders immediately. Use sparingly and communicate with stakeholders. ## Check pause status [#check-pause-status] Query the asset to see current pause state: ```bash curl -X GET "https://your-platform.example.com/api/v2/tokens/0x9459D52E60edBD3178f00F9055f6C117a21b4220" \ -H "X-Api-Key: YOUR_API_KEY" ``` The response wraps pause status in the `data.pausable` field: ```json { "data": { "id": "0x9459D52E60edBD3178f00F9055f6C117a21b4220", "name": "ACME Holdings Common Stock", "symbol": "ACME", "decimals": 0, "totalSupply": "1000", "type": "equity", "pausable": { "paused": false } }, "links": { "self": "/v2/tokens/0x9459D52E60edBD3178f00F9055f6C117a21b4220" } } ``` Use `data.pausable.paused` to check whether the asset is paused. The `data` object includes the full asset object with additional fields. ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------------ | ------------------------------------------------------ | | `401 Unauthorized` | API key is invalid, expired, or disabled | | `403 USER_NOT_AUTHORIZED` | Ensure your account has `emergency` role on this asset | | `Asset already paused` | Check status first, asset may already be paused | | `Asset already unpaused` | Check status first, asset may already be unpaused | | `Cannot transfer while paused` | Unpause the asset before attempting transfers | ## Related guides [#related-guides] * [Forced Transfer](/docs/developer-guides/asset-servicing/forced-transfer) - Administrative transfers * [Mint Assets](/docs/developer-guides/asset-servicing/mint-assets) - Issue assets to investors # AI agent integration Source: https://docs.settlemint.com/docs/developer-guides/cli/ai-agents Use the DALP CLI with AI coding agents like Claude Code, OpenCode, and Codex for automated platform operations and development workflows. The DALP CLI is designed to work cleanly with AI coding agents. Its structured JSON output, Zod-validated inputs, and comprehensive command coverage make it an ideal tool for LLM-driven workflows in development environments. ## Why use the CLI with AI agents? [#why-use-the-cli-with-ai-agents] AI coding agents excel at orchestrating multi-step platform operations: * **Token lifecycle automation** – Create tokens, configure compliance, mint supply, and manage roles in a single conversation * **Bulk operations** – Onboard hundreds of users, process batch transfers, or update compliance across all tokens * **Exploratory analysis** – Query system state, cross-reference holders, and generate reports * **Development workflows** – Deploy to staging, run smoke tests, and validate configuration *** ## Supported agents [#supported-agents] The DALP CLI works with any agent that can execute shell commands: | Agent | How it uses the CLI | | ------------------------------------------------------------- | ------------------------------------------- | | [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | Bash tool executes `dalp` commands directly | | [Codex](https://openai.com/index/codex/) | Shell execution in sandboxed environments | | [OpenCode](https://opencode.ai) | Terminal tool runs CLI commands | *** ## Setup for AI agents [#setup-for-ai-agents] ### 1. Install and authenticate [#1-install-and-authenticate] ```bash npm install -g @settlemint/dalp-cli dalp login --url https://your-platform.example.com ``` The agent inherits your authenticated session. Credentials persist across agent sessions. ### 2. Set JSON output [#2-set-json-output] Configure JSON as the default format so agents can parse responses: ```bash dalp config set format json ``` ### 3. Register MCP and skills [#3-register-mcp-and-skills] Register the CLI as an MCP server and install skill files in one step: ```bash # Register as MCP server (auto-detects Claude Code, Cursor, etc.) dalp mcp add # Install skill files for agent discovery dalp skills add # Enable shell completions dalp completions ``` Optionally, create a `.dalprc.json` in your project root so the CLI picks up project-specific settings: ```json { "apiUrl": "https://your-platform.example.com", "defaultOrg": "your-org", "format": "json" } ``` *** ## MCP server integration [#mcp-server-integration] The DALP CLI has a built-in MCP (Model Context Protocol) server. When started with `--mcp`, it exposes every command as a typed tool that AI agents can call directly – no shell parsing required. ### Automatic registration [#automatic-registration] The fastest way to register the MCP server with your agent: ```bash dalp mcp add ``` This writes the correct configuration for detected agents (Claude Code, Cursor, etc.). ### Manual configuration [#manual-configuration] If you prefer manual setup, add the MCP server to your agent's configuration: **Claude Code** (`.claude/mcp.json`): ```json { "mcpServers": { "dalp": { "command": "dalp", "args": ["--mcp"], "env": { "DALP_URL": "https://your-platform.example.com" } } } } ``` **OpenCode**: ```json { "mcpServers": { "dalp": { "command": "dalp", "args": ["--mcp"] } } } ``` When running as an MCP server, the CLI exposes each command as a typed tool with Zod-validated parameters. The agent sees tool descriptions, parameter types, and examples from the command metadata. *** ## Skills integration [#skills-integration] The CLI auto-generates skill files from command metadata (descriptions, examples, argument schemas). Install them for your agent: ```bash dalp skills add ``` This creates Markdown skill files in `~/.config/agents/skills/` that agents like Claude Code discover automatically. Each skill documents the command's arguments, options, output schema, and usage examples. You can also view the full skill manifest directly: ```bash # Output Markdown skill docs for all commands dalp --llms # Output JSON Schema for all commands dalp --schema ``` *** ## Patterns for agents [#patterns-for-agents] ### Idempotent operations [#idempotent-operations] When agents retry operations, use list-before-create patterns: ```bash # Check if token exists before creating existing=$(dalp tokens list --query "My Token" --format json | jq 'length') if [ "$existing" -eq 0 ]; then dalp tokens create --type equity --name "My Token" --symbol MTK --decimals 18 --countryCode 840 fi ``` ### Error recovery [#error-recovery] Agents should check exit codes and parse error messages: ```bash # The CLI returns structured errors in JSON format result=$(dalp tokens mint --address 0x... --to 0x... --amount 1000 --format json 2>&1) || { echo "$result" | jq -r '.message' # Agent can decide: retry, adjust amount, or escalate } ``` ### Progressive discovery [#progressive-discovery] Agents can explore the platform systematically: ```bash # 1. Discover system capabilities dalp system factories available --format json # 2. List existing tokens dalp tokens list --format json # 3. Inspect a specific token dalp tokens read
--format json # 4. Check holder balances dalp users assets --wallet --format json ``` *** ## Security considerations [#security-considerations] * **Credential scope**: The CLI authenticates as your user with your permissions. Agents inherit your access level. * **Organization isolation**: Operations are scoped to the active organization. Use `dalp auth org-switch` to change context. * **Audit trail**: All CLI operations are logged with `User-Agent: DALP CLI v{version}` for traceability. * **Read-before-write**: Configure agents to query state before making mutations to prevent unintended changes. *** ## Next steps [#next-steps] * **[Command reference](/docs/developer-guides/cli/command-reference)** – Full list of available commands * **[Scripting](/docs/developer-guides/cli/scripting)** – Shell scripting patterns and CI/CD integration * **[API integration](/docs/developer-guides/api-integration/getting-started)** – Direct API access for custom integrations # Command reference Source: https://docs.settlemint.com/docs/developer-guides/cli/command-reference Complete reference for all DALP CLI commands covering authentication, tokens, users, identity, KYC, system management, and more. The DALP CLI organizes commands into domain groups. Every command that modifies state requires authentication – see [Getting started](/docs/developer-guides/cli/getting-started) to log in. ## Authentication and config [#authentication-and-config] Commands that do not require authentication: | Command | Description | | ------------------------------- | ------------------------------------------- | | `dalp login [--url URL]` | Authenticate via device authorization flow | | `dalp logout` | Revoke API key and clear stored credentials | | `dalp config get [key]` | View configuration | | `dalp config set ` | Update configuration | Commands that require authentication: | Command | Description | | ------------- | ---------------------------------------------- | | `dalp whoami` | Display current user, wallet, and organization | *** ## Auth management [#auth-management] Manage sessions, API keys, and organizations: ```bash # Sessions dalp auth session-list dalp auth session-revoke dalp auth session-revoke-all # API keys dalp auth api-key-list dalp auth api-key-create dalp auth api-key-delete # Organizations dalp auth org-list dalp auth org-switch dalp auth org-create dalp auth org-invite [role] dalp auth org-remove-member ``` *** ## Settings and theme management [#settings-and-theme-management] Use settings commands to inspect platform configuration and manage the active Asset Console theme: * Settings upsert commands require `setting: upsert`; settings delete commands require `setting: remove`. * Theme preview and logo-upload commands require `setting: upsert`. * Selecting the global theme requires `setting: set-global-theme`. ```bash # Platform settings dalp settings list dalp settings read BASE_CURRENCY dalp settings upsert --key BASE_CURRENCY --value EUR dalp settings delete SYSTEM_ADDONS_SKIPPED dalp settings public-config # Organization theme dalp settings theme-get # Preview a partial theme diff against the current theme version. dalp settings theme-preview --baseVersion 3 \ --diff '{"cssVars":{"light":{"sm-accent":"#0052cc"}}}' # Persist the theme change after reviewing the preview. dalp settings theme-update --baseVersion 3 \ --diff '{"cssVars":{"light":{"sm-accent":"#0052cc"}}}' # Theme assets dalp settings theme-upload-logo --mode light --fileName logo.png \ --contentType image/png --fileSize 204800 dalp settings theme-proxy-upload-logo --mode favicon --fileName favicon.ico \ --contentType image/x-icon --fileData # Global theme selection dalp settings global-theme-get dalp settings global-theme-set --orgId ``` Theme asset uploads through `dalp settings theme-upload-logo` and `dalp settings theme-proxy-upload-logo` accept `image/jpeg`, `image/png`, `image/svg+xml`, `image/webp`, and `image/x-icon`. Both logo upload commands apply to the active organization, so switch to the target organization before uploading branding assets. The preview command accepts partial theme diffs under supported keys such as `cssVars.light`, `cssVars.dark`, `logo`, `images`, and `fonts`; use `theme-update` with the same diff to persist reviewed changes. The upload command returns storage-specific headers and a presigned PUT URL for the client upload; the proxy upload command sends base64 file data through the API and enforces a 5 MiB file-size limit. *** ## Account state [#account-state] Read indexed native-balance state for one account: ```bash dalp account native-balance read
``` Use this for operations checks that need the latest indexed gas balance for an operator wallet, smart account, system contract, or asset contract. For list, filter, and history reads, use the [account native balance API](/docs/developer-guides/api-integration/account-native-balances). *** ## Token management [#token-management] ### Read operations [#read-operations] ```bash dalp tokens list dalp tokens read
dalp tokens list --query ``` ### Create and configure [#create-and-configure] ```bash # Create a token (pass type-specific fields as JSON) dalp tokens create --type equity --name "Acme Shares" --symbol ACME \ --decimals 18 --countryCode 840 --json '{"maxSupply": "1000000"}' # Supply management dalp tokens mint --address 0x... --to 0x... --amount 1000 dalp tokens burn --address 0x... --from 0x... --amount 500 dalp tokens set-cap --address 0x... --cap 5000000 # Transfers dalp tokens transfer --address 0x... --to 0x... --amount 100 dalp tokens forced-transfer --address 0x... --from 0x... --to 0x... --amount 50 dalp tokens approve --address 0x... --spender 0x... --amount 1000 # State control dalp tokens pause 0x... dalp tokens unpause 0x... # Freeze operations dalp tokens freeze-address --address 0x... --target 0x... dalp tokens freeze-partial --address 0x... --target 0x... --amount 500 dalp tokens unfreeze-partial --address 0x... --target 0x... --amount 500 # To clear an address-level freeze, use the token address-freezes API with freeze=false. # Recovery dalp tokens recover-tokens --address 0x... --lostWallet 0x... dalp tokens forced-recover --address 0x... --lostWallet 0x... --newWallet 0x... dalp tokens recover-erc20 --address 0x... --tokenAddress 0x... --to 0x... --amount 1000 # Maturity and redemption dalp tokens mature --address 0x... dalp tokens redeem --address 0x... --amount 1000 # Access control dalp tokens grant-role --address 0x... --role supplyManagement --account 0x... dalp tokens revoke-role --address 0x... --role supplyManagement --account 0x... # Compliance modules dalp tokens add-compliance-module --address 0x... --moduleAddress 0x... dalp tokens remove-compliance-module --address 0x... --moduleAddress 0x... dalp tokens set-compliance-module-params --address 0x... --moduleAddress 0x... # Identity claims dalp tokens claim-issue --address 0x... --topic 1 --claim 0x... dalp tokens claim-revoke --address 0x... --claimId 0x... # Metadata dalp tokens set-metadata --address 0x... --key prospectus --value "https://..." dalp tokens remove-metadata --address 0x... --key prospectus # Fee configuration dalp tokens set-aum-fee-bps --address 0x... --featureAddress 0x... --feeBps 50 dalp tokens set-aum-fee-recipient --address 0x... --featureAddress 0x... --recipient 0x... # Collateral dalp tokens update-collateral --address 0x... --amount 150 --expiryTimestamp 1735689600 ``` ### Statistics [#statistics] ```bash dalp tokens stats-bond-status --address 0x... dalp tokens stats-collateral-ratio --address 0x... dalp tokens stats-total-supply --address 0x... ``` *** ## User management [#user-management] ```bash # Read operations dalp users list dalp users me dalp users list --query dalp users read-by-user-id dalp users read-by-wallet dalp users read-by-national-id # Create dalp users create --email user@example.com --name "Jane Doe" dalp users create-wallet # Analytics dalp users assets --wallet 0x... dalp users stats --timeRange 30d dalp users stats-user-count dalp users stats-growth # Admin operations dalp users admin-list dalp users admin-security dalp users admin-revoke-session --sessionId dalp users admin-revoke-all-sessions dalp users admin-reset-mfa dalp users admin-trigger-password-reset ``` *** ## Identity management [#identity-management] ```bash # Lifecycle dalp identities list dalp identities me dalp identities create --wallet 0x... dalp identities register --wallet 0x... --country DE dalp identities register-pending --wallet 0x... # Lookups dalp identities read-by-wallet dalp identities read-by-id dalp identities search --wallet 0x... --address 0x... dalp identities registration-status # Management dalp identities delete dalp identities update-country --wallet 0x... --countryCode US # Claims dalp identities claim-history dalp identities claim-issue --targetIdentityAddress 0x... --topic 1 --claimData 0x... dalp identities claim-revoke --targetIdentityAddress 0x... --claimTopic 1 ``` *** ## KYC management [#kyc-management] ```bash # Profile dalp kyc profile # Versions dalp kyc versions dalp kyc version-create dalp kyc version-read --versionId dalp kyc version-update --versionId dalp kyc version-submit --versionId dalp kyc version-approve --versionId dalp kyc version-reject --versionId --rejectionReason "..." dalp kyc version-request-update --versionId --reason "..." # Documents dalp kyc documents --versionId dalp kyc document-upload-url --versionId --documentType passport \ --fileName scan.pdf --fileSize 204800 --mimeType application/pdf dalp kyc document-confirm-upload --versionId --objectKey "..." \ --documentType passport --fileName scan.pdf --fileSize 204800 --mimeType application/pdf dalp kyc document-download-url --versionId --documentId dalp kyc document-delete --versionId --documentId # Action requests dalp kyc action-request-fulfill ``` KYC document uploads support files up to 10 MiB. When the upload URL command returns upload headers, send those headers with the file upload request, for example by passing `headers: upload.data.headers` from the response envelope to the HTTP client. *** ## System management [#system-management] ### Core [#core] ```bash dalp system list dalp system create dalp system read dalp system resume dalp system directory dalp system entities ``` ### Access control [#access-control] ```bash dalp system access-manager roles-list dalp system access-manager roles-read
dalp system access-manager grant-role --account 0x... --role admin dalp system access-manager revoke-role --account 0x... --role admin ``` ### Token factories [#token-factories] ```bash dalp system factories list dalp system factories available --type equity --name "My Token" --symbol MTK --decimals 18 dalp system factories create --factories '[...]' dalp system factories read
dalp system factories predict-address --type equity --name "My Token" --symbol MTK --decimals 18 ``` ### Data feeds [#data-feeds] ```bash # Discovery dalp system feeds capabilities dalp system feeds list dalp system feeds resolve dalp system feeds read
# CRUD dalp system feeds issuer-signed-create --subject "ETH/USD" --description "Ether price" \ --decimals 8 --historyMode rolling --historySize 100 dalp system feeds register-external --subject "ETH/USD" --feedAddress 0x... dalp system feeds replace --subject "ETH/USD" --newFeedAddress 0x... dalp system feeds remove # Data dalp system feeds latest
dalp system feeds round --address 0x... --roundId 5 dalp system feeds staleness --address 0x... --maxAgeSeconds 3600 dalp system feeds config
# Updates dalp system feeds nonce --address 0x... --issuerIdentity 0x... dalp system feeds submit --address 0x... --value 185000000000 --observedAt "2024-06-15T12:00:00Z" ``` ### Compliance [#compliance] ```bash dalp system compliance-modules list dalp system compliance-modules create --complianceModules '[...]' dalp system compliance-modules remove-global --module 0x... ``` ### Trusted issuers [#trusted-issuers] ```bash dalp system trusted-issuers list dalp system trusted-issuers read
dalp system trusted-issuers create --issuerAddress 0x... --claimTopicIds '[1,2]' dalp system trusted-issuers update --address 0x... --claimTopicIds '[1,2,3]' dalp system trusted-issuers upsert --address 0x... --claimTopicIds '[1,2]' dalp system trusted-issuers delete
dalp system trusted-issuers topics
``` ### System statistics [#system-statistics] ```bash dalp system stats assets dalp system stats asset-lifecycle-by-range --interval day --from 2024-01-01 --to 2024-12-31 dalp system stats asset-lifecycle-by-preset ``` *** ## Other commands [#other-commands] | Command | Description | | ------------------------------- | ------------------------------------ | | `dalp search-results ` | Global platform search | | `dalp actions` | Token action management | | `dalp admin organizations list` | List all organizations (super admin) | | `dalp asset-type-templates` | Asset type templates | | `dalp compliance-templates` | Compliance module templates | | `dalp contacts` | Contact management | | `dalp exchange-rates` | Fiat exchange rate data | | `dalp external-tokens` | External token integration | | `dalp fixed-yield-schedules` | Fixed yield operations | | `dalp identity-recoveries` | Identity recovery workflows | | `dalp monitoring` | System health monitoring | | `dalp search-results ` | Global platform search | | `dalp settings` | User settings management | | `dalp tokens documents` | Token-level document management | | `dalp token-sales` | Token sale lifecycle | | `dalp blockchain-transactions` | Transaction tracking and status | | `dalp xvp-settlements` | XvP settlement operations | *** ## Global options [#global-options] All commands accept: | Option | Description | | ------------------- | ---------------------------------------------------- | | `--format ` | Output format: `toon`, `json`, `yaml`, `jsonl`, `md` | | `--json` | Shorthand for `--format json` | | `--help` | Show help for the command | Special flags for agent integration: | Flag | Description | | ---------- | -------------------------------- | | `--mcp` | Start CLI as an MCP stdio server | | `--llms` | Output Markdown skill manifest | | `--schema` | Output JSON Schema manifest | *** ## Exit codes [#exit-codes] | Code | Meaning | | ---- | ----------------------------------------------- | | `0` | Success | | `1` | General error (validation, network, permission) | # Getting started Source: https://docs.settlemint.com/docs/developer-guides/cli/getting-started Install the DALP CLI, authenticate with your platform instance, and run your first commands. The DALP CLI (`@settlemint/dalp-cli`) provides full programmatic access to the Digital Asset Lifecycle Platform from your terminal. It supports interactive use, scripting, CI/CD pipelines, and AI agent integration. ## Installation [#installation] Install the CLI globally: ```bash npm install -g @settlemint/dalp-cli ``` Or with Bun: ```bash bun add -g @settlemint/dalp-cli ``` After installation, the `dalp` command is available in your terminal. ## Authentication [#authentication] The CLI uses the **OAuth 2.0 Device Authorization Grant** (RFC 8628) for secure, browser-based login. No passwords are entered in the terminal. ### Login [#login] ```bash dalp login --url https://your-platform.example.com ``` Or set the URL via environment variable: ```bash export DALP_URL=https://your-platform.example.com dalp login ``` **What happens:** 1. The CLI requests a device code from the platform 2. Your browser opens to the verification page (or displays a URL to visit) 3. You enter the displayed user code and authorize the CLI 4. The CLI receives an access token and creates a long-lived API key 5. Credentials are stored securely (macOS Keychain or encrypted file) ### Verify your session [#verify-your-session] ```bash dalp whoami ``` Returns your user ID, email, wallet address, and active organization. ### Logout [#logout] ```bash dalp logout ``` Revokes the API key on the server and removes local credentials. *** ## Configuration [#configuration] The CLI resolves configuration from multiple sources (highest priority first): 1. **CLI flags** – `--url`, `--org`, `--format` 2. **Environment variables** – `DALP_URL`, `DALP_ORG` 3. **Project config** – `.dalprc.json` in the current directory 4. **Global config** – `~/.config/dalp/config.json` ### Manage config [#manage-config] ```bash # View all config dalp config get # View a single key dalp config get apiUrl # Set a value dalp config set format json dalp config set defaultOrg my-org-slug ``` ### Output formats [#output-formats] The CLI supports multiple output formats: | Format | Flag | Use case | | ------ | --------------- | -------------------------------- | | `toon` | `--format toon` | Human-readable tables (default) | | `json` | `--format json` | Machine-readable, piping to `jq` | | `yaml` | `--format yaml` | Configuration files | | `md` | `--format md` | Documentation, reports | Set the default globally: ```bash dalp config set format json ``` *** ## Credential storage [#credential-storage] Credentials are stored securely based on your operating system: | Platform | Storage | Details | | --------------- | --------------- | --------------------------------------------------------- | | macOS | System Keychain | Via `security` command, service `dalp-cli` | | Linux / Windows | File | `~/.config/dalp/credentials.json` with `0600` permissions | The CLI validates file permissions on load and refuses to read credentials with insecure permissions. *** ## Your first commands [#your-first-commands] After logging in, try these to explore the platform: ```bash # View your user profile dalp whoami # List tokens in the system dalp tokens list # List users dalp users list # View system information dalp system list # Search across the platform dalp search-results "bond" ``` *** ## Environment variables [#environment-variables] | Variable | Description | | ---------- | ---------------------------------------------------- | | `DALP_URL` | Platform URL (alternative to `--url` or config) | | `DALP_ORG` | Organization slug (alternative to `--org` or config) | *** ## Shell completions [#shell-completions] Enable tab completion for your shell: ```bash dalp completions ``` This generates and installs a completion script for your detected shell (bash, zsh, fish). *** ## Next steps [#next-steps] * **[Command reference](/docs/developer-guides/cli/command-reference)** – Full list of commands and options * **[Scripting and automation](/docs/developer-guides/cli/scripting)** – Use the CLI in scripts and CI/CD * **[AI agent integration](/docs/developer-guides/cli/ai-agents)** – Use the CLI with LLMs, MCP servers, and AI coding agents # Scripting and automation Source: https://docs.settlemint.com/docs/developer-guides/cli/scripting Use the DALP CLI in shell scripts, CI/CD pipelines, and automated workflows with JSON output and environment variables. The DALP CLI is designed for both interactive use and headless automation. Use JSON output, environment variables, and composable commands to build reliable scripts and CI/CD pipelines. ## JSON output for scripting [#json-output-for-scripting] Set the output format to JSON for machine-readable responses: ```bash # Per-command dalp tokens list --format json # Or set globally dalp config set format json ``` ### Pipe to jq [#pipe-to-jq] ```bash # Get all token addresses dalp tokens list --format json | jq -r '.[].address' # Get the first admin's wallet dalp users admin-list --format json | jq -r '.[0].wallet' # Count tokens by type dalp tokens list --format json | jq 'group_by(.type) | map({type: .[0].type, count: length})' ``` *** ## Environment-based authentication [#environment-based-authentication] For CI/CD and headless environments, use environment variables instead of interactive login: ```bash export DALP_URL=https://your-platform.example.com export DALP_ORG=your-org-slug ``` For non-interactive authentication, create an API key through the web UI or another authenticated CLI session, then use it directly: ```bash # Create an API key from an authenticated session dalp auth api-key-create "ci-pipeline" # Store the output key securely in your CI secrets ``` *** ## Scripting patterns [#scripting-patterns] ### Token issuance script [#token-issuance-script] ```bash #!/usr/bin/env bash set -euo pipefail # Create a bond token result=$(dalp tokens create \ --type bond \ --name "Corporate Bond 2025" \ --symbol CB25 \ --decimals 18 \ --countryCode 840 \ --json '{"maturityDate": "2025-12-31", "couponRate": "5.5"}' \ --format json) address=$(echo "$result" | jq -r '.address') echo "Token deployed at: $address" # Mint initial supply to the treasury dalp tokens mint \ --address "$address" \ --to 0xTREASURY... \ --amount 1000000 # Grant supply management to operations team dalp tokens grant-role \ --address "$address" \ --role supplyManagement \ --account 0xOPS_WALLET... ``` ### User onboarding script [#user-onboarding-script] ```bash #!/usr/bin/env bash set -euo pipefail EMAIL="$1" NAME="$2" # Create user user=$(dalp users create --email "$EMAIL" --name "$NAME" --format json) userId=$(echo "$user" | jq -r '.id') wallet=$(echo "$user" | jq -r '.wallet') echo "Created user $userId with wallet $wallet" # Register identity dalp identities register --wallet "$wallet" --country US # Create KYC version version=$(dalp kyc version-create "$userId" --format json) versionId=$(echo "$version" | jq -r '.id') echo "KYC version $versionId ready for document upload" ``` ### Batch operations [#batch-operations] ```bash #!/usr/bin/env bash set -euo pipefail TOKEN_ADDRESS="$1" RECIPIENTS_FILE="$2" # CSV: wallet,amount while IFS=, read -r wallet amount; do echo "Minting $amount to $wallet..." dalp tokens mint \ --address "$TOKEN_ADDRESS" \ --to "$wallet" \ --amount "$amount" done < "$RECIPIENTS_FILE" ``` *** ## CI/CD integration [#cicd-integration] ### GitHub Actions [#github-actions] ```yaml name: Token deployment on: workflow_dispatch: inputs: token_name: description: "Token name" required: true jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/setup-node@v4 with: node-version: "22" - name: Install DALP CLI run: npm install -g @settlemint/dalp-cli - name: Deploy token run: | dalp tokens create \ --type equity \ --name "${{ inputs.token_name }}" \ --symbol "TKN" \ --decimals 18 \ --format json env: DALP_URL: ${{ secrets.DALP_URL }} ``` *** ## Error handling in scripts [#error-handling-in-scripts] The CLI exits with a non-zero code on errors. Use standard shell error handling: ```bash #!/usr/bin/env bash set -euo pipefail if ! dalp whoami --format json > /dev/null 2>&1; then echo "Not authenticated. Run: dalp login" exit 1 fi # Proceed with authenticated operations dalp tokens list --format json ``` *** ## Project configuration [#project-configuration] Create a `.dalprc.json` in your project root for team-shared settings: ```json { "apiUrl": "https://staging.example.com", "defaultOrg": "engineering", "format": "json" } ``` This file can be committed to version control. Credentials are never stored in project config – they remain in the secure credential store. ## Related [#related] * [CLI getting started](/docs/developer-guides/cli/getting-started) * [CLI command reference](/docs/developer-guides/cli/command-reference) # Choose a KYC issuance path Source: https://docs.settlemint.com/docs/developer-guides/compliance/choose-kyc-issuance-path Decision guide for choosing manual DALP KYC claim issuance or provider-driven ClaimSource intake with Sumsub or Elliptic. DALP supports two current ways to produce compliance claims for KYC and AML workflows: * manual claim issuance by a configured trusted issuer * provider-driven intake through a [Sumsub](https://docs.sumsub.com/) or [Elliptic](https://developers.elliptic.co/) compliance-provider integration Choose one path per claim topic and policy unless your operating model explicitly needs an override process. Issuing duplicate claims for the same subject and topic can make reviews harder and should be deliberate. ## Use manual KYC claim issuance [#use-manual-kyc-claim-issuance] Use [Verify KYC via API](/docs/developer-guides/compliance/verify-kyc) when compliance staff or an internal back-office system reviews KYC evidence and then issues the claim. This path fits when: * the review happens inside your organisation * your claim issuer is a staff-controlled or service-controlled issuer * the KYC data is submitted and reviewed through DALP user/KYC flows * provider evidence exists, but the final claim decision is made by your own compliance process The issuer must be configured as a trusted issuer for the relevant claim topic. The API caller needs the claim-issuer role and wallet verification for the claim transaction. ## Use provider intake with Sumsub [#use-provider-intake-with-sumsub] Use [Onboard a compliance provider](/docs/developer-guides/compliance/onboarding-a-provider) and [Map compliance-provider subjects](/docs/developer-guides/compliance/compliance-provider-subjects) when Sumsub should produce identity verdicts for DALP identities. This path fits when: * Sumsub creates and reviews applicants * Sumsub applicant-review webhooks should issue or revoke the configured claim topic * Sumsub applicant-on-hold events should appear in the integration's monitoring history * the provider integration EOA should be the on-chain claim issuer Each Sumsub integration is bound to one claim topic. If one Sumsub programme covers multiple topics, create one integration per topic. ## Use provider intake with Elliptic [#use-provider-intake-with-elliptic] Use provider intake when Elliptic should monitor wallets and produce AML alert history for DALP identities. This path fits when: * the subject is a wallet address already linked to a DALP identity * Elliptic wallet alerts should be normalised to DALP severity scores * severe alerts should revoke the configured claim topic when they meet the integration's revocation threshold * the provider integration EOA should be the on-chain claim issuer Elliptic integrations use wallet monitoring topics. The dapp exposes threshold tiers so operators can decide which alert severity level causes claim revocation. ## Keep the paths distinct [#keep-the-paths-distinct] Manual KYC issuance and provider-driven intake can coexist in one DALP environment, but they should not compete silently for the same claim topic. Before enabling provider intake for a topic that is already issued manually, confirm: * which issuer should be authoritative for the topic * whether manual claims become overrides, fallbacks, or legacy evidence * how rejected provider verdicts or high-severity alerts should affect existing claims * which team owns provider dashboard configuration and webhook replay requests ## See also [#see-also] * [Verify KYC via API](/docs/developer-guides/compliance/verify-kyc) * [Onboard a compliance provider](/docs/developer-guides/compliance/onboarding-a-provider) * [Map compliance-provider subjects](/docs/developer-guides/compliance/compliance-provider-subjects) * [How compliance provider intake works](/docs/architecture/integrations/compliance-providers) # Collateral requirement Source: https://docs.settlemint.com/docs/developer-guides/compliance/collateral Configure collateral ratios and issue collateral verifications via API. The collateral compliance module ensures assets are backed by verifiable reserves. This guide explains how to configure the collateral compliance module and issue collateral claims via API, ensuring your tokenized assets are backed by verifiable reserves. For the web interface approach, see the [user guide](/docs/user-guides/compliance/collateral). ## How collateral validation works [#how-collateral-validation-works] When minting new units of an asset with the collateral module enabled, the system: 1. Checks if the asset's identity has a valid collateral claim 2. Verifies the claim was issued by an authorized issuer and is not expired 3. Confirms the collateral amount meets or exceeds the configured ratio based on post-mint total supply This module only applies to **minting operations**. Regular transfers between wallets are not affected by collateral requirements. ## Configuration parameters [#configuration-parameters] When configuring the collateral module during asset creation, three parameters are required: | Parameter | Type | Description | Valid values | | ------------------ | --------- | ----------------------------------------------------- | ------------------------ | | **proofTopic** | BigInt | ERC-735 claim topic ID for collateral proofs | System-provided topic ID | | **ratioBps** | number | Required backing ratio in basis points (10000 = 100%) | 0 to 20000 | | **trustedIssuers** | string\[] | Additional trusted issuers for the proof topic | Ethereum addresses | The collateral ratio is specified in basis points (1/100th of a percent): - **10000** = 100% backing (1,000 minted units require 1,000 collateral) - **15000** = 150% over-collateralized (1,000 minted units require 1,500 collateral) - **20000** = 200% maximum ratio (1,000 minted units require 2,000 collateral) See [Example scenario](#example-scenario) for a minting simulation. ## Configure collateral during asset creation [#configure-collateral-during-asset-creation] ### Prerequisites [#prerequisites] * Platform URL (e.g., `https://your-platform.example.com`) * API key from a user with the **Token Manager** role (see [Getting Started](/docs/developer-guides/api-integration/getting-started) for API key setup) * Wallet verification method enabled on your account (e.g., pincode or 2FA) Supply cap and collateral requirement configuration ### Get collateral module address [#get-collateral-module-address] Query the system to get the registered collateral compliance module address: ```bash curl -X GET "https://your-platform.example.com/api/system/default" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response:** ```json { "complianceModuleRegistry": { "complianceModules": [ { "typeId": "collateral", "module": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707", "name": "Collateral Compliance Module" } // ... other modules ] } } ``` Save the `module` address for the `typeId: "collateral"` entry. ### Get collateral topic ID [#get-collateral-topic-id] Query the registered claim topics to find the collateral topic ID: ```bash curl -X GET "https://your-platform.example.com/api/system/claim-topics" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response:** ```json [ { "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7...", "topicId": "56591694316807385155654796962642700009023257328234168678289712861780104020528", "name": "collateral", "signature": "uint256 amount, uint256 expiryTimestamp", "registry": { "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7" } } // ... other topics ] ``` Save the `topicId` value for the topic with `name: "collateral"`. ### Create asset with collateral module [#create-asset-with-collateral-module] Include the collateral module in the `initialModulePairs` array when creating the asset: ```bash curl -X POST "https://your-platform.example.com/api/token/create" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "equity", "name": "Collateralized Equity", "symbol": "COLEQ", "decimals": 18, "countryCode": "840", "priceCurrency": "USD", "basePrice": "100", "initialModulePairs": [ { "typeId": "collateral", "module": "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707", "values": { "proofTopic": "56591694316807385155654796962642700009023257328234168678289712861780104020528", "ratioBps": 10000, "trustedIssuers": [] } } ], "walletVerification": { "secretVerificationCode": "YOUR_PINCODE", "verificationType": "PINCODE" } }' ``` **Response:** ```json { "id": "0x1234567890AbCdEf1234567890AbCdEf12345678", "txHash": "0x8d95bfd5381478d90992d3e2e64c73178e46bb18592bfcbffdf899f2407aee9b" } ``` **Key fields in `initialModulePairs`:** | Field | Description | | ----------------------- | ------------------------------------------------------------------------------- | | `typeId` | Must be `"collateral"` for the collateral module | | `module` | The collateral module address from step 1 | | `values.proofTopic` | The collateral topic ID from step 2 | | `values.ratioBps` | Collateral ratio (10000 = 100%, 15000 = 150%) | | `values.trustedIssuers` | Additional issuers recognized alongside the global registry (not a replacement) | ### Verify module configuration [#verify-module-configuration] Query the asset to confirm the collateral module is configured: ```bash curl -X GET "https://your-platform.example.com/api/token/0x1234567890AbCdEf1234567890AbCdEf12345678" \ -H "X-Api-Key: YOUR_API_KEY" ``` Check that `complianceModuleConfigs` contains an entry with `typeId: "collateral"`. ## Issue a collateral claim [#issue-a-collateral-claim] After the asset is deployed with the collateral module, a collateral claim must be issued to the asset's identity before minting can occur. ### Prerequisites [#prerequisites-1] Collateral claims must be issued by a **Trusted issuer** for the collateral topic (see [Configure trusted issuers](/docs/developer-guides/compliance/configure-trusted-issuers)) Most teams delegate collateral attestations to compliance or treasury officers who operate as trusted issuers. Assign the `claimIssuer` role to those identities and register them for the collateral topic so they can service multiple assets. ### Claim fields [#claim-fields] | Field | Description | Format | | ------------------- | ---------------------------------------------- | ---------------------- | | **amount** | The collateral value backing the asset | String (in base units) | | **expiryTimestamp** | When the claim expires (must be in the future) | ISO 8601 date-time | ### Issue the collateral claim [#issue-the-collateral-claim] The collateral amount must be specified in base units (the smallest unit of the asset). For an asset with 18 decimals, multiply the desired amount by 10^18. For example, to back 100,000 units on an 18-decimal asset, use `"100000000000000000000000"` (100,000 × 10^18). You issue collateral claims to the token's OnchainID identity using the system endpoint. You can obtain the `targetIdentityAddress` from the token creation response (`identity.id`) or by calling `GET /token/{address}`. Collateral claims are issued to the token's identity contract, not the token address or an operator wallet. Always pass the `identity.id` value as `targetIdentityAddress`. ```bash curl -X POST "https://your-platform.example.com/api/system/identity/claim/issue" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "targetIdentityAddress": "0x20ADDd5023c494eA1594282ab6E94fA5A658C4f2", "claim": { "topic": "collateral", "data": { "amount": "100000000000000000000000", "expiryTimestamp": "2025-12-31T23:59:59Z" } }, "walletVerification": { "secretVerificationCode": "YOUR_PINCODE", "verificationType": "PINCODE" } }' ``` **Response:** ```json { "txHash": "0x9f85bfd5381478d90992d3e2e64c73178e46bb18592bfcbffdf899f2407aee9b", "success": true, "claimTopic": "collateral", "targetIdentity": "0x20ADDd5023c494eA1594282ab6E94fA5A658C4f2" } ``` Asset amounts depend on the asset's decimal configuration. See [Asset Decimals](/docs/developer-guides/api-integration/asset-decimals) for formatting guidance. With this collateral claim of 100,000 units and the 100% ratio configured above, you can mint up to 100,000 tokens. See [Example scenario](#example-scenario) for a detailed minting simulation. ### Verify the claim [#verify-the-claim] Query the asset to confirm the collateral claim was issued: ```bash curl -X GET "https://your-platform.example.com/api/token/0x1234567890AbCdEf1234567890AbCdEf12345678" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response (relevant fields):** ```json { "id": "0x1234567890AbCdEf1234567890AbCdEf12345678", "type": "equity", "name": "Collateralized Equity", "symbol": "COLEQ", "identity": { "id": "0x20ADDd5023c494eA1594282ab6E94fA5A658C4f2", "claims": [ { "name": "collateral", "revoked": false, "values": [ { "key": "amount", "value": "100000000000000000000000" }, { "key": "expiryTimestamp", "value": "1767225599" } ], "isTrusted": false } ] }, "complianceModuleConfigs": [ { "complianceModule": { "typeId": "collateral" } } ] } ``` **Key fields to verify:** * `identity.claims` contains the collateral claim with amount and expiry * `identity.claims[].revoked` is `false` (claim is active) Collateral claims have an expiry date. Renew claims before they expire to avoid blocking future minting operations. ## Example scenario [#example-scenario] Using the configuration from this guide, a company issues equity shares with 100% collateral backing. ### Configuration [#configuration] * **Collateral ratio:** 100% (10000 basis points) * **Proof topic:** System default (collateral) * **Trusted issuers:** Empty (using global registry) ### Collateral claim [#collateral-claim] * **Amount:** 100,000 units * **Expiry:** December 31, 2025 ### Minting simulation [#minting-simulation] | Operation | Current supply | Mint amount | Post-mint supply | Required collateral (100%) | Available | Result | | ------------ | -------------- | ----------- | ---------------- | -------------------------- | --------- | --------- | | Initial mint | 0 | 50,000 | 50,000 | 50,000 | 100,000 | ✅ Allowed | | Second mint | 50,000 | 50,000 | 100,000 | 100,000 | 100,000 | ✅ Allowed | | Third mint | 100,000 | 10,000 | 110,000 | 110,000 | 100,000 | ❌ Blocked | ### Why the third mint fails [#why-the-third-mint-fails] The third mint is blocked because: * Post-mint supply would be 110,000 units * At 100% ratio, required collateral = 110,000 * Current collateral claim only covers 100,000 * A new collateral claim with a higher amount is needed ### To unblock [#to-unblock] 1. Issue a new collateral claim with amount ≥ 110,000 (see [Issue a collateral claim](#issue-a-collateral-claim)) 2. Retry the minting operation ## Request parameters [#request-parameters] ### Collateral module configuration [#collateral-module-configuration] | Parameter | Type | Required | Description | | ----------------------- | --------- | -------- | ------------------------------------------------------------------------------- | | `typeId` | string | Yes | Must be `"collateral"` | | `module` | string | Yes | Collateral module contract address (from system query) | | `values.proofTopic` | string | Yes | ERC-735 claim topic ID (from claim topics query) | | `values.ratioBps` | number | Yes | Collateral ratio in basis points (0-20000) | | `values.trustedIssuers` | string\[] | No | Additional issuers recognized alongside the global registry (not a replacement) | ### Collateral claim issuance [#collateral-claim-issuance] | Parameter | Type | Required | Description | | ---------------------------- | ------ | -------- | ------------------------------------------------------- | | `targetIdentityAddress` | string | Yes | Token identity address receiving the claim | | `claim.topic` | string | Yes | Must be `"collateral"` | | `claim.data.amount` | string | Yes | Collateral amount as string (in base units) | | `claim.data.expiryTimestamp` | string | Yes | Claim expiry in ISO 8601 format (must be in the future) | | `walletVerification` | object | Yes | Wallet verification (pincode or TOTP) | ### Wallet verification object [#wallet-verification-object] | Field | Type | Description | | ------------------------ | ------ | --------------------------------------------------- | | `secretVerificationCode` | string | 6-digit pincode or TOTP code | | `verificationType` | string | `"PINCODE"` (default), `"SECRET_CODES"`, or `"OTP"` | ## Best practices [#best-practices] ### Collateral ratio planning [#collateral-ratio-planning] * Start with 100% (10000 basis points) for standard backing requirements * Use 150% (15000 basis points) or higher for over-collateralization when extra security is needed * Consider market volatility when setting ratios for assets backed by volatile collateral ### Claim renewal [#claim-renewal] To renew a collateral claim, issue a new claim using the steps in [Issue a collateral claim](#issue-a-collateral-claim) with an updated `expiryTimestamp` set to a future date. * Monitor collateral claim expiry dates and renew before they expire * Set calendar reminders or automated alerts for upcoming expirations * Allow buffer time for renewal to avoid blocking minting operations ### Trusted issuers [#trusted-issuers] * Leave `trustedIssuers` empty to rely solely on the global registry * Adding addresses to `trustedIssuers` extends the global registry—it does not replace it * Use this array for issuers not registered globally but authorized for this specific asset * Verify issuer addresses carefully before adding them ## Troubleshooting [#troubleshooting] | Issue | Solution | | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | `401 Unauthorized` | API key is invalid, expired, or disabled | | `403 USER_NOT_AUTHORIZED` | Ensure the caller has the required role: `tokenManager` for asset creation, or `claimIssuer` and trusted issuer status for claim issuance | | `Collateral module not found` | Query `/system/default` to get the correct module address | | `Invalid topic ID` | Query `/system/claim-topics` to get the correct collateral topic ID | | `Insufficient collateral` | Issue a collateral claim with sufficient amount before minting | | `Collateral verification expired` | Issue a new collateral claim with a future expiry date | | `ratioBps out of range` | Value must be between 0 and 20000 | ## Related guides [#related-guides] * [Configure Trusted Issuers](/docs/developer-guides/compliance/configure-trusted-issuers) - Set up issuer permissions * [Mint Assets](/docs/developer-guides/asset-servicing/mint-assets) - Mint tokens after configuring collateral # Compliance provider API reference Source: https://docs.settlemint.com/docs/developer-guides/compliance/compliance-provider-api-reference Reference for DALP compliance-provider integration endpoints, subject mapping endpoints, webhook headers, statuses, and monitoring fields. DALP exposes tenant-scoped endpoints for [Sumsub](https://docs.sumsub.com/), [ComplyAdvantage](https://docs.complyadvantage.com/), [Elliptic](https://developers.elliptic.co/), [Jumio](https://documentation.jumio.ai/docs/developer-resources/API/), [Middesk](https://docs.middesk.com/), [Onfido](https://documentation.onfido.com/api/3.1.0/), [Persona](https://docs.withpersona.com/api-introduction), [Trulioo](https://developer.trulioo.com/), [Veriff](https://devdocs.veriff.com/apidocs/veriff-public-api-guides), and other configured compliance-provider integration setup, subject mapping, webhook intake, and monitoring history. All endpoint paths below are relative to the versioned API base path. Webhook paths are relative to the platform origin. ## Provider kinds [#provider-kinds] | Provider kind | Current use | | ----------------- | ---------------------------------------------------------------------------------------- | | `sumsub` | Identity verdicts and applicant-on-hold monitoring alerts for configured identity topics | | `sumsub-aml` | AML / watchlist monitoring alerts on existing Sumsub applicants | | `sumsub-kyt` | KYT transaction and wallet monitoring for both entity and wallet subjects | | `complyadvantage` | AML and sanctions search monitoring with categorical alert severity | | `elliptic` | Wallet monitoring alerts for wallet-monitoring topics | | `jumio` | Identity-verification verdicts for the `knowYourCustomer` topic | | `middesk` | KYB verdicts for the `knowYourCustomer` topic | | `onfido` | Workflow Studio and classic API identity-verification verdicts for `knowYourCustomer` | | `persona` | Inquiry verdicts for the `knowYourCustomer` topic | | `trulioo` | DataVerify KYC/KYB verdicts for the `knowYourCustomer` topic | | `veriff` | Hosted KYC session verdicts for the `knowYourCustomer` topic | ## Roles [#roles] Compliance-provider read and manage operations require one of these system roles on the active organisation: * `admin` * `systemManager` * `complianceManager` ## Integration statuses [#integration-statuses] | Status | Meaning | | --------- | ---------------------------------------------------------------- | | `pending` | Provisioning or trusted-issuer registration is still in progress | | `active` | The integration can receive and process provider webhooks | | `paused` | Intake is paused by an operator | | `failed` | Provisioning failed and can be retried | | `revoked` | The integration has been revoked | ## Integration endpoints [#integration-endpoints] | Method | Path | Purpose | | ------ | ----------------------------------------------------------------- | -------------------------------------------- | | GET | `/compliance/integrations` | List tenant compliance-provider integrations | | POST | `/compliance/integrations` | Create and provision an integration | | GET | `/compliance/integrations/{integrationId}` | Read one integration | | POST | `/compliance/integrations/validate-credentials` | Validate write-only provider credentials | | POST | `/compliance/integrations/{integrationId}/pause` | Pause intake | | POST | `/compliance/integrations/{integrationId}/resume` | Resume a paused integration | | POST | `/compliance/integrations/{integrationId}/retry-provisioning` | Retry idempotent provisioning | | POST | `/compliance/integrations/{integrationId}/revoke` | Revoke the integration | | PATCH | `/compliance/integrations/{integrationId}/policy` | Update the revocation severity threshold | | POST | `/compliance/integrations/{integrationId}/rotate-secret` | Stage a new webhook signing secret | | POST | `/compliance/integrations/{integrationId}/promote-secret` | Promote the pending signing secret | | POST | `/compliance/integrations/{integrationId}/cancel-secret-rotation` | Discard a pending signing secret | | GET | `/compliance/integrations/{integrationId}/health` | Read integration health | | GET | `/compliance/integrations/{integrationId}/monitoring` | List monitoring alerts | | POST | `/compliance/subjects/transactions/register` | Register a Sumsub KYT transaction | ## Create integration request [#create-integration-request] `POST /compliance/integrations` accepts: | Field | Type | Notes | | ----------------------------- | ----------------------------------- | -------------------------------------------------------------------------- | | `providerKind` | Provider kind | Selects the provider adapter | | `credentials` | object | Provider-specific write-only API credentials | | `topicName` | claim topic name | One integration maps to one claim topic | | `webhookSigningSecret` | string | Secret used to verify inbound provider webhooks | | `webhookAuthMode` | `hmac` or `basic_auth_ip_allowlist` | Defaults to `hmac` for HMAC providers; Jumio resolves to IP allowlisting | | `webhookIpAllowlist` | string array, optional | Required and non-empty when the resolved mode is `basic_auth_ip_allowlist` | | `revocationSeverityThreshold` | integer from `0` through `100` | Defaults to `80` | Webhook authentication is resolved against the selected provider. Sumsub, Elliptic, ComplyAdvantage, Sumsub AML, Sumsub KYT, Middesk, Onfido, Persona, Trulioo, and Veriff use `hmac`. Creation rejects a non-empty `webhookIpAllowlist` for those integrations because their webhook verifiers do not consult an allowlist. Jumio always resolves to `basic_auth_ip_allowlist`, whether the request omits `webhookAuthMode` or supplies the default `hmac`. Jumio creation requires a non-empty `webhookIpAllowlist`. Sumsub credentials use: ```json { "providerKind": "sumsub", "credentials": { "appToken": "...", "secretKey": "..." } } ``` Sumsub AML credentials use: ```json { "providerKind": "sumsub-aml", "credentials": { "apiToken": "...", "secretKey": "...", "webhookSigningSecret": "...", "levelName": "aml-monitoring-level" } } ``` Sumsub KYT credentials use: ```json { "providerKind": "sumsub-kyt", "credentials": { "apiToken": "...", "secretKey": "...", "webhookSigningSecret": "...", "levelName": "kyt-monitoring-level" } } ``` Sumsub KYT supports both entity and wallet subject mapping: the applicant id anchors the entity subject, while `txn.info.address` is treated as the tenant wallet and `txn.counterparty.address` as the other side of the monitored transaction. Elliptic credentials use: ```json { "providerKind": "elliptic", "credentials": { "apiKey": "...", "apiSecret": "..." } } ``` ComplyAdvantage credentials use: ```json { "providerKind": "complyadvantage", "credentials": { "apiToken": "...", "webhookSigningSecret": "..." } } ``` Jumio credentials use: ```json { "providerKind": "jumio", "credentials": { "apiToken": "...", "apiSecret": "...", "region": "eu-1", "basicAuthCredentials": "..." }, "webhookSigningSecret": "jumio-callback-basic-auth-secret", "webhookAuthMode": "basic_auth_ip_allowlist", "webhookIpAllowlist": ["203.0.113.10"] } ``` Jumio regions are `amer-1`, `eu-1`, and `sg-1`. Jumio uses callback Basic Auth plus an IP allowlist instead of HMAC-only webhook authentication. The Basic Auth credential configured in the Jumio callback must match the integration `webhookSigningSecret`; `credentials.basicAuthCredentials` is provider credential material, not the callback verifier secret. Middesk credentials use: ```json { "providerKind": "middesk", "credentials": { "apiKey": "...", "webhookSigningSecret": "..." } } ``` Onfido credentials use: ```json { "providerKind": "onfido", "credentials": { "apiToken": "...", "webhookSigningSecret": "...", "region": "eu" } } ``` Onfido regions are `us`, `eu`, and `ca`. Persona credentials use: ```json { "providerKind": "persona", "credentials": { "apiToken": "...", "webhookSigningSecret": "...", "inquiryTemplateId": "itmpl_..." } } ``` Trulioo credentials use: ```json { "providerKind": "trulioo", "credentials": { "apiKey": "...", "apiSecret": "...", "webhookSigningSecret": "..." } } ``` Veriff credentials use: ```json { "providerKind": "veriff", "credentials": { "apiKey": "...", "apiSecret": "...", "webhookSigningSecret": "..." } } ``` ## Integration response fields [#integration-response-fields] | Field | Meaning | | ----------------------------- | --------------------------------------------------- | | `id` | Integration identifier | | `providerKind` | Provider kind | | `topicName` | Configured claim topic name | | `topicId` | Claim topic ID as a digit string | | `status` | Current integration status | | `statusExplanation` | Human-readable status details | | `issuerEoaAddress` | Provider issuer EOA registered as trusted issuer | | `webhookUrlToken` | URL token used in the provider webhook URL | | `revocationSeverityThreshold` | Severity threshold for monitoring-driven revocation | | `pendingSecretExpiresAt` | Expiry for the pending webhook signing secret | | `lastActivity` | Latest verdict or alert activity | | `lastHealthCheck` | Last stored credential/secret health result | | `createdAt` / `updatedAt` | Integration timestamps | ## Subject endpoints [#subject-endpoints] | Method | Path | Provider | Purpose | | ------ | --------------------------------------- | --------------- | ------------------------------------------- | | POST | `/compliance/subjects/create-applicant` | Sumsub | Create and map a Sumsub applicant | | POST | `/compliance/subjects/create-applicant` | Jumio | Create and map a Jumio applicant | | POST | `/compliance/subjects/create-applicant` | Middesk | Create and map a Middesk business | | POST | `/compliance/subjects/create-applicant` | Onfido | Create and map an Onfido applicant | | POST | `/compliance/subjects/create-applicant` | Persona | Create and map a Persona inquiry | | POST | `/compliance/subjects/create-applicant` | Trulioo | Run DataVerify and map a Trulioo subject | | POST | `/compliance/subjects/create-applicant` | Veriff | Create and map a hosted Veriff session | | POST | `/compliance/subjects/register-wallet` | ComplyAdvantage | Register and map a monitored entity search | | POST | `/compliance/subjects/register-wallet` | Elliptic | Register and map an Elliptic wallet subject | `create-applicant` accepts: | Field | Type | Notes | | ---------------------------------------------------------- | -------------------- | ------------------------------------------------------------- | | `integrationId` | UUID | Must identify an active Surface-A integration | | `identityAddress` | Ethereum address | DALP identity address in the active system | | `applicantHints.externalUserId` | string, optional | External subject identifier; defaults to the identity address | | `applicantHints.level` | string, optional | Provider workflow, level, or configuration selector | | `applicantHints.name` / `businessName` | string, Middesk only | Business name | | `applicantHints.tin` | string, Middesk only | Business tax identifier | | `applicantHints.addresses` | array, Middesk only | Business addresses | | `applicantHints.firstName` / `first_name` | string, Onfido only | Applicant first name | | `applicantHints.lastName` / `last_name` | string, Onfido only | Applicant last name | | `applicantHints.inquiryTemplateId` / `inquiry_template_id` | string, Persona only | Persona inquiry template override | | `applicantHints.fields` | object, Persona only | Persona inquiry fields | | `applicantHints.countryCode` | string, Trulioo only | DataVerify country code | | `applicantHints.demographics` | object, Trulioo only | DataVerify `DataFields.PersonInfo` input | | `applicantHints.person` | object, Veriff only | Hosted session person prefill | | `applicantHints.callbackUrl` | string, Veriff only | Veriff callback URL | `create-applicant` returns `externalId` and may return `redirectUrl`. When `applicantHints.level` is omitted, DALP forwards its default level value to providers that need a selector. Use that omission only when the provider tenant has a real workflow or configuration with that exact value. The default is not a portable Jumio or Onfido workflow identifier. For Jumio, pass `applicantHints.level` as the tenant's workflow definition key, such as the numeric key or label configured in Jumio. DALP uses `applicantHints.externalUserId`, or the identity address when omitted, as the external subject id and may return a hosted web URL as `redirectUrl`. For Middesk, pass `applicantHints.name` or `businessName`, `applicantHints.tin`, and at least one address in `applicantHints.addresses`. DALP stores Middesk `external_id`, falling back to the external subject id supplied in the request. For Onfido, pass `applicantHints.level` as the active workflow id from Onfido, and pass `applicantHints.firstName` and `applicantHints.lastName`. DALP forwards optional email, phone number, redirect URLs, and locale. DALP also maps the returned Onfido applicant id when it differs from the external subject id. For Persona, pass `applicantHints.inquiryTemplateId` or configure `inquiryTemplateId` on the integration. DALP creates an inquiry and stores the returned Persona inquiry id as the external subject id. The response may include the hosted inquiry URL as `redirectUrl`. For Trulioo, pass `applicantHints.countryCode` and `applicantHints.demographics`. DALP forwards the demographics object to DataVerify as `DataFields.PersonInfo`. DALP stores `CustomerReferenceID` as the external subject id. This adapter does not return a hosted redirect URL. For Veriff, pass `applicantHints.person` when pre-filling hosted session person data and `applicantHints.callbackUrl` when the session should carry an explicit Veriff callback URL. DALP stores `vendorData` as the external subject id and returns the hosted session URL as `redirectUrl`. `register-wallet` accepts either an Elliptic wallet mapping or a ComplyAdvantage entity-search mapping: | Field | Type | Notes | | ------------------------- | ---------------------- | ----------------------------------------------------------------------- | | `integrationId` | UUID | Must identify an active Surface-B integration | | `walletAddress` | Ethereum address | Elliptic only; wallet already registered to a DALP identity | | `identityAddress` | Ethereum address | ComplyAdvantage only; DALP identity to bind to the search | | `subjectHints.searchTerm` | string | ComplyAdvantage search term | | `subjectHints.clientRef` | string, optional | ComplyAdvantage client reference; defaults to `identityAddress` | | `subjectHints.entityType` | string, optional | ComplyAdvantage entity type; defaults to `person` | | `subjectHints.types` | string array, optional | ComplyAdvantage screening list types; defaults to `sanction`, `warning` | `register-wallet` returns `externalId` and the resolved `identityAddress`. ## Webhook paths [#webhook-paths] Provider dashboards send webhook events to: ```text /api/webhooks/compliance/// ``` Current provider path segments are: * `sumsub` * `sumsub-aml` * `sumsub-kyt` * `complyadvantage` * `elliptic` * `jumio` * `middesk` * `onfido` * `persona` * `trulioo` * `veriff` The request body limit is 64 KiB. ## Webhook signatures [#webhook-signatures] | Provider | Headers | Accepted digest | | --------------- | ------------------------------------------ | ---------------------------------------------------------------------- | | Sumsub | `x-payload-digest`, `x-payload-digest-alg` | `HMAC_SHA1_HEX`, `HMAC_SHA256_HEX`, or `HMAC_SHA512_HEX` over raw body | | ComplyAdvantage | `x-complyadvantage-signature` | HMAC-SHA256 hex over raw body | | Elliptic | `x-elliptic-signature` | HMAC-SHA256 hex over raw body, with optional `sha256=` prefix | | Jumio | `Authorization` | Basic Auth credentials plus configured source IP allowlist | | Middesk | `x-middesk-signature-256` | HMAC-SHA256 hex over raw body | | Onfido | `x-sha2-signature` | HMAC-SHA256 hex over raw body | | Persona | `persona-signature` | HMAC-SHA256 hex over `${timestamp}.${rawBody}` | | Trulioo | `x-trulioo-signature` | HMAC-SHA256 hex over raw body | | Veriff | `x-hmac-signature` | HMAC-SHA256 hex over raw body | If Sumsub omits `x-payload-digest-alg`, DALP uses `HMAC_SHA256_HEX`. Persona accepts space-separated signature sets during key rotation. Trulioo DataVerify webhooks prefer the top-level `TransactionId` as the provider event id. Legacy deliveries without that stable id use a lower-trust composite fallback: SHA-256 over `(TransactionId, RecordStatus, body_hash)`, where `body_hash` is the SHA-256 hex digest of the raw body. ComplyAdvantage webhooks prefer the top-level `id` as the provider event id. Legacy deliveries without that stable id use a composite fallback: SHA-256 over `(search_id, created_at)`. ## Webhook processing outcomes [#webhook-processing-outcomes] | Condition | Behaviour | | -------------------------------------------------- | ---------------------------------------------------------------- | | Signature invalid | Request is rejected | | Body exceeds 64 KiB | Request is rejected as too large | | New event is outside the five-minute replay window | Request is rejected | | Duplicate completed event | Event is treated as replayed | | Retryable claim processing failure | Request returns retry behaviour so the provider can resend | | Subject is unmapped | Event is recorded for audit, but no on-chain claim effect occurs | ## Monitoring fields [#monitoring-fields] The monitoring endpoint returns paginated rows with: | Field | Meaning | | ----------------- | ---------------------------------------------------------- | | `id` | Monitoring row ID | | `providerEventId` | Provider event identifier | | `subjectAddress` | DALP subject address when resolved | | `topicName` | Claim topic when available | | `severity` | Normalised severity from `0` through `100`, when available | | `outcome` | Processing outcome | | `rawPayload` | Original provider payload retained for audit | | `processedAt` | Processing timestamp when completed | | `createdAt` | Row creation timestamp | ## See also [#see-also] * [Onboard a compliance provider](/docs/developer-guides/compliance/onboarding-a-provider) * [Map compliance-provider subjects](/docs/developer-guides/compliance/compliance-provider-subjects) * [How compliance provider intake works](/docs/architecture/integrations/compliance-providers) # Map compliance-provider subjects Source: https://docs.settlemint.com/docs/developer-guides/compliance/compliance-provider-subjects Developer guide for creating DALP subject mappings so compliance-provider webhooks can issue or revoke claims for known identities. Subject mapping connects a provider applicant, search, wallet, or monitored transaction to a DALP identity. After mapping, provider webhooks can issue or revoke claims for the correct on-chain identity. For provider setup in the dapp, see [Onboard a compliance provider](/docs/developer-guides/compliance/onboarding-a-provider). ## Prerequisites [#prerequisites] * API access for a user with `admin`, `systemManager`, or `complianceManager` on the active organisation. * An active compliance-provider integration. * The integration ID from the dapp integration detail page or the integration list endpoint. * A DALP identity or wallet that is already registered in the active system identity registry. ## Map a Sumsub applicant [#map-a-sumsub-applicant] Use this path when the provider integration kind is `sumsub`. The request creates a Sumsub applicant and stores the subject mapping for the DALP identity. ```bash curl -X POST "https://your-platform.example.com/api/v2/compliance/subjects/create-applicant" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "integrationId": "11111111-1111-1111-1111-111111111111", "identityAddress": "0x1111111111111111111111111111111111111111", "applicantHints": { "externalUserId": "investor-123", "level": "basic-kyc-level" } }' ``` Response: ```json { "data": { "externalId": "sumsub-external-user-id", "redirectUrl": "https://..." }, "links": { "self": "/v2/compliance/subjects/create-applicant" } } ``` DALP validates that the identity belongs to the active system's identity registry before it stores the mapping. DALP stores the Sumsub external ID. When Sumsub returns a different applicant ID, DALP stores that applicant ID as an alternate mapping because Sumsub webhooks may use either identifier. ## Register an Elliptic wallet [#register-an-elliptic-wallet] Use this path when the provider integration kind is `elliptic`. The request registers the wallet with Elliptic and stores the wallet-to-identity mapping in DALP. ```bash curl -X POST "https://your-platform.example.com/api/v2/compliance/subjects/register-wallet" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "integrationId": "22222222-2222-2222-2222-222222222222", "walletAddress": "0x2222222222222222222222222222222222222222" }' ``` Response: ```json { "data": { "externalId": "elliptic-subject-id", "identityAddress": "0x3333333333333333333333333333333333333333" }, "links": { "self": "/v2/compliance/subjects/register-wallet" } } ``` DALP resolves the wallet's on-chain identity before writing the subject mapping. Wallets that are not registered as DALP identities are rejected. ## Register a ComplyAdvantage entity search [#register-a-complyadvantage-entity-search] Use this path when the provider integration kind is `complyadvantage`. The request creates a ComplyAdvantage search and enables monitoring. DALP stores the search client reference as the external subject mapping for the DALP identity. ```bash curl -X POST "https://your-platform.example.com/api/v2/compliance/subjects/register-wallet" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "integrationId": "33333333-3333-3333-3333-333333333333", "identityAddress": "0x3333333333333333333333333333333333333333", "subjectHints": { "searchTerm": "Ada Lovelace", "clientRef": "investor-123", "entityType": "person", "types": ["sanction", "warning"] } }' ``` Response: ```json { "data": { "externalId": "complyadvantage-search-id", "identityAddress": "0x3333333333333333333333333333333333333333" }, "links": { "self": "/v2/compliance/subjects/register-wallet" } } ``` ComplyAdvantage is entity-level monitoring, not wallet monitoring. The subject mapping stores the provider `client_ref` against the DALP identity and leaves `walletAddress` empty. ## Register a Sumsub KYT transaction [#register-a-sumsub-kyt-transaction] Use this path when the provider integration kind is `sumsub-kyt`. The request registers a transaction with Sumsub KYT. DALP stores mapping rows for the returned KYT identifiers when the transaction resolves to a known identity. ```bash curl -X POST "https://your-platform.example.com/api/v2/compliance/subjects/transactions/register" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "integrationId": "44444444-4444-4444-4444-444444444444", "kytDataTxnId": "transfer-2026-0001", "txn": { "applicantId": "sumsub-applicant-id", "info": { "address": "0x4444444444444444444444444444444444444444" } } }' ``` Response: ```json { "data": { "kytDataTxnId": "transfer-2026-0001", "kytTxnId": "sumsub-kyt-transaction-id", "identityAddress": "0x5555555555555555555555555555555555555555" }, "links": { "self": "/v2/compliance/subjects/transactions/register" } } ``` If `txn.info.address` is present, DALP resolves that wallet strictly against the active system's identity registry. If the wallet is unknown, DALP does not fall back to `applicantId`, so the transaction is not mapped to the wrong identity. When the request omits `txn.info.address` and includes `txn.applicantId`, DALP can reuse the existing applicant mapping created by `register-wallet`. If an identity is resolved, DALP stores mappings for both returned KYT identifiers so later Sumsub KYT webhooks can resolve the same identity. If no identity is resolved, the response omits `identityAddress` and no subject mapping is written for that transaction. ## Handle provider events after mapping [#handle-provider-events-after-mapping] After the subject is mapped, provider webhooks can affect the configured claim topic: * Sumsub approved applicant-review events issue claims. * Sumsub rejected applicant-review events revoke claims. * Sumsub applicant-on-hold events appear in monitoring history. * ComplyAdvantage search monitoring events appear in monitoring history and can revoke claims when severity meets the integration threshold. * Elliptic wallet alerts appear in monitoring history and can revoke claims when severity meets the integration threshold. Map the subject before provider webhook delivery. DALP records unmapped events for audit. No on-chain claim effect is created. DALP does not automatically replay the same delivered provider event after mapping. ## Common errors [#common-errors] * **Wrong provider kind.** `create-applicant` requires a Surface-A integration. `register-wallet` requires a Surface-B integration. `transactions/register` is only available for Sumsub KYT integrations. * **Inactive integration.** The integration must be active before subject mapping can be used for claim effects. * **Wallet not registered.** Register the wallet as a DALP identity before calling `register-wallet`. * **Provider credential failure.** Check the integration credentials and provider account status. * **Duplicate or conflicting subject mapping.** Confirm the provider subject belongs to the same DALP identity before retrying. ## See also [#see-also] * [Sumsub developer documentation](https://docs.sumsub.com/) * [Elliptic developer documentation](https://developers.elliptic.co/) * [Compliance provider API reference](/docs/developer-guides/compliance/compliance-provider-api-reference) * [Onboard a compliance provider](/docs/developer-guides/compliance/onboarding-a-provider) * [How compliance provider intake works](/docs/architecture/integrations/compliance-providers) # Configure trusted issuers Source: https://docs.settlemint.com/docs/developer-guides/compliance/configure-trusted-issuers Configure trusted entities who can issue verifications via API. For the web interface approach, see the [user guide](/docs/user-guides/compliance/configure-trusted-issuers). ## Prerequisites [#prerequisites] * Platform URL (e.g., `https://your-platform.example.com`) * API key from a user with the **Claim Policy Manager** role (see [Getting Started](/docs/developer-guides/api-integration/getting-started) for API key setup) * Wallet verification method enabled on your account (e.g., pincode or 2FA) * Understanding of your compliance requirements * Wallet address of the user you want to make a trusted issuer ## About trusted issuers [#about-trusted-issuers] Trusted issuers are entities authorized by your organization to issue verifications that are automatically recognized for compliance purposes. For background on how the verification system works, see [Compliance Overview](/docs/user-guides/compliance/overview). Trust relationships are specific to your organization. An issuer trusted by Organization A may not be trusted by Organization B for the same verification topics. ## Available verification topics [#available-verification-topics] The platform comes with preset verification topics for common compliance scenarios. Each topic has a unique `topicId` and a `signature` defining the data structure for that verification type. These are preset topics included with the platform. You can create additional custom verification topics to meet your specific compliance requirements. ### Investor verification topics [#investor-verification-topics] | Topic Name | Signature | Purpose | | -------------------------------- | -------------- | ----------------------------------------- | | `knowYourCustomer` | `string claim` | Basic KYC verification | | `accreditedInvestor` | `string claim` | Accredited investor status | | `accreditedInvestorVerified` | `string claim` | Verified accredited investor | | `qualifiedInstitutionalInvestor` | `string claim` | Qualified institutional buyer (QIB) | | `professionalInvestor` | `string claim` | Professional investor status | | `antiMoneyLaundering` | `string claim` | AML compliance verification | | `regulationS` | `string claim` | Regulation S (non-US investor) compliance | ### Issuer verification topics [#issuer-verification-topics] | Topic Name | Signature | Purpose | | -------------------------- | ----------------------------------------------------------------------------------- | ----------------------- | | `issuerLicensed` | `string licenseType, string licenseNumber, string jurisdiction, uint256 validUntil` | Issuer licensing status | | `issuerJurisdiction` | `string jurisdiction` | Issuer jurisdiction | | `issuerProspectusFiled` | `string prospectusReference` | Prospectus filing | | `issuerProspectusExempt` | `string exemptionReference` | Prospectus exemption | | `issuerReportingCompliant` | `bool compliant, uint256 lastUpdated` | Reporting compliance | ### Asset verification topics [#asset-verification-topics] | Topic Name | Signature | Purpose | | --------------------- | ----------------------------------------------------- | ---------------------- | | `assetClassification` | `string class, string category` | Asset classification | | `assetLocation` | `string city, string districtCode, string areaId` | Asset location | | `assetIssuer` | `address issuerAddress` | Asset issuer identity | | `basePrice` | `uint256 amount, string currencyCode, uint8 decimals` | Base price information | | `collateral` | `uint256 amount, uint256 expiryTimestamp` | Collateral details | | `contractIdentity` | `address contractAddress` | Contract identity | | `uniqueIdentifier` | `string identifier` | Unique identifier | Each topic has a unique large numeric `topicId` (e.g., `26984799302505749158794800959285050858086405868089409909048783980951278841746`). Use the `GET /api/system/claim-topics` endpoint to retrieve the exact topic IDs for your platform. ## Configuring trusted issuers [#configuring-trusted-issuers] ### Get issuer's identity address [#get-issuers-identity-address] First, you need the identity contract address of the user you want to make a trusted issuer. If you have their wallet address, query their identity: ```bash curl -X GET "https://your-platform.example.com/api/system/identity/by-wallet/0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response:** ```json { "id": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890", "account": { "id": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "contractName": null }, "isContract": false, "hasIdentity": true, "claims": [] } ``` **Important:** Use the `id` field (identity CONTRACT address), not the `account.id` (wallet address). ### List available verification topics [#list-available-verification-topics] Query the available verification topics to identify which topics you want this issuer to be trusted for: ```bash curl -X GET "https://your-platform.example.com/api/system/claim-topics" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response:** ```json [ { "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7...", "topicId": "26984799302505749158794800959285050858086405868089409909048783980951278841746", "name": "knowYourCustomer", "signature": "string claim", "registry": { "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7" } }, { "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7...", "topicId": "15733030998618876990024220391915773205162379317494393310546829862321881862123", "name": "accreditedInvestor", "signature": "string claim", "registry": { "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7" } }, { "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7...", "topicId": "39526553109170329799339511574661256630735485618560740361645615581310848276505", "name": "qualifiedInstitutionalInvestor", "signature": "string claim", "registry": { "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7" } } // ... additional topics available ] ``` Note the `topicId` values for the topics you want to assign to this issuer. See [Available verification topics](#available-verification-topics) above for the complete list. Trusted issuer configuration in the verification topic registry ### Add trusted issuer [#add-trusted-issuer] Create the trusted issuer by specifying their identity address and the verification topic IDs they can issue: ```bash curl -X POST "https://your-platform.example.com/api/system/trusted-issuers" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "issuerAddress": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890", "claimTopicIds": [ "26984799302505749158794800959285050858086405868089409909048783980951278841746", "15733030998618876990024220391915773205162379317494393310546829862321881862123" ], "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` **Response:** ```json { "txHash": "0x1234567890abcdef...", "issuerAddress": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890" } ``` You can assign multiple verification topics in a single request by including multiple topic IDs in the `claimTopicIds` array. ### Verify completion [#verify-completion] Query the trusted issuers list to confirm the issuer was added with the correct verification topics: ```bash curl -X GET "https://your-platform.example.com/api/system/trusted-issuers" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response:** ```json [ { "id": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890", "account": { "id": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" }, "claimTopics": [ { "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7...", "topicId": "26984799302505749158794800959285050858086405868089409909048783980951278841746", "name": "knowYourCustomer", "signature": "string claim" }, { "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7...", "topicId": "15733030998618876990024220391915773205162379317494393310546829862321881862123", "name": "accreditedInvestor", "signature": "string claim" } ], "deployedInTransaction": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" } ] ``` **Key fields to verify:** * `id` matches the issuer's identity address you added * `claimTopics` contains the verification topics you assigned (with fields: `id`, `topicId`, `name`, `signature`) * `account.id` shows the issuer's wallet address The issuer can now issue verifications for the assigned topics. ## Managing trusted issuers [#managing-trusted-issuers] ### Add or remove one topic atomically [#add-or-remove-one-topic-atomically] Use the single-topic endpoints when your integration needs to add or remove one verification topic without replacing the issuer's full topic list: ```bash curl -X POST "https://your-platform.example.com/api/v2/system/trusted-issuers/0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890/claim-topics/26984799302505749158794800959285050858086405868089409909048783980951278841746" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` Use `DELETE` on the same path to remove the topic from that issuer. These endpoints compute the next topic list server-side, so a single-topic add or remove does not overwrite unrelated topics that are already assigned to the issuer. The response includes `issuerAddress`, `topicId`, and `txHash`. If the requested add is already present, or the requested remove is already absent, the call is idempotent and returns an empty `txHash` instead of submitting another on-chain update. If another claim-topic mutation for the same trusted issuer is already running, the API returns a retryable `409` `DALP-0461` conflict. Wait for the active mutation to finish, refresh the issuer's topic list, then retry only if the topic still needs to change. ### Update issuer topics [#update-issuer-topics] To replace the full set of verification topics for an issuer: ```bash curl -X PUT "https://your-platform.example.com/api/system/trusted-issuers/0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "claimTopicIds": [ "26984799302505749158794800959285050858086405868089409909048783980951278841746", "15733030998618876990024220391915773205162379317494393310546829862321881862123", "39526553109170329799339511574661256630735485618560740361645615581310848276505" ], "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` **Response:** ```json { "txHash": "0xabcdef1234567890...", "issuerAddress": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890" } ``` The `claimTopicIds` array replaces all existing topics. Include all topics you want the issuer to be trusted for, not just new additions. ### Remove trusted issuer [#remove-trusted-issuer] To revoke an issuer's trusted status: ```bash curl -X DELETE "https://your-platform.example.com/api/system/trusted-issuers/0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` **Response:** ```json { "txHash": "0x9876543210fedcba...", "issuerAddress": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890" } ``` Removing an issuer doesn't invalidate existing verifications but prevents new ones. Existing verifications remain valid until expiration. ### Get topic details with trusted issuers [#get-topic-details-with-trusted-issuers] Get full details for a specific claim topic, including its trusted issuers: ```bash curl -X GET "https://your-platform.example.com/api/system/claim-topics/knowYourCustomer" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response:** ```json { "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7...", "name": "knowYourCustomer", "signature": "string claim", "topicId": "26984799302505749158794800959285050858086405868089409909048783980951278841746", "trustedIssuers": [ { "id": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890", "addedAt": "2024-01-15T10:30:00Z", "revokedAt": "1970-01-01T00:00:00Z", "account": { "id": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" } } ] } ``` The response includes full topic details along with all non-revoked trusted issuers authorized for that topic. ### List claim topics for a trusted issuer [#list-claim-topics-for-a-trusted-issuer] Get the full topic list for a specific trusted issuer identity: ```bash curl -X GET "https://your-platform.example.com/api/system/trusted-issuers/0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890/topics" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response:** ```json { "issuerAddress": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890", "claimTopics": [ { "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7...", "topicId": "26984799302505749158794800959285050858086405868089409909048783980951278841746", "name": "knowYourCustomer", "signature": "string claim" } ] } ``` ## Request parameters [#request-parameters] ### Create trusted issuer [#create-trusted-issuer] | Parameter | Type | Required | Description | | -------------------- | --------- | -------- | ----------------------------------------------------- | | `issuerAddress` | string | Yes | Identity contract address (0x...) | | `claimTopicIds` | string\[] | Yes | Array of verification topic IDs (min: 1) | | `walletVerification` | object | Yes | Your wallet verification to authorize the transaction | ### Update trusted issuer [#update-trusted-issuer] | Parameter | Type | Required | Description | | -------------------- | --------- | -------- | ----------------------------------------------------- | | `issuerAddress` | string | Yes | Identity address to update (path parameter) | | `claimTopicIds` | string\[] | Yes | New array of verification topic IDs (min: 1) | | `walletVerification` | object | Yes | Your wallet verification to authorize the transaction | ### Delete trusted issuer [#delete-trusted-issuer] | Parameter | Type | Required | Description | | -------------------- | ------ | -------- | ----------------------------------------------------- | | `issuerAddress` | string | Yes | Identity address to remove (path parameter) | | `walletVerification` | object | Yes | Your wallet verification to authorize the transaction | ### Get topic details (with trusted issuers) [#get-topic-details-with-trusted-issuers-1] | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------------------------------------------------------- | | `name` | string | Yes | Claim topic name (path parameter, e.g., `knowYourCustomer`) | ### List claim topics for issuer [#list-claim-topics-for-issuer] | Parameter | Type | Required | Description | | --------------- | ------ | -------- | -------------------------------------------- | | `issuerAddress` | string | Yes | Trusted issuer identity address (path param) | ### Wallet verification object [#wallet-verification-object] | Field | Type | Description | | ------------------------ | ------ | ---------------------------------------------- | | `secretVerificationCode` | string | 6-digit pincode or TOTP code | | `verificationType` | string | "PINCODE" (default), "SECRET\_CODES", or "OTP" | ## Response fields [#response-fields] ### Trusted issuer [#trusted-issuer] | Field | Type | Description | | ----------------------- | ------ | ------------------------------------------ | | `id` | string | Issuer identity contract address | | `account.id` | string | Issuer wallet address (optional) | | `claimTopics` | array | Verification topics this issuer can verify | | `deployedInTransaction` | string | Transaction hash where issuer was added | ### Verification topic [#verification-topic] **In trusted issuer response (`claimTopics` array):** | Field | Type | Description | | ----------- | ------ | ------------------------------------------- | | `id` | string | Topic scheme identifier | | `topicId` | string | Large numeric ID unique to the topic | | `name` | string | Human-readable name of the topic | | `signature` | string | Claim data parameter types for verification | **In claim topics endpoint (`GET /api/system/claim-topics`):** Includes all fields above plus: | Field | Type | Description | | ------------- | ------ | -------------------------------------------- | | `registry` | object | Registry information containing registry ID | | `registry.id` | string | Contract address of the claim topic registry | ## Best practices [#best-practices] ### Issuer selection criteria [#issuer-selection-criteria] Choose trusted issuers based on: * **Authority** - Legal or professional standing * **Expertise** - Knowledge of verification area * **Independence** - Avoid conflicts of interest * **Reliability** - Consistent and accurate ### Topic assignment principles [#topic-assignment-principles] * **Segregation** - Separate issuer types by domain * **Redundancy** - Multiple issuers for critical topics * **Specialization** - Match expertise to topics * **Compliance** - Follow regulatory requirements ### Operational considerations [#operational-considerations] 1. **Regular audits** - Review issuer activities 2. **Rotation** - Periodically update issuers 3. **Training** - Ensure issuers understand responsibilities 4. **Documentation** - Record issuer selection rationale ## Troubleshooting [#troubleshooting] | Issue | Solution | | ---------------------------------- | ------------------------------------------------------------------------------------------------------------- | | `401 Unauthorized` | API key is invalid, expired, or disabled | | `403 USER_NOT_AUTHORIZED` | Ensure your account has `claimPolicyManager` role | | `Identity not found` | User must be registered first; see [Register User](/docs/developer-guides/user-management/register-user) | | `Invalid issuer address` | Use the identity CONTRACT address, not the wallet address | | `Topic not found` | Verify the topic ID exists using `GET /api/system/claim-topics` | | `409 DALP-0461` retryable conflict | Another topic mutation for this trusted issuer is still running. Refresh the topic list, then retry if needed | | `Issuer already exists` | The issuer is already registered; use PUT to update topics | ## Related guides [#related-guides] * [Compliance Overview](/docs/user-guides/compliance/overview) - Complete compliance reference * [Verify KYC](/docs/developer-guides/compliance/verify-kyc) - Issue verifications via API * [Add Administrators](/docs/developer-guides/platform-setup/add-admins) - Grant roles via API * [Configure Trusted Issuers (User Guide)](/docs/user-guides/compliance/configure-trusted-issuers) - Web interface approach # Onboard a compliance provider Source: https://docs.settlemint.com/docs/developer-guides/compliance/onboarding-a-provider Operator guide for adding Sumsub or Elliptic in the DALP dapp, configuring provider webhooks, and reaching an active compliance-provider integration. DALP administrators and compliance managers add provider integrations in the dapp, then configure provider webhook delivery and subject mapping. For API subject mapping after the integration is active, see [Map compliance-provider subjects](/docs/developer-guides/compliance/compliance-provider-subjects). For endpoint and schema details, see [Compliance provider API reference](/docs/developer-guides/compliance/compliance-provider-api-reference). ## Prerequisites [#prerequisites] * A DALP user with the `admin`, `systemManager`, or `complianceManager` role on the active organisation. * A Sumsub account with an app token and secret key, or an Elliptic account with API key and API secret. * Access to the provider dashboard where webhook destinations and signing secrets are configured. * An operational trusted issuer registry for the DALP organisation. ## Add the integration in DALP [#add-the-integration-in-dalp] ### Open the provider section [#open-the-provider-section] In the dapp, go to **Platform Settings → Compliance providers → Add integration**. ### Select the provider [#select-the-provider] Choose one provider: * **Sumsub** for identity verdict topics such as `knowYourCustomer`, `antiMoneyLaundering`, `accreditedInvestor`, and `regulationS`. Sumsub applicant-on-hold events also appear as monitoring alerts for the integration. * **Elliptic** for wallet monitoring. The topic selector shows wallet-monitoring topics. Each integration declares exactly one claim topic. If one provider workflow covers multiple topics, create one integration per topic. ### Enter write-only credentials [#enter-write-only-credentials] Sumsub asks for **App token** and **Secret key**. Elliptic asks for **API key** and **API secret**. DALP also asks for the **webhook signing secret** that you configure in the provider dashboard. Credentials are encrypted and never displayed back. The dapp validates the credential shape before provisioning starts. ### Set the revocation threshold [#set-the-revocation-threshold] For monitoring alerts, select the severity tier that should revoke claims for this integration. The default threshold is `80`, shown in the dapp as the **High** tier. Sumsub applicant-review verdicts issue or revoke claims from the verdict state. Sumsub applicant-on-hold events and Elliptic wallet alerts use severity-based monitoring behaviour. ### Wait for provisioning [#wait-for-provisioning] After credential validation, DALP provisions the provider issuer EOA and registers it as a trusted issuer for the integration's claim topic. The integration status can be: * `pending`: provisioning or trusted-issuer registration is still in progress * `active`: webhook intake is ready * `paused`: intake is paused by an operator * `failed`: provisioning failed and can be retried * `revoked`: the integration has been revoked If provisioning fails, open the integration detail page and use **Retry provisioning**. The retry is idempotent. ### Copy the webhook URL [#copy-the-webhook-url] On completion, DALP opens the integration detail page. Copy the webhook URL: ```text https://your-platform.example.com/api/webhooks/compliance/// ``` The URL token is partially masked by default in the UI. Use the reveal toggle before copying the full URL. ## Configure the provider dashboard [#configure-the-provider-dashboard] ### Sumsub [#sumsub] In the Sumsub dashboard, go to **Dev space → Webhooks → Webhook manager → Create webhook**. Sumsub documents the flow in its [Webhook manager guide](https://docs.sumsub.com/docs/webhook-manager). * Use the DALP webhook URL as the HTTP address. * Configure the same webhook signing secret you entered in DALP. * Sumsub sends `x-payload-digest-alg` and `x-payload-digest`; DALP verifies the digest against the raw request body. ### Elliptic [#elliptic] Configure alert webhook delivery to the DALP webhook URL in Elliptic. Elliptic documents alert webhooks in [Rescreening and Alerting](https://developers.elliptic.co/docs/rescreening-and-alerting) and API authentication in [Manual Integration](https://developers.elliptic.co/docs/authentication). * Use the DALP webhook URL as the destination. * Configure the same webhook signing secret you entered in DALP. * Elliptic sends `x-elliptic-signature`; DALP verifies the HMAC-SHA256 digest against the raw request body. ## After activation [#after-activation] An active integration can receive webhooks, but DALP still needs a subject mapping before a provider event can affect claims. * For Sumsub, create an applicant mapping for the DALP identity. * For Elliptic, register the wallet so DALP can resolve it to its on-chain identity. Follow [Map compliance-provider subjects](/docs/developer-guides/compliance/compliance-provider-subjects) for the API calls. ## Troubleshooting [#troubleshooting] * **Provisioning failed.** Use **Retry provisioning** on the integration detail page. The retry picks up existing provisioning work and only retries steps that did not land. * **Webhook signature rejected.** Confirm the provider dashboard's signing secret matches the one entered in DALP. Confirm the provider sends the expected signature header. * **Webhook accepted but no claim appeared.** Confirm the subject was mapped before the webhook was sent. If the subject was unmapped at delivery time, the event is retained for audit without an on-chain claim effect; map the subject before the next provider event. * **Monitoring tab is empty.** Monitoring rows appear only after Sumsub applicant-on-hold events or Elliptic wallet alerts arrive. Use the Identity view for issued or revoked claims from Sumsub applicant-review verdicts. * **Need to rotate the webhook signing secret.** Use **Rotate signing secret** on the integration detail page to stage the new secret. Update the provider dashboard to use the new secret. Promote the secret before the grace window expires. The existing active secret remains valid until promotion. ## See also [#see-also] * [Choose a KYC issuance path](/docs/developer-guides/compliance/choose-kyc-issuance-path) * [Map compliance-provider subjects](/docs/developer-guides/compliance/compliance-provider-subjects) * [Compliance provider API reference](/docs/developer-guides/compliance/compliance-provider-api-reference) * [How compliance provider intake works](/docs/architecture/integrations/compliance-providers) # Smart identity verification Source: https://docs.settlemint.com/docs/developer-guides/compliance/smart-identity-verification Configure the identity verification compliance module to require verified OnchainID claims for all asset transfers. Build logical expressions combining multiple claim requirements. This guide explains how to configure the identity verification compliance module when creating an asset via API. This module ensures both sender and recipient wallets have verified OnchainID claims before any transfer can occur. On-chain identity registry for market participants For the web interface approach, see the [compliance overview](/docs/user-guides/compliance/overview). ## Prerequisites [#prerequisites] * Platform URL (e.g., `https://your-platform.example.com`) * API key from a user with the **Token Manager** role (see [Getting Started](/docs/developer-guides/api-integration/getting-started) for API key setup) * Token factory for your desired asset type must be deployed * Wallet verification method enabled on your account (pincode or 2FA) ## How identity verification works [#how-identity-verification-works] When you enable the identity verification compliance module on an asset, the blockchain validates every transfer by checking: 1. **Sender verification** - The sending wallet must have an OnchainID with the required claim(s) 2. **Recipient verification** - The receiving wallet must have an OnchainID with the required claim(s) 3. **Trusted issuer** - The claim must be issued by an issuer in the asset's trusted issuer registry If any check fails, the transfer is rejected on-chain. When minting new units, the recipient wallet must also pass identity verification. Only wallets with valid claims can receive newly minted assets. ## Configuration parameters [#configuration-parameters] When configuring the identity verification module during asset creation, you set these parameters: | Parameter | Type | Description | | --------- | ------ | ----------------------------------------------------------------- | | `typeId` | string | Must be `"identity-verification"` | | `module` | string | Identity verification module contract address (from system query) | | `values` | array | Expression nodes defining the required claim logic | ### Expression nodes [#expression-nodes] The `values` array contains expression nodes that define which claims are required. Each node has two fields: | Field | Type | Description | | ---------- | ------ | ------------------------------------------------------------------ | | `nodeType` | number | Operation type: `0` = TOPIC, `1` = AND, `2` = OR, `3` = NOT | | `value` | string | For TOPIC nodes: the claim topic ID (BigInt). For operators: `"0"` | Expressions use **infix notation** in the API - topics and operators are listed in reading order. For example, "KYC AND AML" is expressed as: `[KYC_TOPIC, AML_TOPIC, AND_OPERATOR]`. The system converts this to postfix notation for on-chain evaluation. ## Configuring identity verification during asset creation [#configuring-identity-verification-during-asset-creation] ### Get identity verification module address [#get-identity-verification-module-address] Query the system to get the registered identity verification compliance module address: ```bash curl -X GET "https://your-platform.example.com/api/system/default" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response:** ```json { "complianceModuleRegistry": { "complianceModules": [ { "typeId": "identity-verification", "module": "0x7a2088a1bFc9d81c55368AE168C2C02570cB814F", "name": "SMART Identity Verification Module" } // ... other modules ] } } ``` Save the `module` address for the `typeId: "identity-verification"` entry. ### Choose claim topic IDs [#choose-claim-topic-ids] Select the claim topics you want to require. Each topic has a unique ID computed as `keccak256(topicName)`: **Investor-level claims:** | Topic | Topic ID (BigInt) | | -------------------------------- | -------------------------------------------------------------------------------- | | `knowYourCustomer` | `26984799302505749158794800959285050858086405868089409909048783980951278841746` | | `antiMoneyLaundering` | `66602700950116947137359654609674923607788815289970476259958745144319039408766` | | `qualifiedInstitutionalInvestor` | `39526553109170329799339511574661256630735485618560740361645615581310848276505` | | `professionalInvestor` | `82180795564044264154236077867753775185787185513502198122023755895636360079450` | | `accreditedInvestor` | `15733030998618876990024220391915773205162379317494393310546829862321881862123` | | `accreditedInvestorVerified` | `59850607985445873266538496619638148123529590973767384129725397368921421733377` | | `regulationS` | `110498984792486559366779193967170177172527553783122289566437277521370918941833` | **Issuer-level claims:** | Topic | Topic ID (BigInt) | | -------------------------- | -------------------------------------------------------------------------------- | | `issuerProspectusFiled` | `66024707054635888688268119510009865452443315877106845614779928773130009296293` | | `issuerProspectusExempt` | `108155241138976356337931447616840292633551902984574900198960811245767215772826` | | `issuerLicensed` | `4159646717597184831767716799371953881543937679636133282035549426319534456864` | | `issuerReportingCompliant` | `63974049055435959798075765549996859356159125666706735332156257908409531829710` | | `issuerJurisdiction` | `22945659622044541592208660721714212954694094425401272022345597464077438219436` | **Asset-level claims:** | Topic | Topic ID (BigInt) | | --------------------- | -------------------------------------------------------------------------------- | | `collateral` | `56591694316807385155654796962642700009023257328234168678289712861780104020528` | | `uniqueIdentifier` | `114741468009595078276449793420639789199829227967433767091939592755701866581418` | | `assetClassification` | `24450086974696203741514129497527543680077858637075869492686262873957363087146` | | `basePrice` | `57654513796690178928725925529203455242162418455779606444866766669020006213509` | | `assetIssuer` | `59193974906477606357781697605998854609236670153591542535032894715512537823578` | | `assetLocation` | `8432405482680553454773023573904831382358253771058496665012951440405906006611` | **General claims:** | Topic | Topic ID (BigInt) | | ------------------ | ------------------------------------------------------------------------------- | | `contractIdentity` | `43841672744129833047570617593608746101743408532017342577134968204499777399068` | | `custom` | `4204500278131556441620930517354114011833808539324421779944429921463295225973` | ### Create asset with identity verification module [#create-asset-with-identity-verification-module] Include the identity verification module in the `initialModulePairs` array when creating your asset. **Example: Require KYC verification** ```bash curl -X POST "https://your-platform.example.com/api/token/create" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "equity", "name": "KYC Protected Equity", "symbol": "KYCE", "decimals": 18, "countryCode": 840, "priceCurrency": "USD", "basePrice": "100", "initialModulePairs": [ { "typeId": "identity-verification", "module": "0x7a2088a1bFc9d81c55368AE168C2C02570cB814F", "values": [ { "nodeType": 0, "value": "26984799302505749158794800959285050858086405868089409909048783980951278841746" } ] } ], "walletVerification": { "secretVerificationCode": "YOUR_PINCODE", "verificationType": "PINCODE" } }' ``` **Response:** ```json { "id": "0x1234567890AbCdEf1234567890AbCdEf12345678", "type": "equity", "createdAt": "2025-01-15T10:30:00.000Z", "name": "KYC Protected Equity", "symbol": "KYCE", "decimals": 18, "basePrice": "[\"100\",0]", "basePriceCurrencyCode": "USD", "totalSupply": "[\"0\",0]", "pausable": { "paused": true } } ``` The response includes the full asset object with additional fields like `identity`, `accessControl`, `complianceModuleConfigs`, `userPermissions`, and `stats`. Note that the asset starts paused by default. **Key fields in `initialModulePairs`:** | Field | Description | | ------------------- | ---------------------------------------------------------- | | `typeId` | Must be `"identity-verification"` for this module | | `module` | The identity verification module address from step 1 | | `values` | Array of expression nodes defining required claims | | `values[].nodeType` | `0` for claim topics, `1` for AND, `2` for OR, `3` for NOT | | `values[].value` | Topic ID (BigInt string) for topics, `"0"` for operators | ### Verify configuration [#verify-configuration] Query the asset to confirm the identity verification module is configured: ```bash curl -X GET "https://your-platform.example.com/api/token/0x1234567890AbCdEf1234567890AbCdEf12345678" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response (relevant fields):** ```json { "id": "0x1234567890AbCdEf1234567890AbCdEf12345678", "type": "equity", "name": "KYC Protected Equity", "symbol": "KYCE", "complianceModuleConfigs": [ { "complianceModule": { "typeId": "identity-verification", "module": "0x7a2088a1bFc9d81c55368AE168C2C02570cB814F" } } ] } ``` Confirm that `complianceModuleConfigs` contains an entry with `typeId: "identity-verification"`. ## Expression examples [#expression-examples] ### Single requirement (KYC only) [#single-requirement-kyc-only] Require only the `knowYourCustomer` claim: ```json { "values": [ { "nodeType": 0, "value": "26984799302505749158794800959285050858086405868089409909048783980951278841746" } ] } ``` ### Combined requirements (KYC AND AML) [#combined-requirements-kyc-and-aml] Require both `knowYourCustomer` AND `antiMoneyLaundering` claims: ```json { "values": [ { "nodeType": 0, "value": "26984799302505749158794800959285050858086405868089409909048783980951278841746" }, { "nodeType": 0, "value": "66602700950116947137359654609674923607788815289970476259958745144319039408766" }, { "nodeType": 1, "value": "0" } ] } ``` ### Alternative requirements (KYC OR accreditedInvestor) [#alternative-requirements-kyc-or-accreditedinvestor] Require either `knowYourCustomer` OR `accreditedInvestor` claim: ```json { "values": [ { "nodeType": 0, "value": "26984799302505749158794800959285050858086405868089409909048783980951278841746" }, { "nodeType": 0, "value": "15733030998618876990024220391915773205162379317494393310546829862321881862123" }, { "nodeType": 2, "value": "0" } ] } ``` ### Complex expression with grouping [#complex-expression-with-grouping] Require `(KYC AND AML) OR qualifiedInstitutionalInvestor`: ```json { "values": [ "(", { "nodeType": 0, "value": "26984799302505749158794800959285050858086405868089409909048783980951278841746" }, { "nodeType": 0, "value": "66602700950116947137359654609674923607788815289970476259958745144319039408766" }, { "nodeType": 1, "value": "0" }, ")", { "nodeType": 0, "value": "39526553109170329799339511574661256630735485618560740361645615581310848276505" }, { "nodeType": 2, "value": "0" } ] } ``` When building expressions without parentheses, operators follow this precedence (highest to lowest): NOT (3) → AND (2) → OR (1). Use parentheses `"("` and `")"` as array elements to explicitly group operations. ## Request parameters [#request-parameters] ### Identity verification module configuration [#identity-verification-module-configuration] | Parameter | Type | Required | Description | | ------------------- | ------ | -------- | ----------------------------------------------------------------- | | `typeId` | string | Yes | Must be `"identity-verification"` | | `module` | string | Yes | Module contract address (from system query) | | `values` | array | Yes | Expression nodes array (topics, operators, and optional grouping) | | `values[].nodeType` | number | Yes | `0` = TOPIC, `1` = AND, `2` = OR, `3` = NOT | | `values[].value` | string | Yes | Topic ID for TOPIC nodes, `"0"` for operators | ### Wallet verification object [#wallet-verification-object] | Field | Type | Description | | ------------------------ | ------ | --------------------------------------------------- | | `secretVerificationCode` | string | 6-digit pincode or TOTP code | | `verificationType` | string | `"PINCODE"` (default), `"SECRET_CODES"`, or `"OTP"` | ## Best practices [#best-practices] ### Claim selection [#claim-selection] * Start with `knowYourCustomer` for basic regulatory compliance * Add `antiMoneyLaundering` for enhanced due diligence requirements * Use `accreditedInvestor` or `qualifiedInstitutionalInvestor` for securities with investor restrictions * Combine claims with AND for stricter requirements, OR for flexibility ### Expression design [#expression-design] * Keep expressions simple when possible - a single topic is often sufficient * Test complex expressions thoroughly before deploying to production * Document your expression logic for compliance audits * Consider using OR with accreditation claims to support different investor types ### Trusted issuers [#trusted-issuers] * Ensure claim issuers are registered in the trusted issuer registry * Verify issuer credentials before adding them to the registry * Regularly audit trusted issuer permissions ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------- | ---------------------------------------------------------------------------- | | `401 Unauthorized` | API key is invalid, expired, or disabled | | `403 USER_NOT_AUTHORIZED` | Ensure your account has `tokenManager` role | | `Module not found` | Query `/system/default` to get the correct module address | | `Invalid expression` | Verify expression nodes follow the correct format with valid nodeType values | | `Invalid topic ID` | Verify topic ID matches the keccak256 hash of the claim topic name | ## Related guides [#related-guides] * [Verify KYC](/docs/developer-guides/compliance/verify-kyc) - Issue KYC verifications to users * [Configure Trusted Issuers](/docs/developer-guides/compliance/configure-trusted-issuers) - Set up issuer permissions # Verify KYC Source: https://docs.settlemint.com/docs/developer-guides/compliance/verify-kyc Issue KYC verifications to registered users via API This guide explains how to issue KYC (Know Your Customer) verifications for registered users via API, enabling them to receive assets and participate in compliant transactions. For the web interface approach, see the [user guide](/docs/user-guides/compliance/verify-kyc). ## Prerequisites [#prerequisites] * Platform URL (e.g., `https://your-platform.example.com`) * API key from a user with the **Claim Issuer** (`claimIssuer`) system role (see [Getting Started](/docs/developer-guides/api-integration/getting-started) for API key setup) * Wallet verification method enabled on your account (e.g., pincode or 2FA) * User must be [registered](/docs/developer-guides/user-management/register-user) first * You must be configured as a [trusted issuer](/docs/developer-guides/compliance/configure-trusted-issuers) for the KYC topic ## About KYC verifications [#about-kyc-verifications] KYC verifications are on-chain attestations that confirm: * User identity has been verified * Documentation meets compliance standards * User is eligible for asset transactions * Verification is issued by a trusted entity ### When KYC is required [#when-kyc-is-required] KYC verification is **not automatically required** for all users. It becomes necessary when: **Global requirement:** The platform's Identity verification compliance module is configured to require KYC verification for all asset transactions. This is recommended as a default security measure. **Per-asset requirement:** Individual assets can enable the Identity verification compliance module with KYC as a required verification topic, even if not required globally. **No requirement:** Some use cases may not require KYC verification at all, depending on the asset type and regulatory context. For complete information about verification topics and framework, see [Compliance Overview](/docs/user-guides/compliance/overview). ## Issuing KYC verifications [#issuing-kyc-verifications] ### Identify user to verify [#identify-user-to-verify] You need the user's wallet address and userId to issue verification. If you don't have them, search by email or name: ```bash curl -X GET "https://your-platform.example.com/api/user/search?query=investor@example.com" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response:** ```json [ { "id": "usr_abc123", "name": "John Investor", "wallet": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "role": "member" } ] ``` Save both the `id` (userId) and `wallet` address for subsequent steps. ### List available verification topics [#list-available-verification-topics] Query the available claim topics to identify the KYC topic: ```bash curl -X GET "https://your-platform.example.com/api/system/claim-topics" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response:** ```json [ { "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7...", "topicId": "26984799302505749158794800959285050858086405868089409909048783980951278841746", "name": "knowYourCustomer", "signature": "string claim", "registry": { "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7" } }, { "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7...", "topicId": "15733030998618876990024220391915773205162379317494393310546829862321881862123", "name": "accreditedInvestor", "signature": "string claim", "registry": { "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7" } }, { "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7...", "topicId": "39526553109170329799339511574661256630735485618560740361645615581310848276505", "name": "qualifiedInstitutionalInvestor", "signature": "string claim", "registry": { "id": "0x534b8f03c16c92c70d1da1d2fae43b98352bf3d7" } } // ... additional topics available ] ``` The KYC topic has `name: "knowYourCustomer"`. Use the `name` field when issuing the claim. Verification topic registry for KYC claim types ### Create KYC profile [#create-kyc-profile] If the user doesn't have a KYC profile yet, create one with their personal information: ```bash curl -X POST "https://your-platform.example.com/api/user/usr_abc123/kyc/upsert" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "userId": "usr_abc123", "firstName": "John", "lastName": "Investor", "dob": "1985-03-15T00:00:00.000Z", "country": "BE", "residencyStatus": "resident", "nationalId": "123456789" }' ``` **Response:** ```json { "changed": true, "currentVersion": { "id": "ver_xyz789", "number": 1, "contentHash": "1e1329fc9216a119e9c596084cd353949f0754ddfa53014760ae6cc7ef8d1d35", "createdAt": "2024-01-15T10:00:00.000Z" }, "profile": { "id": "kyc_abc123", "userId": "usr_abc123", "firstName": "John", "lastName": "Investor", "dob": "1985-03-15T00:00:00.000Z", "country": "BE", "residencyStatus": "resident", "nationalId": "123456789", "createdAt": "2024-01-15T10:00:00.000Z", "updatedAt": "2024-01-15T10:00:00.000Z" } } ``` Save the `contentHash` from `currentVersion` - you'll use this as the claim value in the next steps. **Required fields:** * `userId` - User ID from step 1 * At least one of: `firstName`, `lastName`, `dob`, `country`, `residencyStatus`, or `nationalId` **Field validation:** * `dob` - User must be at least 18 years old * `country` - ISO 3166-1 alpha-2 country code (e.g., "BE", "US", "DE") * `residencyStatus` - One of: `"resident"`, `"non_resident"`, `"dual_resident"`, `"unknown"` ### Get claim value [#get-claim-value] Different verification topics require different claim values. **For `knowYourCustomer` topic:** If you just created the KYC profile in the previous step, use the `contentHash` from the response. Otherwise, fetch the user's existing KYC content hash: ```bash curl -X GET "https://your-platform.example.com/api/user/usr_abc123/kyc/read" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response:** ```json { "id": "kyc_xyz789", "userId": "usr_abc123", "firstName": "John", "lastName": "Investor", "dob": "1985-03-15T00:00:00.000Z", "country": "BE", "residencyStatus": "resident", "nationalId": "123456789", "contentHash": "1e1329fc9216a119e9c596084cd353949f0754ddfa53014760ae6cc7ef8d1d35", "createdAt": "2024-01-15T10:00:00.000Z", "updatedAt": "2024-01-15T10:00:00.000Z" } ``` Use the `contentHash` value as the claim data (note: it's a hex string without the `0x` prefix). **For boolean verification topics (skip this API call, use "true"):** The following topics use the literal string `"true"` as the claim value: * `antiMoneyLaundering` * `accreditedInvestor` * `accreditedInvestorVerified` * `professionalInvestor` * `qualifiedInstitutionalInvestor` * `regulationS` ### Get user's identity address [#get-users-identity-address] Query the user's identity by wallet address to get their identity contract address: ```bash curl -X GET "https://your-platform.example.com/api/system/identity/by-wallet/0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response:** ```json { "id": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890", "account": { "id": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "contractName": null }, "isContract": false, "hasIdentity": true, "registered": { "isRegistered": true, "country": "BE" }, "claims": [] } ``` **Important:** Use the `id` field (identity CONTRACT address), not the `account.id` (wallet address). ### Issue KYC verification [#issue-kyc-verification] Issue the claim to the user's identity contract: ```bash curl -X POST "https://your-platform.example.com/api/system/identity/claim/issue" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "targetIdentityAddress": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890", "claim": { "topic": "knowYourCustomer", "data": { "claim": "1e1329fc9216a119e9c596084cd353949f0754ddfa53014760ae6cc7ef8d1d35" } }, "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` **Response:** ```json { "txHash": "0x8d95bfd5381478d90992d3e2e64c73178e46bb18592bfcbffdf899f2407aee9b", "success": true, "claimTopic": "knowYourCustomer", "targetWallet": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890" } ``` The `targetWallet` field in the response contains the identity contract address (matching the `targetIdentityAddress` from the request), not the wallet address. ### Verify completion [#verify-completion] Query the identity again to confirm the claim was issued: ```bash curl -X GET "https://your-platform.example.com/api/system/identity/by-wallet/0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response when verified:** ```json { "id": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890", "account": { "id": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "contractName": null }, "isContract": false, "hasIdentity": true, "registered": { "isRegistered": true, "country": "BE" }, "claims": [ { "id": "0xc057fb9c66650cfaf24192baa697f463b0ddf81eb8b764c460cd1841967742a63551c161fda0528a93030bdcc6efb139f05b3a912053c839655f0147e7370e77bff1df5c5b637964", "name": "knowYourCustomer", "signature": "0xfe5b3c723b68a482c5cfd4fc38c384a217e748b5375340d2b05e8684f5d0558c0eabb331636cb5182d424e478cd6ba3db6f6efca452b570e6d6516d1b8bd24cf1b", "revoked": false, "issuer": { "id": "0xD3c16123446a6fe39635adD185574e7c6DC617Fe" }, "values": [ { "key": "claim", "value": "1e1329fc9216a119e9c596084cd353949f0754ddfa53014760ae6cc7ef8d1d35" } ], "isTrusted": true } ] } ``` **Key fields to verify:** * `claims` array contains the new KYC claim * `claims[].name` = `"knowYourCustomer"` (the claim topic) * `claims[].signature` = cryptographic signature proving the claim was signed by the issuer * `claims[].values` = array with the contentHash claim data * `claims[].issuer.id` = identity address of the claim issuer * `claims[].isTrusted` = `true` (confirms you're a trusted issuer for this topic) * `claims[].revoked` = `false` (claim is active) The user can now receive assets requiring KYC verification. User identity with verification status ## Request parameters [#request-parameters] ### User search [#user-search] | Parameter | Type | Required | Description | | --------- | ------ | -------- | ----------------------------------- | | `query` | string | Yes | Email, name, or wallet to search by | ### Claim issue [#claim-issue] | Parameter | Type | Required | Description | | ----------------------- | ------ | -------- | -------------------------------------------------------- | | `targetIdentityAddress` | string | Yes | Identity contract address (0x...) from identity lookup | | `claim.topic` | string | Yes | Claim topic name (e.g., `"knowYourCustomer"`) | | `claim.data.claim` | string | Yes | Claim value (contentHash for KYC, `"true"` for booleans) | | `walletVerification` | object | Yes | Your wallet verification (pincode or totp) | The `targetIdentityAddress` must be the identity contract address (for example `identity.id` from the identity lookup). Do not send the user's wallet address. ### Wallet verification object [#wallet-verification-object] | Field | Type | Description | | ------------------------ | ------ | ---------------------------------------------- | | `secretVerificationCode` | string | 6-digit pincode or TOTP code | | `verificationType` | string | "PINCODE" (default), "SECRET\_CODES", or "OTP" | ### Claim issue response fields [#claim-issue-response-fields] | Field | Type | Description | | -------------- | ------ | ------------------------------------------------- | | `txHash` | string | Transaction hash for the claim issuance | | `success` | bool | Whether the claim issuance succeeded | | `claimTopic` | string | Topic name that was issued | | `targetWallet` | string | Identity contract address that received the claim | ## Common claim topics [#common-claim-topics] | Topic ID | Name | Claim Value | Description | | -------- | -------------------------------- | --------------- | --------------------------------- | | 1 | `knowYourCustomer` | KYC contentHash | Basic identity verification | | 2 | `accreditedInvestor` | `"true"` | US qualified investor status | | 3 | `qualifiedInstitutionalInvestor` | `"true"` | EU institutional investor rules | | 4 | `antiMoneyLaundering` | `"true"` | Source of funds verification | | 5 | `professionalInvestor` | `"true"` | MiFID professional classification | | 6 | `accreditedInvestorVerified` | `"true"` | Verified accredited investor | | 7 | `regulationS` | `"true"` | Regulation S compliance | ## Best practices [#best-practices] ### Verification standards [#verification-standards] * Follow your written KYC policy consistently * Maintain evidence of verification decisions * Document verification rationale for audit * Use consistent verification data formats ### Data privacy [#data-privacy] * Store minimal personal data on-chain * Use hashes for sensitive information references * Maintain secure off-chain document storage * Follow applicable data protection regulations ### Verification quality [#verification-quality] * Verify document authenticity * Cross-check information sources * Monitor for suspicious patterns * Maintain verification standards ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------- | -------------------------------------------------------------------------------------------------------- | | `401 Unauthorized` | API key is invalid, expired, or disabled | | `403 USER_NOT_AUTHORIZED` | Ensure your account has `claimIssuer` system role | | `Not a trusted issuer` | Configure yourself as trusted issuer for KYC topic first | | `Identity not found` | User must be registered first; see [Register User](/docs/developer-guides/user-management/register-user) | | `Claim already exists` | User already has this verification | | `No KYC data` | User must have a KYC profile; see [Create KYC profile](#create-kyc-profile) step above | ## Related guides [#related-guides] * [Register User](/docs/developer-guides/user-management/register-user) - Register users before verification * [Create Users](/docs/developer-guides/user-management/create-users) - Create user accounts with wallet and identity * [Configure Trusted Issuers](/docs/user-guides/compliance/configure-trusted-issuers) - Set up verification permissions * [Compliance Overview](/docs/user-guides/compliance/overview) - Complete compliance reference * [Verify KYC (User Guide)](/docs/user-guides/compliance/verify-kyc) - Web interface approach # Create feeds Source: https://docs.settlemint.com/docs/developer-guides/feeds/create-feeds Deploy issuer-signed scalar feeds via the factory, create aggregator adapters for stable consumer addresses, and register external feeds in the directory. ## Create an issuer-signed scalar feed [#create-an-issuer-signed-scalar-feed] Issuer-signed feeds are deployed via the `IssuerSignedScalarFeedFactory` addon. Updates are authorized through EIP-712 typed data signatures from trusted issuers. ### Prerequisites [#prerequisites] Verify the factory is installed: ```bash curl "$DALP_API_URL/system/feeds/capabilities" \ -H "X-Api-Key: $API_KEY" ``` Check that `issuerSignedScalarFeedFactory.installed` is `true` in the response. DALP prepares the shared price topic and issuer-signed scalar feed factory during system and organization deployment. If the capability is not installed yet, finish the platform setup or deployment flow first, then rerun the capabilities check before creating issuer-signed feeds. The deployment flow is safe to rerun. When the price topic or feed factory already exists, DALP reuses the existing registration instead of creating a duplicate. On a fresh deployment, DALP resolves the new factory from the registry confirmation first. If that receipt-based resolution does not return the factory address, DALP waits for the platform's indexed contract view and resolves the factory there as a fallback. Deterministic configuration or authorization failures fail the setup step; transient network, RPC, or indexing delays are retried by the deployment workflow. ### Register a topic [#register-a-topic] Before creating a feed, register the topic name in the TopicSchemeRegistry. The API accepts either a human-readable `topicName` string, a `topicId` hash, or both. When `topicName` is provided, the on-chain `topicId` is computed internally. When both are provided, the API verifies they match. ### Deploy the feed [#deploy-the-feed] ```bash curl -X POST "$DALP_API_URL/system/feeds/issuer-signed/create" \ -H "X-Api-Key: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "subject": "0xTokenAddress", "topicName": "PRICE/USD", "decimals": 8, "description": "USD price feed for token", "historyMode": "BOUNDED", "historySize": 100, "requirePositive": true, "driftAllowance": 0 }' ``` You can also use `topicId` instead of `topicName`: ```bash curl -X POST "$DALP_API_URL/system/feeds/issuer-signed/create" \ -H "X-Api-Key: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "subject": "0xTokenAddress", "topicId": "109530253152918849413157129862927938307097811649912148858507112538040816216783", "decimals": 8, "description": "USD price feed for token", "historyMode": "BOUNDED", "historySize": 100, "requirePositive": true, "driftAllowance": 0 }' ``` #### Parameters [#parameters] | Parameter | Type | Required | Description | | -------------------- | ------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------- | | `subject` | address | Yes | The asset or entity this feed relates to | | `topicName` | string | At least one of `topicName` or `topicId` | Human-readable topic name (e.g., "PRICE/USD"). Must be registered in the TopicSchemeRegistry first. | | `topicId` | string | At least one of `topicName` or `topicId` | The keccak256 hash of the topic name, as a decimal string | | `decimals` | number | Yes | Decimal precision for feed values (0--18) | | `description` | string | Yes | Human-readable description of the feed | | `historyMode` | enum | Yes | `LATEST_ONLY`, `BOUNDED`, or `FULL` | | `historySize` | number | Yes | Ring buffer size when using `BOUNDED` mode (ignored for other modes) | | `requirePositive` | boolean | Yes | Reject updates with non-positive values | | `driftAllowance` | number | Yes | Maximum allowed drift in seconds between `observedAt` and block timestamp | | `walletVerification` | object | No (optional for API key auth) | Wallet verification for transaction signing: required for session-based auth only | Response (synchronous, default): ```json { "data": { "feedAddress": "0x...", "subject": "0x...", "topicId": "0x...", "decimals": 8, "historyMode": "BOUNDED", "transactionHash": "0x..." }, "meta": { "txHashes": ["0x..."] }, "links": { "self": "/system/feeds/issuer-signed/create" } } ``` To request asynchronous processing, add `Prefer: respond-async` to the request headers. The server returns HTTP 202 with a status URL for polling: ```json { "transactionId": "01965a1b-...", "status": "pending", "statusUrl": "/transactions/01965a1b-..." } ``` Poll the `statusUrl` until `status` changes to `"completed"` or `"failed"`. The feed is automatically registered in the FeedsDirectory upon creation. ### Permissions [#permissions] Feed creation supports two authorization paths: 1. **Feeds Manager role** (system-level): can create feeds for any subject including global (address(0)). 2. **Governance role on subject token**: holders of the GOVERNANCE role on a specific DALP token can create feeds for that token, provided the token supports the ISMART and ISMARTTokenAccessManaged interfaces. Does not apply to global feeds. If neither condition is met, the factory reverts with `UnauthorizedFeedCreation`. | Error | Cause | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `UnauthorizedFeedCreation` | Caller lacks FEEDS\_MANAGER\_ROLE and does not hold GOVERNANCE\_ROLE on the subject token (or subject is address(0) and caller is not a feeds manager) | ## Create an aggregator adapter [#create-an-aggregator-adapter] Adapters are stable-address proxies that resolve to the current underlying feed for a `(subject, topic)` pair. Consumers point at the adapter address instead of the feed directly. When you replace the underlying feed, the adapter automatically resolves to the new feed without consumers changing their configuration. ### Prerequisites [#prerequisites-1] Verify the adapter factory is installed: ```bash curl "$DALP_API_URL/system/feeds/capabilities" \ -H "X-Api-Key: $API_KEY" ``` Check that `scalarFeedAggregatorAdapterFactory.installed` is `true`. ### Deploy the adapter [#deploy-the-adapter] ```bash curl -X POST "$DALP_API_URL/system/feeds/adapters/create" \ -H "X-Api-Key: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "subject": "0xTokenAddress", "topicName": "PRICE/USD" }' ``` | Parameter | Type | Required | Description | | -------------------- | ------- | ---------------------------------------- | --------------------------------------------------------------------------------- | | `subject` | address | Yes | The asset or entity | | `topicName` | string | At least one of `topicName` or `topicId` | Human-readable topic name | | `topicId` | string | At least one of `topicName` or `topicId` | The keccak256 hash of the topic name | | `walletVerification` | object | No (optional for API key auth) | Wallet verification for transaction signing: required for session-based auth only | Response (synchronous, default): ```json { "data": { "adapterAddress": "0x...", "subject": "0x...", "topicId": "0x...", "transactionHash": "0x..." }, "meta": { "txHashes": ["0x..."] }, "links": { "self": "/system/feeds/adapters/create" } } ``` Add `Prefer: respond-async` for asynchronous processing (HTTP 202 with `statusUrl` for polling). ### Permissions [#permissions-1] Adapter creation follows the same two-path authorization as feed creation: 1. **Feeds Manager role** (system-level): can create adapters for any subject including global (address(0)). 2. **Governance role on subject token**: can create adapters for their specific token. Does not apply to global subjects. | Error | Cause | | -------------------------- | ----------------------------------------------------------------------------------------- | | `UnauthorizedFeedCreation` | Caller lacks FEEDS\_MANAGER\_ROLE and does not hold GOVERNANCE\_ROLE on the subject token | ## Register an external feed [#register-an-external-feed] Register an existing feed contract (e.g., a Chainlink price feed) in the directory so it can be discovered through the same `(subject, topic)` lookup as platform-managed feeds. ```bash curl -X POST "$DALP_API_URL/system/feeds/register-external" \ -H "X-Api-Key: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "subject": "0xTokenAddress", "topicName": "PRICE/USD", "feedAddress": "0xExternalFeedContract", "kind": "SCALAR" }' ``` | Parameter | Type | Required | Description | | -------------------- | ------- | ---------------------------------------- | --------------------------------------------------------------------------------- | | `subject` | address | Yes | The asset or entity | | `topicName` | string | At least one of `topicName` or `topicId` | Human-readable topic name | | `topicId` | string | At least one of `topicName` or `topicId` | The keccak256 hash of the topic name | | `feedAddress` | address | Yes | The external feed contract address | | `kind` | enum | Yes | Feed kind (`SCALAR`) | | `walletVerification` | object | No (optional for API key auth) | Wallet verification for transaction signing: required for session-based auth only | ## Directory operations [#directory-operations] ### Resolve a feed [#resolve-a-feed] Look up the feed registered for a `(subject, topic)` pair. You can use either `topicName` or `topicId`: ```bash curl "$DALP_API_URL/system/feeds/resolve?subject=0xToken&topicName=PRICE/USD" \ -H "X-Api-Key: $API_KEY" ``` Or with `topicId`: ```bash curl "$DALP_API_URL/system/feeds/resolve?subject=0xToken&topicId=109530253..." \ -H "X-Api-Key: $API_KEY" ``` Returns the feed address, kind, schema hash, adapter address (if one exists), and indexed data from DIDX. ### Replace a feed [#replace-a-feed] Swap the feed registered for a `(subject, topic)` pair with a different feed contract: ```bash curl -X PUT "$DALP_API_URL/system/feeds/replace" \ -H "X-Api-Key: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "subject": "0xTokenAddress", "topicName": "PRICE/USD", "newFeedAddress": "0xNewFeedContract", "kind": "SCALAR" }' ``` | Parameter | Type | Required | Description | | -------------------- | ------- | ---------------------------------------- | --------------------------------------------------------------------------------- | | `subject` | address | Yes | The asset or entity | | `topicName` | string | At least one of `topicName` or `topicId` | Human-readable topic name | | `topicId` | string | At least one of `topicName` or `topicId` | The keccak256 hash of the topic name | | `newFeedAddress` | address | Yes | The new feed contract address | | `kind` | enum | Yes | Feed kind (`SCALAR`) | | `walletVerification` | object | No (optional for API key auth) | Wallet verification for transaction signing: required for session-based auth only | If an aggregator adapter exists for this `(subject, topic)`, it automatically resolves to the new feed. ### Remove a feed [#remove-a-feed] Remove a feed registration from the directory: ```bash curl -X DELETE "$DALP_API_URL/system/feeds/remove" \ -H "X-Api-Key: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "subject": "0xTokenAddress", "topicName": "PRICE/USD" }' ``` | Parameter | Type | Required | Description | | -------------------- | ------- | ---------------------------------------- | --------------------------------------------------------------------------------- | | `subject` | address | Yes | The asset or entity | | `topicName` | string | At least one of `topicName` or `topicId` | Human-readable topic name | | `topicId` | string | At least one of `topicName` or `topicId` | The keccak256 hash of the topic name | | `walletVerification` | object | No (optional for API key auth) | Wallet verification for transaction signing: required for session-based auth only | ## Related [#related] * [Data feeds overview](/docs/developer-guides/feeds/overview) * [Submit feed updates](/docs/developer-guides/feeds/submit-updates) # Feeds overview Source: https://docs.settlemint.com/docs/developer-guides/feeds/overview On-chain data feeds registered in a system-level FeedsDirectory. Use feeds for price reporting, NAV tracking, interest rates, and custom oracle data. Feeds are on-chain data sources registered in a system-level **FeedsDirectory**. Each feed is identified by a `(subject, topicName)` pair, where the subject is the asset or entity the data relates to, and the topicName is a human-readable string identifying the data type (e.g., "NAV", "PRICE/USD"). The API computes the on-chain `topicId` (`bytes32` keccak256 hash) from the topic name internally. Feeds can be reviewed from platform settings and managed through the feed APIs. ## Use cases [#use-cases] * **Price feeds** -- publish token prices for collateral valuation or trading * **NAV reporting** -- report net asset value for fund tokens on a schedule * **Interest rates** -- publish benchmark rates for variable-yield instruments * **Custom oracle data** -- any scalar value that needs to be available on-chain ## Key concepts [#key-concepts] | Concept | Description | | ------------------ | -------------------------------------------------------------------------------------------------------------------------- | | **FeedsDirectory** | System-level registry mapping `(subject, topicId)` to a feed contract address | | **Subject** | The Ethereum address of the asset or entity the feed relates to | | **TopicName** | A human-readable string for the data type (e.g., "PRICE/USD"). The API computes the on-chain `topicId` hash automatically. | | **FeedKind** | The type of data the feed provides. Currently only `SCALAR` (a single numeric value) | | **Round** | A single data point submission, identified by an incrementing `roundId` | | **HistoryMode** | How the feed stores historical rounds (see below) | | **Adapter** | A stable-address proxy that resolves to the current underlying feed for a `(subject, topicName)` pair | ## Feed types [#feed-types] **Issuer-Signed Scalar Feed** -- deployed via the `IssuerSignedScalarFeedFactory` addon. The platform manages signing and submission. Updates are authorized via EIP-712 typed data signatures from trusted issuers. **External feeds** -- any existing feed contract (e.g., Chainlink) can be registered in the directory manually. The directory only stores the address mapping; it does not manage the external feed's lifecycle. ## History modes [#history-modes] | Mode | On-chain value | Behavior | | ------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------- | | `LATEST_ONLY` | 0 | Only the most recent round is stored. Previous values are overwritten. | | `BOUNDED` | 1 | A fixed-size ring buffer of rounds. Oldest rounds are evicted when the buffer is full. Configure the size with `historySize`. | | `FULL` | 2 | All rounds are stored permanently. Storage grows without bound. | Choose `LATEST_ONLY` for feeds where only the current value matters (e.g., real-time prices). Use `BOUNDED` when consumers need a sliding window of recent values. Use `FULL` for audit trails where every historical value must be preserved. ## Capabilities check [#capabilities-check] Before operating on feeds, verify which feed modules are installed on the platform: ```bash curl "$DALP_API_URL/system/feeds/capabilities" \ -H "X-Api-Key: $API_KEY" ``` The response indicates whether the FeedsDirectory, IssuerSignedScalarFeedFactory, and ScalarFeedAggregatorAdapterFactory are installed, along with their contract addresses and the supported feed kinds. ## Feed lifecycle [#feed-lifecycle] Asset analytics with data feed integration ## Next steps [#next-steps] Deploy issuer-signed feeds, aggregator adapters, and register external feeds. Submit signed feed updates with nonce management and staleness monitoring. Query latest values, historical rounds, and check feed staleness. # Read data Source: https://docs.settlemint.com/docs/developer-guides/feeds/read-data Query feed data: latest values, historical rounds, resolve feeds by subject and topic, list feeds with filtering, and check staleness. This guide covers reading data from feeds: querying the latest value, fetching historical rounds, resolving feeds by subject and topic, and listing feeds with filters. ## Latest value [#latest-value] Get the most recent round from a feed: ```bash curl "$DALP_API_URL/system/feeds/0xFeed/latest" \ -H "X-Api-Key: $API_KEY" ``` Response: ```json { "feedAddress": "0x...", "roundId": "42", "answer": "150000000", "decimals": 8, "formattedAnswer": "1.50000000", "startedAt": "1700000000", "updatedAt": "1700000000", "answeredInRound": "42", "description": "USD price feed", "version": "1" } ``` | Field | Description | | ----------------- | ----------------------------------------------- | | `roundId` | Incrementing identifier for this data point | | `answer` | Raw value scaled by `decimals` | | `formattedAnswer` | Human-readable value with decimal point applied | | `startedAt` | Timestamp when this round was initiated | | `updatedAt` | Timestamp when this round was last updated | | `description` | Feed description from the contract | | `version` | Feed contract version | ## Historical rounds [#historical-rounds] Fetch a specific round by its ID: ```bash curl "$DALP_API_URL/system/feeds/0xFeed/round/40" \ -H "X-Api-Key: $API_KEY" ``` Response: ```json { "roundId": "40", "answer": "148500000", "startedAt": "1699900000", "updatedAt": "1699900000", "answeredInRound": "40" } ``` Round availability depends on the feed's history mode. `LATEST_ONLY` feeds only have the current round. `BOUNDED` feeds keep a fixed window. `FULL` feeds retain all rounds. ## Resolve a feed [#resolve-a-feed] Look up a feed by its `(subject, topic)` pair. You can identify the topic by name or by ID: ```bash # By topic name curl "$DALP_API_URL/system/feeds/resolve?subject=0xToken&topicName=PRICE/USD" \ -H "X-Api-Key: $API_KEY" # By topic ID curl "$DALP_API_URL/system/feeds/resolve?subject=0xToken&topicId=109530253..." \ -H "X-Api-Key: $API_KEY" ``` | Parameter | Type | Required | Description | | ----------- | ------- | ---------------------------------------- | ------------------------------------ | | `subject` | address | Yes | The asset or entity address | | `topicName` | string | At least one of `topicName` or `topicId` | Human-readable topic name | | `topicId` | string | At least one of `topicName` or `topicId` | The keccak256 hash of the topic name | Response: ```json { "exists": true, "feedAddress": "0x...", "kind": "SCALAR", "schemaHash": "0x...", "adapterAddress": "0x...", "indexed": { "decimals": 8, "latestValue": { "roundId": 42, "answer": "150000000", "observedAt": "1700000000", "updatedAt": "1700000000", "issuer": "0x...", "signer": "0x..." }, "registeredAt": "1699000000" } } ``` The `indexed` field contains data from DIDX and may be `null` if the feed has not yet been indexed. In `latestValue`, `issuer` is the OnchainID identity contract that authorized the update, and `signer` is the EOA wallet that signed the EIP-712 typed data. ## List feeds [#list-feeds] Query all feeds with optional filters and pagination: ```bash curl "$DALP_API_URL/system/feeds?subject=0xToken&isActive=true&first=20&skip=0" \ -H "X-Api-Key: $API_KEY" ``` ### Filter parameters [#filter-parameters] | Parameter | Type | Description | | -------------- | ------- | --------------------------------------------------------------------------------------------- | | `subject` | address | Filter by the asset or entity address | | `kind` | enum | Filter by feed kind (`SCALAR`) | | `isActive` | boolean | Filter by active/inactive status | | `tokenAddress` | address | Filter by associated token address | | `first` | number | Page size (max 1000) | | `skip` | number | Number of items to skip | | `orderBy` | enum | Sort field: `id`, `feedAddress`, `subject`, `topicId`, `decimals`, `isActive`, `registeredAt` | Response: ```json { "items": [ { "id": "0x...", "feedAddress": "0x...", "subject": "0x...", "topicId": "0x...", "kind": "SCALAR", "schemaHash": "0x...", "decimals": 8, "token": { "id": "0x...", "name": "Token" }, "factory": { "id": "0x...", "typeId": "0x..." }, "creator": { "id": "0x..." }, "adapterAddress": "0x...", "isActive": true, "registeredAt": "1699000000", "latestValue": { "roundId": 42, "answer": "150000000", "observedAt": "1700000000", "updatedAt": "1700000000", "issuer": "0x...", "signer": "0x..." } } ], "totalCount": 1 } ``` ## List issuer-signed feeds [#list-issuer-signed-feeds] List feeds created through the IssuerSignedScalarFeedFactory: ```bash curl "$DALP_API_URL/system/feeds/issuer-signed?first=20&skip=0" \ -H "X-Api-Key: $API_KEY" ``` Response: ```json { "items": [ { "feedAddress": "0x...", "subject": "0x...", "topicId": "0x...", "creator": "0x..." } ], "totalCount": 1 } ``` ## List adapters [#list-adapters] List aggregator adapters created through the ScalarFeedAggregatorAdapterFactory: ```bash curl "$DALP_API_URL/system/feeds/adapters?first=20&skip=0" \ -H "X-Api-Key: $API_KEY" ``` Response: ```json { "items": [ { "adapterAddress": "0x...", "subject": "0x...", "topicId": "0x..." } ], "totalCount": 1 } ``` ## Check staleness [#check-staleness] Determine whether a feed's latest value is fresh enough: ```bash curl "$DALP_API_URL/system/feeds/0xFeed/staleness?maxAgeSeconds=3600" \ -H "X-Api-Key: $API_KEY" ``` See [Submit updates -- Check staleness](/docs/developer-guides/feeds/submit-updates#check-staleness) for the full response format and field descriptions. # Submit updates Source: https://docs.settlemint.com/docs/developer-guides/feeds/submit-updates Submit signed feed updates via EIP-712 typed data. Manage nonces, read feed configuration, and monitor staleness. This guide covers submitting new values to issuer-signed scalar feeds, managing nonces, and monitoring feed staleness. ## How issuer-signed updates work [#how-issuer-signed-updates-work] The platform signs an EIP-712 typed data message on behalf of the authenticated user, containing the new value, observation timestamp, and nonce. The signed message is submitted on-chain, where the feed contract verifies the signature against the user's OnchainID identity and the trusted issuers registry before storing the new round. ## Prerequisites [#prerequisites] Before submitting feed updates, the authenticated user must: 1. **Have a registered OnchainID identity** -- the platform derives the issuer identity contract from the authenticated user automatically 2. **Be a trusted issuer** -- the user's identity must be added as a trusted issuer for the feed's claim topic via the trusted issuers registry ## Get the current nonce [#get-the-current-nonce] Before submitting an update, retrieve the current nonce for the issuer's identity contract on the target feed: ```bash curl "$DALP_API_URL/system/feeds/0xFeed/nonce/0xIdentityContract" \ -H "X-Api-Key: $API_KEY" ``` The `issuerIdentity` path parameter is the OnchainID identity contract address, not the EOA wallet address. Nonces are tracked per identity contract on-chain. Response: ```json { "feedAddress": "0x...", "issuerIdentity": "0x...", "currentNonce": "5", "nextNonce": "6" } ``` Use `nextNonce` when constructing your submission. The feed contract rejects updates with a nonce that does not match the expected next value. ## Submit an update [#submit-an-update] ```bash curl -X POST "$DALP_API_URL/system/feeds/0xFeed/submit" \ -H "X-Api-Key: $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "value": "150000000", "observedAt": 1700000000, "deadline": 0 }' ``` The feed address is specified in the URL path. The issuer identity is derived automatically from the authenticated user's OnchainID identity contract. ### Parameters [#parameters] | Parameter | Type | Description | | -------------------- | ------ | -------------------------------------------------------------------------------------------------------------- | | `value` | string | The new value as a string (scaled by the feed's `decimals`) | | `observedAt` | number | Unix timestamp when the value was observed | | `deadline` | number | Optional. Unix timestamp after which the update is rejected. `0` means no deadline. | | `walletVerification` | object | Optional for API key auth. Wallet verification for transaction signing — required for session-based auth only. | Response: ```json { "transactionHash": "0x...", "feedAddress": "0x...", "value": "150000000", "nonce": "6" } ``` Values are encoded as strings scaled by the feed's decimal precision. For a feed with `decimals: 8`, a value of `1.50` is submitted as `"150000000"`. ## Submit flow [#submit-flow] ## Read feed configuration [#read-feed-configuration] Query the immutable configuration of an issuer-signed feed to understand its parameters: ```bash curl "$DALP_API_URL/system/feeds/0xFeed/config" \ -H "X-Api-Key: $API_KEY" ``` Response: ```json { "feedAddress": "0x...", "subject": "0x...", "topicId": "0x...", "expectedSchemaHash": "0x...", "decimals": 8, "description": "USD price feed", "historyMode": "BOUNDED", "historySize": 100, "requirePositive": true, "driftAllowance": 300, "domainSeparator": "0x...", "trustedIssuersRegistry": "0x...", "topicSchemeRegistry": "0x..." } ``` Key fields: | Field | Description | | ----------------- | -------------------------------------------------------- | | `decimals` | Decimal precision for scaling values | | `description` | Human-readable feed description | | `historyMode` | How historical rounds are stored | | `historySize` | Ring buffer size (for `BOUNDED` mode) | | `requirePositive` | Whether non-positive values are rejected | | `driftAllowance` | Maximum seconds between `observedAt` and block timestamp | ## Check staleness [#check-staleness] Determine whether a feed's latest value is fresh enough for a given use case: ```bash curl "$DALP_API_URL/system/feeds/0xFeed/staleness?maxAgeSeconds=3600" \ -H "X-Api-Key: $API_KEY" ``` Response: ```json { "feedAddress": "0x...", "latestUpdatedAt": "1700000000", "currentTimestamp": "1700003500", "ageSeconds": 3500, "maxAgeSeconds": 3600, "isStale": false, "roundId": "6", "answer": "150000000" } ``` | Field | Description | | --------------- | -------------------------------------- | | `ageSeconds` | Seconds since the last update | | `maxAgeSeconds` | The threshold you provided | | `isStale` | `true` if `ageSeconds > maxAgeSeconds` | Use staleness checks before consuming feed data in critical operations (e.g., collateral valuation, settlement pricing) to ensure the value is sufficiently recent. API request logs for tracking feed update submissions ## Related [#related] * [Data feeds overview](/docs/developer-guides/feeds/overview) * [Read data feeds](/docs/developer-guides/feeds/read-data) # Introduction Source: https://docs.settlemint.com/docs/developer-guides Build integrations with the Digital Asset Lifecycle Platform using the type-safe API. Create tokens, manage compliance, and automate asset operations programmatically. ## Overview [#overview] The following guides walk you through API authentication, token operations, user management, and compliance configuration. ## CLI [#cli] Install the CLI, authenticate, and run your first commands. Complete reference for all CLI commands across every domain. Use the CLI in shell scripts, CI/CD pipelines, and batch operations. Connect the CLI to Claude Code, Codex, and OpenCode as an MCP server. ## API integration [#api-integration] Create your first API key and configure the TypeScript client. Visual flowcharts for token creation, minting, transfers, and burns. Query holder balances and execute governed transfer workflows. Store named wallet recipients for safer transfer and servicing operations. Handle errors correctly with retry strategies and best practices. Configure pushed event delivery, payload privacy, retries, and audit proofs. List paymasters, check funding, manage sponsorship, and rotate signer keys. Format asset amounts correctly using decimal configuration. OpenAPI specification and type-safe client generation. ## Platform setup [#platform-setup] API-based platform setup and administrator role management. Initialize your platform with the first administrator account. Grant administrative permissions to users in your organization. Grant or revoke platform administrator roles. ## User management [#user-management] Create platform users with automatic wallet and identity setup. Register users in the identity registry for compliance tracking. ## Compliance [#compliance] Configure entities authorized to issue verifications. Issue KYC verifications to registered users. Configure identity verification compliance modules. Add a provider, provision a provider issuer EOA, and configure webhooks. Configure collateral ratios and backing requirements. ## Asset creation [#asset-creation] Deploy a new tokenized asset on the blockchain. ## Asset servicing [#asset-servicing] Grant or revoke administrator roles for specific assets. Issue new units to investors. Administratively move assets between wallets for compliance or recovery. Enable or disable all transfers for an asset. ## Feeds [#feeds] On-chain data feeds for price reporting, NAV tracking, and oracle data. Deploy issuer-signed feeds, aggregator adapters, and register external feeds. Submit signed feed updates with EIP-712 typed data and nonce management. Query latest values, historical rounds, and check feed staleness. ## Operations [#operations] Check transaction status after timeout errors. Monitor chain RPC and indexer health from the API or CLI. Recover blocked Restate workflows and clean stale deployments. Compare on-chain state with your internal records. ## Runbooks [#runbooks] Complete walkthrough for multi-organization equity tokenization. Create debt instruments with coupon schedules and maturity dates. Issue deposit certificates with interest accrual. Deploy equity tokens with dividend distribution. Launch fund tokens with NAV tracking. Create collateral-backed stablecoins. Add collateral requirements using the Compliance Module API. # Blockchain monitoring Source: https://docs.settlemint.com/docs/developer-guides/operations/blockchain-monitoring Monitor platform status from the DALP dapp and API, and inspect chain RPC and indexer health from the API or CLI. Use blockchain monitoring when you need detailed chain RPC and indexer diagnostics. The API exposes sync lag, block age, finality lag, stall time, reindex state, and recent service status for each monitored chain service. Use platform status when you need the operator dashboard rollup. The platform status API combines data freshness, transaction infrastructure, API request health, and workflow health into one snapshot and a trailing history view. This is operational data for a DALP environment. It helps you answer questions like: * Is an indexer behind the chain head? * Is a chain RPC endpoint stale or stalled? * Which networks have degraded or critical health? * Did a service recover after an incident? Blockchain monitoring endpoints require the global `admin` role or the organization `owner` role. API keys inherit the permissions of the user that created them. ## What DALP monitors [#what-dalp-monitors] DALP reports health for two service types: | Service type | What it tells you | | ------------ | ----------------------------------------------------------------------------------- | | `chain-rpc` | Whether DALP can read fresh blocks from the configured chain RPC endpoint. | | `indexer` | Whether the indexer is keeping up with chain head and serving current indexed data. | Health status values are: | Status | Meaning | | ------------ | -------------------------------------------------------------- | | `healthy` | The service is reporting normally. | | `degraded` | The service is lagging or stale enough to need attention. | | `critical` | The service is unhealthy for the monitored range. | | `unknown` | DALP does not have enough recent data to classify the service. | | `reindexing` | The indexer is rebuilding data for a chain. | ## Get the platform status rollup [#get-the-platform-status-rollup] Open Platform settings > Platform status in the dapp when you need the operator dashboard view. DALP shows the same snapshot and history data from the API to users with administrator or owner access. Use the platform status snapshot when you need a single environment-level health view for an operations dashboard. The snapshot returns a header verdict, four panel verdicts, and stat cards for the last 24 hours. ```bash curl -G "https://your-platform.example.com/api/v2/platform-status/snapshot" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` The response groups health into four panels: | Panel | What it summarizes | | -------------- | -------------------------------------------------------------------------- | | Data freshness | Latest indexer rollups, chains in sync, and recent sync errors. | | Transactions | Chain RPC service coverage. | | Platform API | Request volume and 4xx/5xx rates over the trailing 24 hours. | | Workflows | Completed and stalled workflow counts when workflow metrics are available. | The platform status verdict is `operational`, `degraded`, `outage`, or `no_data`. `no_data` means DALP has not collected enough rollup data yet, such as immediately after a fresh deployment. Use the history endpoint when you need the trailing daily severity grid shown in the dapp status page. The `days` query parameter accepts 1 to 90 days and defaults to 90. ```bash curl -G "https://your-platform.example.com/api/v2/platform-status/history" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ --data-urlencode "days=30" ``` ## Get a blockchain monitoring summary [#get-a-blockchain-monitoring-summary] Use the blockchain monitoring summary endpoint for the current state per monitored chain service. ```bash curl -G "https://your-platform.example.com/api/v2/blockchain-monitoring/health-metrics/summary" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ --data-urlencode "from=2026-05-01T00:00:00.000Z" \ --data-urlencode "to=2026-05-01T12:00:00.000Z" ``` The response contains one entry per monitored service: ```json { "services": [ { "serviceId": "01964f21-9f02-7000-9e8d-1e6d1fdc7f01", "serviceType": "indexer", "chainId": 11155111, "networkName": "Sepolia", "latestStatus": "healthy", "latestSampledAt": "2026-05-01T11:59:30.000Z", "snapshotCount": 48, "syncLag": 2, "blockHeight": 8154321, "blockAgeSeconds": null, "finalityLagBlocks": null, "stallSeconds": null, "recentLatencies": [124, 119, 131], "deploymentState": null } ] } ``` Use these fields like this: | Field | Use it for | | ------------------- | ------------------------------------------------------------------- | | `latestStatus` | The current classified health state. | | `syncLag` | Indexer lag in blocks. This is only set for `indexer` services. | | `blockAgeSeconds` | Chain RPC freshness. This is only set for `chain-rpc` services. | | `finalityLagBlocks` | Chain RPC finality lag. | | `stallSeconds` | How long the chain head has stopped advancing. | | `deploymentState` | Indexer version and reindex progress when an indexer is rebuilding. | Admins also see active indexer reindexes on the main dashboard. When an indexer's deployment state shows an active rebuild, the dashboard displays a reindex banner with the progress percentage, elapsed time, and a link to `/platform-settings/blockchain-monitoring`. If three or more indexers are actively reindexing, the banner switches to an aggregate count instead of listing every indexer. The banner is hidden for non-admin users and when there is no meaningful active reindex status to show. ## Check a timeline [#check-a-timeline] Use the timeline endpoint when you need chart data or want to see whether a problem is getting worse. ```bash curl -G "https://your-platform.example.com/api/v2/blockchain-monitoring/health-metrics/timeline" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ --data-urlencode "from=2026-04-24T00:00:00.000Z" \ --data-urlencode "to=2026-05-01T00:00:00.000Z" \ --data-urlencode "granularity=day" ``` The timeline accepts `hour` or `day` granularity. The queried range must be 31 days or less. ```json { "buckets": [ { "timestamp": "2026-04-30T00:00:00.000Z", "services": [ { "serviceKey": "indexer-11155111", "serviceType": "indexer", "chainId": 11155111, "networkName": "Sepolia", "healthyPct": 95.8, "degradedPct": 4.2, "criticalPct": 0 } ] } ] } ``` ## Inspect service health [#inspect-service-health] Use the service breakdown when you need one row per monitored service with aggregate percentages. ```bash curl -G "https://your-platform.example.com/api/v2/blockchain-monitoring/service-health-metrics" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ --data-urlencode "from=2026-05-01T00:00:00.000Z" \ --data-urlencode "to=2026-05-01T12:00:00.000Z" ``` The time range for summary and service breakdown calls must be 7 days or less. ## Inspect raw snapshots [#inspect-raw-snapshots] Use snapshots when you need the underlying health log. You can filter by service, status, service type, network name, and time range. ```bash curl -G "https://your-platform.example.com/api/v2/blockchain-monitoring/health-snapshots" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ --data-urlencode "status=degraded" \ --data-urlencode "serviceType=indexer" \ --data-urlencode "limit=20" ``` Each snapshot includes the sampled time, service type, chain ID, network name, status, block metrics, and collector latency. Responses include `nextCursor` for pagination. ```json { "items": [ { "id": "01964f32-4cc1-7000-a05e-88a188d62921", "serviceType": "chain-rpc", "chainId": 11155111, "networkName": "Sepolia", "status": "degraded", "rawStatus": "degraded", "sampledAt": "2026-05-01T11:55:00.000Z", "blockHeight": 8154300, "chainHeadBlock": null, "syncLag": null, "entityCount": null, "blockAgeSeconds": 45, "finalityLagBlocks": 3, "stallSeconds": 30, "collectorLatencyMs": 148 } ], "nextCursor": null, "totalCount": 1, "facets": {} } ``` ## Stream health events [#stream-health-events] Use the stream endpoint when you want live updates in an operations screen or incident console. ```bash curl -N "https://your-platform.example.com/api/v2/blockchain-monitoring/health-snapshots/stream" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` The stream uses server-sent events. Every event includes `eventType`, so parse that first instead of assuming each message has snapshot fields. Snapshot events use `eventType: "snapshot"` and carry the same health fields as snapshot list items, plus `serviceId` and optional `deploymentState`: ```json { "eventType": "snapshot", "id": "01964f32-4cc1-7000-a05e-88a188d62921", "serviceId": "01964f21-9f02-7000-9e8d-1e6d1fdc7f01", "serviceType": "indexer", "chainId": 11155111, "networkName": "Sepolia", "status": "healthy", "rawStatus": "healthy", "sampledAt": "2026-05-01T11:59:30.000Z", "blockHeight": 8154321, "chainHeadBlock": 8154323, "syncLag": 2, "entityCount": null, "blockAgeSeconds": null, "finalityLagBlocks": null, "stallSeconds": null, "collectorLatencyMs": 124, "deploymentState": null } ``` Block-progress events use `eventType: "block-progress"`. DALP emits these while an indexer processes block batches, including during reindexing: ```json { "eventType": "block-progress", "chainId": 11155111, "blockNumber": 8154321 } ``` Keep the connection open and update your view based on the event type you receive. ## Use the CLI [#use-the-cli] The same monitoring data is available from the DALP CLI: ```bash dalp monitoring blockchain summary --from 2026-05-01T00:00:00.000Z --to 2026-05-01T12:00:00.000Z dalp monitoring blockchain timeline --from 2026-04-24T00:00:00.000Z --to 2026-05-01T00:00:00.000Z dalp monitoring blockchain services --from 2026-05-01T00:00:00.000Z --to 2026-05-01T12:00:00.000Z dalp monitoring blockchain snapshots dalp monitoring blockchain snapshots-stream ``` Use the CLI for quick checks. Use the API when you are building dashboards, alerting, or operational reports. ## Related operations [#related-operations] * [Transaction tracking](/docs/developer-guides/operations/transaction-tracking) - Check the status of a submitted transaction by hash. * [Reconciliate balances](/docs/developer-guides/operations/reconciliate-balances) - Compare on-chain balances with your internal records. * [API reference](/docs/developer-guides/api-integration/api-reference) - Browse generated API coverage. # Reconciliate balances Source: https://docs.settlemint.com/docs/developer-guides/operations/reconciliate-balances Compare token balances and transaction history between your system and on-chain state. Reconciliation compares your internal records against on-chain state to detect discrepancies. Regular reconciliation catches integration bugs, missed events, and unauthorized transfers before they become audit findings. **Common use cases:** * **Daily reconciliation** - Compare end-of-day balances with your ledger * **Transaction matching** - Verify all transfers in your system exist on-chain * **Audit preparation** - Generate proof of on-chain state for compliance * **Incident investigation** - Trace balance discrepancies to specific transactions * **Migration validation** - Confirm balances after system upgrades All endpoints return indexed on-chain data. Typical latency is under 2 seconds after block confirmation. For real-time status of pending transactions, use [transaction tracking](/docs/developer-guides/operations/transaction-tracking). ## Prerequisites [#prerequisites] * Platform URL (e.g., `https://your-platform.example.com`) * API key (see [Getting Started](/docs/developer-guides/api-integration/getting-started)) * Token contract address for the asset to reconcile * Your internal balance records for comparison Asset analytics for balance reconciliation ## Steps [#steps] ### Query current holder balances [#query-current-holder-balances] Get all holder balances for a token: ```bash curl -X GET "https://your-platform.example.com/api/token/0xTOKEN_ADDRESS/holders" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` **Response:** ```json { "token": { "balances": [ { "account": { "id": "0xHOLDER_1" }, "value": "1000.00", "available": "800.00", "frozen": "200.00", "isFrozen": false, "lastUpdatedAt": "2025-01-15T10:30:00.000Z" }, { "account": { "id": "0xHOLDER_2" }, "value": "500.00", "available": "500.00", "frozen": "0.00", "isFrozen": false, "lastUpdatedAt": "2025-01-14T15:20:00.000Z" } ] }, "totalCount": 2 } ``` For a single holder: ```bash curl -X GET "https://your-platform.example.com/api/token/0xTOKEN_ADDRESS/holder?holderAddress=0xHOLDER_ADDRESS" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` ### Query transaction history [#query-transaction-history] Get balance-affecting events for the token: ```bash curl -X GET "https://your-platform.example.com/api/token/0xTOKEN_ADDRESS/events?eventNames=TransferCompleted&eventNames=MintCompleted&eventNames=BurnCompleted" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` **Response:** ```json { "events": [ { "id": "0xEVENT_ID", "eventName": "TransferCompleted", "blockNumber": "12345678", "blockTimestamp": "2025-01-15T10:30:00.000Z", "transactionHash": "0xTX_HASH", "emitter": { "id": "0xTOKEN_ADDRESS" }, "sender": { "id": "0xSENDER_ADDRESS" }, "values": [ { "id": "0x...", "name": "from", "value": "0xFROM_ADDRESS" }, { "id": "0x...", "name": "to", "value": "0xTO_ADDRESS" }, { "id": "0x...", "name": "amount", "value": "1000000000000000000" } ] } ] } ``` ### Compare with your records [#compare-with-your-records] Match on-chain data against your internal ledger: 1. **Balance check** - For each holder, compare `value` field with your records 2. **Transaction check** - For each event, verify a matching entry exists in your system 3. **Missing transactions** - Events on-chain without internal records indicate missed processing 4. **Extra transactions** - Internal records without on-chain events indicate failed submissions ### Investigate discrepancies [#investigate-discrepancies] For mismatches, query specific transactions: ```bash curl -X GET "https://your-platform.example.com/api/token/0xTOKEN_ADDRESS/events?transactionHashes=0xTX_HASH_1&transactionHashes=0xTX_HASH_2" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` Or filter by sender to find transactions initiated by a specific wallet: ```bash curl -X GET "https://your-platform.example.com/api/token/0xTOKEN_ADDRESS/events?eventNames=TransferCompleted&senderAddress=0xYOUR_WALLET" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` ## Reconciliation flow [#reconciliation-flow] ## API endpoints [#api-endpoints] ### All holders for a token [#all-holders-for-a-token] **Endpoint:** `GET /api/token/{tokenAddress}/holders` Returns all holder balances for the specified token. ### Single holder balance [#single-holder-balance] **Endpoint:** `GET /api/token/{tokenAddress}/holder` **Query parameters:** | Parameter | Type | Description | | --------------- | -------- | ----------------------- | | `holderAddress` | `string` | Wallet address to query | ### All balances for a user [#all-balances-for-a-user] **Endpoint:** `GET /api/user/assets` **Query parameters:** | Parameter | Type | Description | | --------- | -------- | ----------------------- | | `wallet` | `string` | Wallet address to query | Returns all token balances held by a specific wallet. ### Token events [#token-events] **Endpoint:** `GET /api/token/{tokenAddress}/events` **Query parameters:** | Parameter | Type | Description | | ------------------- | ---------- | ------------------------------- | | `eventNames` | `string[]` | Filter by event types | | `senderAddress` | `string` | Filter by transaction initiator | | `transactionHashes` | `string[]` | Filter by specific transactions | ### User events [#user-events] **Endpoint:** `GET /api/user/events` **Query parameters:** | Parameter | Type | Default | Description | | ------------------- | ---------- | ---------------- | ---------------------- | | `limit` | `number` | `20` | Max events (1-500) | | `offset` | `number` | `0` | Pagination offset | | `orderBy` | `string` | `blockTimestamp` | Sort field | | `orderDirection` | `string` | `desc` | Sort direction | | `eventNames` | `string[]` | — | Filter by event types | | `senderAddress` | `string` | — | Filter by initiator | | `transactionHashes` | `string[]` | — | Filter by transactions | ## Event types for reconciliation [#event-types-for-reconciliation] | Event | Description | Balance Impact | | ------------------- | --------------------------------- | -------------------- | | `TransferCompleted` | Standard transfer between holders | Sender ↓, Receiver ↑ | | `MintCompleted` | New tokens created | Recipient ↑ | | `BurnCompleted` | Tokens destroyed | Holder ↓ | | `ForcedTransfer` | Custodian-initiated transfer | Sender ↓, Receiver ↑ | ## Response fields [#response-fields] ### Balance fields [#balance-fields] | Field | Type | Description | | --------------- | --------- | ----------------------------------------- | | `value` | `string` | Total balance (available + frozen) | | `available` | `string` | Balance available for transfer | | `frozen` | `string` | Balance locked by compliance or custodian | | `isFrozen` | `boolean` | Whether the entire account is frozen | | `lastUpdatedAt` | `string` | ISO timestamp of last balance change | ### Event fields [#event-fields] | Field | Type | Description | | ----------------- | -------- | ----------------------------------------- | | `id` | `string` | Unique event identifier | | `eventName` | `string` | Type of event | | `transactionHash` | `string` | On-chain transaction identifier | | `blockTimestamp` | `string` | ISO timestamp when event occurred | | `blockNumber` | `string` | Block containing the transaction | | `emitter` | `object` | Contract that emitted the event | | `sender` | `object` | Account that initiated the transaction | | `involved` | `array` | All accounts participating in the event | | `values` | `array` | Event parameters (from, to, amount, etc.) | ## Sender vs involved [#sender-vs-involved] * **`sender`** - The wallet that signed and submitted the transaction * **`involved`** - All addresses participating in the event (sender, recipient, grantee, etc.) Use `senderAddress` filter when you want only transactions you initiated, excluding those where you were merely a recipient. ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------- | ---------------------------------------------- | | Balance mismatch | Check for pending transactions not yet indexed | | Missing events | Verify event name filter matches exactly | | Events show wrong amounts | Amounts are in wei; apply token decimals | | Stale data | Indexer latency \~2 seconds; wait and retry | | Too many results | Use pagination with `limit` and `offset` | ## Related operations [#related-operations] * [Transaction tracking](/docs/developer-guides/operations/transaction-tracking) - Check transaction status by hash * [Error handling](/docs/developer-guides/api-integration/error-handling) - Handle API errors * [API reference](/docs/developer-guides/api-integration/api-reference) - Complete endpoint documentation # Restate workflow recovery Source: https://docs.settlemint.com/docs/developer-guides/operations/restate-workflow-recovery Recover Restate-backed DALP workflows with operator-only DAPI routes for health checks, redeployment, stale deployment cleanup, and stuck workflow recovery. Run the `doctor` route first. It reports Restate ingress, admin API, deployment, service, and invocation health without changing workflow state. Use the write routes only after you know which condition you are fixing. Each route requires an authenticated operator with the system operate permission. DALP records operator route attempts for audit, including the actor, route, request arguments, reason when supplied, outcome, and error message when a route fails. ## Before you start [#before-you-start] Confirm you have: * access to the DALP admin API for the environment you are operating * an operator account or API key with system operate permission * the Restate service URL used by the durable workflow service, when you need to redeploy or clean deployments * the Restate workflow `serviceName` and `serviceKey`, when you need to clear a blocked workflow Set a base URL for the examples: ```bash export DALP_API_URL="https://your-platform.example.com" export DALP_API_KEY="sm_dalp_xxxxxxxxxxxxxxxx" ``` ## 1. Check workflow health [#1-check-workflow-health] Run the doctor route first: ```bash curl -X POST "$DALP_API_URL/api/v2/admin/operator/restate/doctor" \ -H "X-Api-Key: $DALP_API_KEY" \ -H "Content-Type: application/json" \ -d '{}' ``` The response has five independent components: | Component | What it tells you | | ------------- | -------------------------------------------------------------------- | | `ingress` | Whether the Restate ingress URL responds and how long it took. | | `admin` | Whether the Restate admin API responds and which version it reports. | | `deployments` | Registered Restate deployments and their service URLs. | | `services` | Registered Restate service names and revisions. | | `invocations` | Invocation counts by status and up to 20 recent failures. | Each component has its own `status`: `ok`, `unreachable`, or `degraded`. A failed sub-check degrades only that component, so read the whole response before choosing a recovery action. Example response shape: ```json { "ingress": { "status": "ok", "latencyMs": 12 }, "admin": { "status": "ok", "latencyMs": 9, "version": "1.4.0" }, "deployments": { "status": "ok", "items": [ { "id": "dp_01j...", "serviceUrl": "http://ddwf:9080", "createdAt": "2026-05-09T10:00:00.000Z" } ], "error": null }, "services": { "status": "ok", "items": [{ "name": "IdentityRecoveryWorkflow", "revision": 3 }], "error": null }, "invocations": { "status": "ok", "byStatus": { "invoked": 2, "suspended": 1 }, "recentFailures": [], "error": null } } ``` ## 2. Re-register the durable workflow service [#2-re-register-the-durable-workflow-service] Use `force-redeploy` when the service needs to be registered again with Restate. ```bash curl -X POST "$DALP_API_URL/api/v2/admin/operator/restate/force-redeploy" \ -H "X-Api-Key: $DALP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "serviceUrl": "http://ddwf:9080", "force": true }' ``` A successful response acknowledges the registration and returns the Restate deployment id: ```json { "acknowledged": true, "deploymentId": "dp_01j..." } ``` `force` defaults to `true`. This route does not remove old deployments. If doctor still shows old deployments after redeploying, run stale deployment cleanup next. ## 3. Remove stale deployments [#3-remove-stale-deployments] Use `cleanup-stale-deployments` after you know which service URL should remain active. DALP keeps the deployment matching `serviceUrl` and drains every other registered deployment. ```bash curl -X POST "$DALP_API_URL/api/v2/admin/operator/restate/cleanup-stale-deployments" \ -H "X-Api-Key: $DALP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "serviceUrl": "http://ddwf:9080", "forceDrain": false }' ``` A successful response is: ```json { "acknowledged": true } ``` Use `forceDrain: true` only when stale deployments point to dead services and still have pending invocations that cannot drain normally. Confirm the result with doctor or the Restate admin deployment list. ## 4. Clear a blocked workflow for retry [#4-clear-a-blocked-workflow-for-retry] Use `recover-stuck-workflow` when a specific workflow key is wedged and must start again from a blank state. ```bash curl -X POST "$DALP_API_URL/api/v2/admin/operator/restate/recover-stuck-workflow" \ -H "X-Api-Key: $DALP_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "serviceName": "InvitationWorkflow", "serviceKey": "invitation_01j8m7k2q3r4s5t6u7v8w9x0y1_1770000000000" }' ``` `serviceName` and `serviceKey` may contain letters, numbers, underscores, and hyphens. A successful response is: ```json { "acknowledged": true } ``` This route kills and purges prior invocations for the supplied workflow key, then clears keyed workflow state. The next workflow submission starts from a blank state. DALP refuses to clear a workflow when recovery would hide a still-active invocation or a workflow that already succeeded. In that case, the route returns `RESTATE_WORKFLOW_RETRY_BLOCKED` with a structured reason and the relevant invocation ids. ## Recovery error codes [#recovery-error-codes] | Error code | Meaning | What to do | | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | | `OFFCHAIN_USER_PERMISSION_REQUIRED` | The caller does not have system operate permission. | Use an operator account or update the caller's role before retrying. | | `RESTATE_ADMIN_UNREACHABLE` | DALP could not resolve or reach the Restate admin API. | Check Restate admin connectivity, then rerun doctor. | | `RESTATE_DEPLOYMENT_NOT_FOUND` | Cleanup or redeploy could not find the expected deployment for the supplied service URL. | Run doctor and use the exact registered service URL. | | `RESTATE_WORKFLOW_RETRY_BLOCKED` | Workflow recovery found an active invocation, an already succeeded invocation, or a purge/query condition that prevents safe retry. | Inspect the returned `reason` and `invocationIds` before deciding whether to retry later or investigate manually. | ## Related pages [#related-pages] * [Blockchain monitoring](/docs/developer-guides/operations/blockchain-monitoring) * [Transaction tracking](/docs/developer-guides/operations/transaction-tracking) * [DAPI error reference](/docs/developer-guides/api-integration/dapi-error-reference) * [Authorization](/docs/architecture/security/authorization) # System upgrades Source: https://docs.settlemint.com/docs/developer-guides/operations/system-upgrades Compare system contract versions and run the guided upgrade workflow from the dApp. DALP includes a system upgrade surface for keeping deployed system contracts in sync with the latest implementations available for the active network. Operators use this workflow when the platform reports that system components need an upgrade. DALP compares the active system with the network directory, shows the components that differ, then runs a guided upgrade with live progress. System upgrades can include on-chain transactions. Closing the dialog or retrying after a failure does not roll back completed steps. Review the comparison before you start. ## Who can run upgrades [#who-can-run-upgrades] The system upgrade page is visible in platform settings, but the upgrade action is available only to accounts with the required system-management permission. The API accepts callers that are platform admins or wallets with the system manager or admin role on the indexed system. If your account does not have permission, the page shows that system management is unavailable and does not offer the upgrade action. ## What the comparison shows [#what-the-comparison-shows] Before an upgrade starts, DALP compares the active system address with the expected implementations from the directory for the active network. The comparison groups components by area, including core system components, identity, per-token infrastructure, compliance, registries, token factories, add-ons, and compliance modules. Each row shows whether the component is already up to date or has a newer implementation available. Factory rows can also show how many deployed tokens are affected. Components that exist in the directory but are not installed on the active system are tracked separately from upgradeable components. Installing new factory, add-on, or compliance module types is handled by the dedicated management pages, not by the system upgrade workflow. ## Run an upgrade [#run-an-upgrade] ### Review the comparison [#review-the-comparison] Open the system upgrade page and review the summary. You can hide or show up-to-date components and search for a specific component name. If all components are current, the page reports that the system is up to date and the upgrade button is disabled. ### Start the guided upgrade [#start-the-guided-upgrade] Select **Upgrade all** when the summary shows upgradeable components. The dialog lists the number of system contracts that will be upgraded and the number of deployed tokens affected by factory upgrades. The confirmation step requires you to confirm before DALP submits the upgrade workflow. ### Monitor live progress [#monitor-live-progress] After the workflow starts, the dialog switches to a progress view. DALP streams the migration tree and step updates until the workflow completes or fails. Keep the dialog open while the upgrade is running. If you refresh or reopen the dialog during an in-flight upgrade, DALP checks for an active migration for the current organization and reconnects to the live progress stream. ### Review the result [#review-the-result] When the upgrade succeeds, close the dialog. DALP refreshes the comparison so the page reflects the updated system state. If the upgrade fails, the dialog shows the failed state and the reason reported by the workflow. Correct the underlying issue, then retry from the dialog. ## Retry behavior [#retry-behavior] Retrying a failed system upgrade is safe. DALP reuses the comparison as the source of truth and the workflow skips steps that are already complete or no longer need action. When another upgrade is already running for the organization, DALP reconnects the user to the active migration instead of starting a second one. If DALP cannot confirm or reset the previous workflow state, the platform refuses to start a new run and returns a retryable error. Migration progress is scoped to the active organization. A caller can subscribe only to the migration that belongs to the caller's organization. ## Troubleshooting [#troubleshooting] | Symptom | What it means | What to do | | ------------------------------------------------------------------ | --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | | The page says system management is unavailable | The account does not have the required system-management permission. | Use an admin or system-manager account, or ask an admin to grant access. | | The comparison cannot run because the system address is missing | DALP cannot resolve the active deployed system for the organization. | Deploy a system or configure the active system address, then retry the comparison. | | The comparison cannot run because the directory address is missing | The active network configuration does not include the directory contract address. | Set the directory contract address for the active network, then retry. | | Start returns that another migration is in progress | DALP found an active upgrade for the organization. | Reopen the dialog to reconnect to progress, or wait for the active run to finish. | | The upgrade fails after some steps complete | The failed step stopped the workflow, but completed steps remain applied. | Fix the underlying issue shown in the dialog, then retry the upgrade. | ## Related [#related] * [Operations guides](/docs/developer-guides/operations/blockchain-monitoring) * [Blockchain monitoring](/docs/developer-guides/operations/blockchain-monitoring) # Transaction tracking Source: https://docs.settlemint.com/docs/developer-guides/operations/transaction-tracking Check transaction status after timeout errors to determine retry safety. After a blockchain transaction times out, you need to verify its status before retrying. Retrying a successful transaction creates duplicates; retrying a failed one wastes time. The transaction tracking API tells you what DALP can confirm for your caller scope. **Common use cases:** * **Timeout recovery** - Determine if a timed-out transaction succeeded, failed, or is still pending * **Audit trails** - Retrieve transaction receipts for compliance records * **Debugging** - Investigate why a transaction reverted with decoded error messages * **Monitoring** - Track transaction confirmation across block explorers In the dApp, you can also use the **View on Explorer** action to open the configured block explorer (Etherscan, Blockscout, or a custom explorer base URL) for additional inspection. After a `CONFIRMATION_TIMEOUT` error, always check transaction status first. Retrying a successful transaction creates duplicates that cannot be undone. ## Prerequisites [#prerequisites] * Platform URL (e.g., `https://your-platform.example.com`) * API key (see [Getting Started](/docs/developer-guides/api-integration/getting-started)) * Transaction hash from error response or `X-Transaction-Hash` header ## Stored queue record visibility [#stored-queue-record-visibility] The hash lookup can still return public on-chain transaction data for a known transaction hash. DALP-stored queue records are scoped separately. When the API uses a stored queue record to return a pending transaction or a stored receipt, the record must be visible to the caller behind the API key. Visible stored records for hash lookup, status, and cancel include transactions submitted by the caller's externally owned account and by smart wallets where that account is a signer. Transaction-request list operations can widen to the organization's wallets when the caller has organization-level permissions. Admin force-fail, force-retry, and force-nonce operations use the same organization-level wallet visibility before they mutate a stored transaction. Transaction count and history metrics still count completed on-chain transfer events, but DALP filters them to the active chain, active system, and the caller's effective wallet set. A not-found response from the hash lookup means DALP did not find the transaction on-chain and did not find a stored queue record visible to the caller. It does not prove that no DALP tenant has ever stored that hash. ## Steps [#steps] ### Extract the transaction hash [#extract-the-transaction-hash] When a `CONFIRMATION_TIMEOUT` error occurs, get the hash from the error response: ```json { "code": "CONFIRMATION_TIMEOUT", "status": 504, "message": "Transaction confirmation timed out", "data": { "transactionHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" } } ``` Or from the response header: ```bash TX_HASH=$(curl -s -i -X POST "https://your-platform.example.com/api/token/mint" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{...}' | grep -i "x-transaction-hash" | cut -d' ' -f2 | tr -d '\r') ``` ### Query transaction status [#query-transaction-status] Call the transaction endpoint with the hash: ```bash curl -X GET "https://your-platform.example.com/api/transaction/0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" \ -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx" ``` **Success response (200 OK):** ```json { "transactionHash": "0x1234567890abcdef...", "from": "0xABCD1234567890abcdef1234567890abcdef1234", "receipt": { "status": "Success", "revertReason": null, "revertReasonDecoded": null, "blockNumber": "12345678", "blockHash": "0xabcd...", "gasUsed": "21000", "effectiveGasPrice": "1000000000", "from": "0xABCD...", "to": "0x5678..." } } ``` ### Interpret the result [#interpret-the-result] Check the response to determine your next action: | State | Condition | Action | | ------------- | -------------------------------- | ------------------------------------------------------------------------ | | **Pending** | `receipt` is `null` | Wait 3-5 seconds, poll again | | **Success** | `receipt.status` is `"Success"` | Do NOT retry, already completed | | **Reverted** | `receipt.status` is `"Reverted"` | Check `revertReasonDecoded`, fix issue, retry | | **Not found** | 404 not-found error | Verify the hash, API key, wallet, and organization scope before retrying | ### Handle each state [#handle-each-state] **Pending** — Transaction still processing: ```json { "transactionHash": "0x1234...", "from": "0xABCD...", "receipt": null } ``` Continue polling every 3-5 seconds. Stop after 2-3 minutes if still pending. **Success** — Transaction completed: ```json { "receipt": { "status": "Success", "blockNumber": "12345678" } } ``` Proceed with your workflow. Do not retry the original request. **Reverted** — Transaction failed on-chain: ```json { "receipt": { "status": "Reverted", "revertReason": "0x08c379a0...", "revertReasonDecoded": "InsufficientTokenBalance" } } ``` Fix the underlying issue and submit a new request. See [Error handling](/docs/developer-guides/api-integration/error-handling#blockchain-transaction-errors) for common revert reasons. **Not found** - No on-chain transaction and no caller-visible stored queue record: ```json { "code": "TRANSACTION_NOT_FOUND_NOT_CHAIN_NO_STORED_QUEUE_RECORD_HASH", "status": 404, "message": "Transaction not found. The transaction is not on-chain and has no stored queue record. Verify the hash is correct." } ``` The response message says there is no stored queue record. Read that as no stored queue record visible to the API key, wallet, and organization scope you used for this lookup. Retry the original request only after you confirm the hash came from your own request and the transaction remains absent from chain data and your visible transaction records. Activity log for transaction auditing ## Transaction state flow [#transaction-state-flow] ## Response fields [#response-fields] | Field | Type | Description | | ----------------------------- | ------------------------- | -------------------------------------- | | `transactionHash` | `string` | Unique transaction identifier | | `from` | `string` | Sender wallet address | | `receipt` | `object \| null` | Transaction receipt, `null` if pending | | `receipt.status` | `"Success" \| "Reverted"` | Execution result | | `receipt.revertReason` | `string \| null` | Raw revert reason if failed | | `receipt.revertReasonDecoded` | `string \| null` | Human-readable revert reason | | `receipt.blockNumber` | `string` | Block containing the transaction | | `receipt.gasUsed` | `string` | Actual gas consumed | On-chain transaction monitoring ## Polling pattern [#polling-pattern] 1. **Extract hash** — Get `transactionHash` from the error response 2. **Initial delay** — Wait 2-3 seconds before the first poll 3. **Poll interval** — Query every 3-5 seconds 4. **Timeout** — Stop after 2-3 minutes if still pending (investigate manually) 5. **Exit conditions** — Stop when `receipt` is not `null` or you get a scoped 404 after checking caller visibility ## Troubleshooting [#troubleshooting] | Issue | Solution | | ----------------------------------------- | ---------------------------------------------------------------------------------------------- | | 404 after known submission | Confirm the same API key, wallet, and organization scope submitted the request before retrying | | Stuck in pending | Check network congestion; may need gas price bump | | Reverted without reason | Contract may not emit revert strings; check logs | | Receipt shows success but state unchanged | Indexer latency; wait 2-5 seconds and re-query | ## Related operations [#related-operations] * [Error handling](/docs/developer-guides/api-integration/error-handling) — Handle API errors and blockchain reverts * [Reconciliate balances](/docs/developer-guides/operations/reconciliate-balances) — Compare on-chain state with your records * [API reference](/docs/developer-guides/api-integration/api-reference) — Complete endpoint documentation # Add administrators Source: https://docs.settlemint.com/docs/developer-guides/platform-setup/add-admins Grant administrative permissions to users in your organization via API. This guide explains how to grant platform administrator roles to wallet addresses using the API. Use this for automation, bulk operations, or integration into your provisioning workflows. For the web interface approach, see the [user guide](/docs/user-guides/platform-setup/add-admins). ## Prerequisites [#prerequisites] * Platform URL (e.g., `https://your-platform.example.com`) * API access token with `admin` system role (required to grant platform administrator roles) * Wallet verification method enabled on your account (e.g., pincode or 2FA) * Target wallet address (can be any valid address or looked up by email) * See [Getting Started](/docs/developer-guides/api-integration/getting-started) for API key setup ## When to add administrators via API [#when-to-add-administrators-via-api] ### Recommended scenarios [#recommended-scenarios] * **Initial setup automation** — Scripting first-time platform configuration * **Bulk provisioning** — Adding multiple administrators in batch operations * **Organizational onboarding** — Integrating with HR/identity systems Platform administrator roles control system-wide operations. Asset-specific roles (Asset Operator, Custodian, Supply Management, Emergency) are assigned per token during asset creation. ## Available system roles [#available-system-roles] | Role | Description | Common use cases | | -------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------- | | `admin` | Root authority that can grant or revoke all other system roles | Platform ops account, initial setup | | `systemManager` | Core system configuration (upgrades, registering factories/modules) | Deployment team, rarely granted to EOAs | | `identityManager` | Identity registry maintenance (register/recover identities, onboarding) | Compliance/onboarding teams managing identities | | `tokenManager` | Token factory calls such as `/api/token/create` | Every wallet that deploys assets | | `complianceManager` | Global compliance module setup, bypass lists, enforcement toggles | Custom compliance flows, allowlists | | `claimPolicyManager` | Trusted issuer and claim topic management | Workflows that check collateral/KYC claims before minting | | `claimIssuer` | Permission to create claims on identities | Auditors, service providers issuing attestations | ## Steps to add administrators [#steps-to-add-administrators] ### Identify target user [#identify-target-user] Look up the user by email to get their wallet address. If you already have the wallet address, skip this step. ```bash curl -X GET "https://your-platform.example.com/api/user/search?query=new.admin@example.com" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response:** ```json [ { "id": "usr_abc123", "name": "New Admin", "wallet": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "role": "member" } ] ``` Save the `wallet` address for the grant step. ### Check existing roles (optional) [#check-existing-roles-optional] Before granting roles, verify the user's current role assignments: ```bash curl -X GET "https://your-platform.example.com/api/system/access-manager/roles" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response:** ```json [ { "account": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "roles": [] }, { "account": "0xExistingAdmin...", "roles": ["admin", "tokenManager"] } ] ``` To check a single wallet directly: ```bash curl -X GET "https://your-platform.example.com/api/system/access-manager/roles/0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" \ -H "X-Api-Key: YOUR_API_KEY" ``` ### Grant administrator role [#grant-administrator-role] Assign the desired platform role to the target wallet: ```bash curl -X POST "https://your-platform.example.com/api/system/access-manager/grant-roles" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "account": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "role": "identityManager", "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` **Response:** ```json { "accounts": ["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"], "roles": ["identityManager"] } ``` #### Grant multiple roles [#grant-multiple-roles] To assign multiple roles in a single transaction: ```bash curl -X POST "https://your-platform.example.com/api/system/access-manager/grant-roles" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "account": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "role": ["identityManager", "tokenManager"], "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` You can grant multiple roles to one address, or one role to multiple addresses, but not multiple roles to multiple addresses in a single request. Use separate requests for complex bulk operations. ### Verify role assignment [#verify-role-assignment] Confirm the role was granted by checking the updated roles list: ```bash curl -X GET "https://your-platform.example.com/api/system/access-manager/roles" \ -H "X-Api-Key: YOUR_API_KEY" ``` The target wallet should now appear with the assigned roles in the response. User profile with administrative role assignment ## Request parameters [#request-parameters] | Parameter | Type | Required | Description | | -------------------- | --------------- | -------- | ---------------------------------------------------------------- | | `account` | string or array | Yes | Wallet address(es) to grant role to | | `role` | string or array | Yes | Role(s) to grant | | `walletVerification` | object | Yes | Your wallet verification to authorize the blockchain transaction | ### Wallet verification object [#wallet-verification-object] | Field | Type | Description | | ------------------------ | ------ | ---------------------------------------------- | | `secretVerificationCode` | string | 6-digit pincode or TOTP code | | `verificationType` | string | "PINCODE" (default), "SECRET\_CODES", or "OTP" | ## Response fields [#response-fields] | Field | Type | Description | | ---------- | ----- | ------------------------------------ | | `accounts` | array | Wallet addresses that received roles | | `roles` | array | Roles that were granted | ## Best practices [#best-practices] ### Role assignment principles [#role-assignment-principles] * **Least privilege** — Grant only necessary permissions * **Separation of duties** — Divide critical functions among different admins * **Regular review** — Audit role assignments periodically * **Document decisions** — Record why roles were granted for audit purposes ### Security considerations [#security-considerations] * Keep `admin` role restricted to platform ops accounts * Use separate wallets for different administrative functions * Store API keys securely and rotate regularly * Use environment variables for credentials in scripts * Test role changes in staging before production ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------------- | -------------------------------------------------------------------------------------------- | | `401 Unauthorized` | API key is invalid, expired, or disabled | | `403 USER_NOT_AUTHORIZED` | Verify you have `admin` system role. Only admins can grant other system roles. | | `404 User not found` | Email lookup failed; verify user exists or use wallet address directly | | `400 Role not found` | Check role name matches exactly (case-sensitive). See available roles table above. | | `400 Duplicate role` | User already has this role. Check existing roles before granting. | | Transaction fails | Ensure your wallet has sufficient gas. Verify PIN/OTP is correct. | | Batch operation fails | Cannot grant multiple roles to multiple addresses in one call. Split into separate requests. | | User cannot see new permissions | Ask user to log out and back in. Verify transaction was confirmed on-chain. | ## Related guides [#related-guides] * [Change Admin Roles](/docs/developer-guides/platform-setup/change-admin-roles) — Modify or revoke existing role assignments via API * [API Reference](/docs/developer-guides/api-integration/api-reference) — Full OpenAPI specification * [Getting Started](/docs/developer-guides/api-integration/getting-started) — API key setup * [Add Administrators (User Guide)](/docs/user-guides/platform-setup/add-admins) — Web interface approach * [Platform Setup Overview](/docs/user-guides/platform-setup/platform-overview) — Complete role descriptions and permissions # Change admin roles Source: https://docs.settlemint.com/docs/developer-guides/platform-setup/change-admin-roles Grant or revoke platform administrator roles. This guide explains how to modify platform administrator roles using the API. Use this for automation, organizational restructuring, or integration into your provisioning workflows. For the web interface approach, see the [user guide](/docs/user-guides/platform-setup/change-admin-roles). ## Prerequisites [#prerequisites] * Platform URL (e.g., `https://your-platform.example.com`) * API key from a user with the **Admin** role (see [Getting Started](/docs/developer-guides/api-integration/getting-started) for API key setup) * Wallet verification method enabled on your account (e.g., pincode or 2FA) ## When to change admin roles [#when-to-change-admin-roles] ### Common scenarios [#common-scenarios] * **Initial setup** — First admin needs additional roles after platform initialization * **Role expansion** — Admin taking on new responsibilities * **Role reduction** — Removing unnecessary permissions following least privilege * **Temporary delegation** — Granting time-limited permissions for specific tasks * **Organizational changes** — Restructuring administrative responsibilities * **Automation** — Integrating role management into your provisioning workflows ### Security considerations [#security-considerations] * Follow principle of least privilege * Remove roles when no longer needed * Document role changes for audit purposes * Coordinate with affected administrators ## Available system roles [#available-system-roles] | Role | Description | Common use cases | | -------------------- | ----------------------------------------------------------------------- | --------------------------------------------------------- | | `admin` | Root authority that can grant or revoke all other system roles | Platform ops account, initial setup | | `systemManager` | Core system configuration (upgrades, registering factories/modules) | Deployment team, rarely granted to EOAs | | `identityManager` | Identity registry maintenance (register/recover identities, onboarding) | Compliance/onboarding teams managing identities | | `tokenManager` | Token factory calls such as `/api/token/create` | Every wallet that deploys assets | | `complianceManager` | Global compliance module setup, bypass lists, enforcement toggles | Custom compliance flows, allowlists | | `claimPolicyManager` | Trusted issuer and claim topic management | Workflows that check collateral/KYC claims before minting | | `claimIssuer` | Permission to create claims on identities | Auditors, service providers issuing attestations | ## Changing roles [#changing-roles] ### List current roles [#list-current-roles] Check existing role assignments before making changes: ```bash curl -X GET "https://your-platform.example.com/api/system/access-manager/roles" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response:** ```json [ { "account": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "roles": ["admin", "tokenManager"] }, { "account": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890", "roles": ["identityManager"] } ] ``` To check roles for a specific wallet: ```bash curl -X GET "https://your-platform.example.com/api/system/access-manager/roles/0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" \ -H "X-Api-Key: YOUR_API_KEY" ``` ### Grant a role [#grant-a-role] Grant a single system role to a wallet address: ```bash curl -X POST "https://your-platform.example.com/api/system/access-manager/grant-roles" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "account": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "role": "tokenManager", "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` **Response:** ```json { "accounts": ["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"], "roles": ["tokenManager"] } ``` #### Grant multiple roles [#grant-multiple-roles] Assign multiple roles to one wallet in a single transaction: ```bash curl -X POST "https://your-platform.example.com/api/system/access-manager/grant-roles" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "account": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "role": ["tokenManager", "identityManager"], "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` #### Batch grant to multiple wallets [#batch-grant-to-multiple-wallets] Grant the same role to multiple wallets efficiently: ```bash curl -X POST "https://your-platform.example.com/api/system/access-manager/grant-roles" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "account": ["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890"], "role": "tokenManager", "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` You cannot grant multiple roles to multiple addresses in a single transaction. Use separate requests for each address or each role combination. ### Revoke a role [#revoke-a-role] Remove a system role from a wallet address: ```bash curl -X DELETE "https://your-platform.example.com/api/system/access-manager/revoke-roles" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "account": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "role": "tokenManager", "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` **Response:** ```json { "accounts": ["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"], "roles": ["tokenManager"] } ``` #### Revoke multiple roles [#revoke-multiple-roles] Remove multiple roles from one wallet in a single transaction: ```bash curl -X DELETE "https://your-platform.example.com/api/system/access-manager/revoke-roles" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "account": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "role": ["tokenManager", "identityManager"], "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` #### Batch revoke from multiple wallets [#batch-revoke-from-multiple-wallets] Revoke the same role from multiple wallets efficiently: ```bash curl -X DELETE "https://your-platform.example.com/api/system/access-manager/revoke-roles" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "account": ["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890"], "role": "tokenManager", "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` ### Verify changes [#verify-changes] Confirm role changes by listing updated roles: ```bash curl -X GET "https://your-platform.example.com/api/system/access-manager/roles" \ -H "X-Api-Key: YOUR_API_KEY" ``` The response shows all current role assignments. Verify the changes were applied correctly. ## Request parameters [#request-parameters] | Parameter | Type | Required | Description | | -------------------- | --------------- | -------- | ---------------------------------------------------------------- | | `account` | string or array | Yes | Wallet address(es) to grant/revoke role | | `role` | string or array | Yes | Role(s) to grant/revoke | | `walletVerification` | object | Yes | Your wallet verification to authorize the blockchain transaction | ### Wallet verification object [#wallet-verification-object] | Field | Type | Description | | ------------------------ | ------ | ---------------------------------------------- | | `secretVerificationCode` | string | 6-digit pincode or TOTP code | | `verificationType` | string | "PINCODE" (default), "SECRET\_CODES", or "OTP" | ## Response fields [#response-fields] | Field | Type | Description | | ---------- | ----- | ----------------------------------- | | `accounts` | array | Wallet addresses that were modified | | `roles` | array | Roles that were granted/revoked | ## Role dependencies and conflicts [#role-dependencies-and-conflicts] Common role combinations for specific functions: * **Compliance Manager** + **Identity Manager** — Complete compliance setup * **Token Manager** — Full token lifecycle management * **Identity Manager** + **Claim Issuer** — Complete user onboarding ## Best practices [#best-practices] ### Role assignment [#role-assignment] * **Principle of least privilege** — Grant only necessary permissions * **Separation of duties** — Divide critical functions among multiple admins * **Regular review** — Audit and update roles periodically * **Documentation** — Record role changes and rationale ### Security [#security] * Keep `admin` role restricted to platform ops accounts * Use separate wallets for different administrative functions * Maintain backup administrators for critical roles * Monitor role changes through event logs ### Operations [#operations] * Test role changes in a staging environment first * Coordinate role changes with affected administrators * Plan for administrator unavailability with backup coverage * Define clear escalation paths for requesting permissions ## Troubleshooting [#troubleshooting] | Issue | Solution | | ----------------------- | -------------------------------------------------------------------------------------------- | | Permission denied | Verify you have the `admin` system role. Only admins can grant/revoke roles. | | Role not found | Check the role name matches exactly (case-sensitive). See available roles table above. | | Transaction fails | Ensure wallet has sufficient gas. Verify PIN/OTP is correct. Check network connectivity. | | Duplicate role error | The wallet already has this role. Check current roles before granting. | | Cannot revoke own admin | Smart contract prevents self-revocation of admin role. Another admin must revoke it. | | Batch operation fails | Cannot grant multiple roles to multiple addresses in one call. Split into separate requests. | ## Related guides [#related-guides] * [Change Asset Admin Roles](/docs/developer-guides/asset-servicing/change-asset-admin-roles) — Manage token-level permissions * [API Integration](/docs/developer-guides/api-integration/getting-started) — Getting started with the DALP API * [Change Admin Roles (UI)](/docs/user-guides/platform-setup/change-admin-roles) — Web interface guide # First administrator setup Source: https://docs.settlemint.com/docs/developer-guides/platform-setup/first-admin-setup Initialize your platform by creating the first administrator account and setting up system infrastructure via API. The first administrator is responsible for initializing the entire platform. This one-time process creates your organization, sets up system infrastructure, and establishes the foundation for all future operations. Initial login for first administrator setup For the web interface approach, see the [user guide](/docs/user-guides/platform-setup/first-admin-setup). ## Prerequisites [#prerequisites] * Platform URL (e.g., `https://your-platform.example.com`) * ETH or native tokens for gas fees (public chains only) * Email address for the administrator account When using the API for account creation, you must handle passwords with extreme care. Never store passwords in plaintext — use secure secrets management (HashiCorp Vault, AWS Secrets Manager, etc.). Never log passwords — ensure passwords are excluded from all logging. Use environment variables — never hardcode credentials in scripts. Consider using the web interface — for manual setup, the UI is more secure as passwords never leave the browser. If you must store passwords programmatically, rotate them immediately after setup. ## Overview [#overview] The first admin setup involves: 1. Creating your administrator account 2. Naming your organization 3. Creating an API key for subsequent requests 4. Setting up a blockchain wallet with security 5. Creating your on-chain identity 6. Initializing system infrastructure 7. Configuring platform basics (currency, asset types) Only the first user can initialize the system. This sets up all core platform infrastructure including identity management, access control, and compliance capabilities. ## Steps [#steps] ### Create administrator account [#create-administrator-account] Register the first administrator account using email and password: ```bash curl -i -c cookies.txt -X POST "https://your-platform.example.com/api/auth/sign-up/email" \ -H "Content-Type: application/json" \ -H "Origin: https://your-platform.example.com" \ -d '{ "name": "Platform Admin", "email": "admin@example.com", "password": "YOUR_SECURE_PASSWORD" }' ``` **Response:** ```json { "token": "1CL....UdI", "user": { "name": "Platform Admin", "email": "admin@example.com", "emailVerified": false, "image": null, "createdAt": "2024-01-15T10:00:00.000Z", "updatedAt": "2024-01-15T10:00:00.000Z", "role": "admin", "banned": false, "banReason": null, "banExpires": null, "twoFactorEnabled": false, "wallet": "0x0000000000000000000000000000000000000000", "lastLoginAt": null, "walletPincodeEnabled": false, "walletPincodeVerificationId": null, "walletTwoFactorEnabled": false, "walletTwoFactorVerificationId": null, "walletSecretCodeVerificationId": null, "walletSecretCodesConfirmed": false, "id": "usr_abc123" } } ``` The session is now stored in `cookies.txt`. All subsequent authentication requests will use this session cookie automatically. Save the `user.id` value (`usr_abc123` in this example)—you'll need it for the KYC profile step later. You can also retrieve your user information at any time using: ```bash curl -X GET "https://your-platform.example.com/api/user/me" \ -H "X-Api-Key: YOUR_API_KEY" ``` If automating this step, ensure the password is sourced from a secure secrets manager and never persisted in logs, scripts, or version control. Consider deleting the password from memory immediately after use. ### Name your organization [#name-your-organization] Enter your organization name—this will be the primary entity operating the platform (e.g., "Financial Institution S.A." or "Digital Securities Ltd."). This creates your organization and sets it as your active context: ```bash curl -b cookies.txt -X POST "https://your-platform.example.com/api/auth/organization/create" \ -H "Content-Type: application/json" \ -H "Origin: https://your-platform.example.com" \ -d '{ "name": "Financial Institution S.A.", "slug": "financial-institution-sa" }' ``` **Response:** ```json { "name": "Financial Institution S.A.", "slug": "financial-institution-sa", "logo": null, "createdAt": "2024-01-15T10:00:00.000Z", "id": "org_abc123", "members": [ { "organizationId": "org_abc123", "userId": "usr_abc123", "role": "owner", "createdAt": "2024-01-15T10:00:00.000Z", "id": "mem_abc123" } ] } ``` After creating the organization, set it as your active context: ```bash curl -b cookies.txt -X POST "https://your-platform.example.com/api/auth/organization/set-active" \ -H "Content-Type: application/json" \ -H "Origin: https://your-platform.example.com" \ -d '{ "organizationId": "org_abc123" }' ``` **Response:** ```json { "name": "Financial Institution S.A.", "slug": "financial-institution-sa", "logo": null, "createdAt": "2024-01-15T10:00:00.000Z", "metadata": null, "id": "org_abc123" } ``` Save the organization `id` from the response—you'll need it when creating API keys. ### Create API key [#create-api-key] Create an API key to authenticate subsequent requests: ```bash curl -b cookies.txt -X POST "https://your-platform.example.com/api/auth/api-key/create" \ -H "Content-Type: application/json" \ -H "Origin: https://your-platform.example.com" \ -d '{ "name": "Platform Setup Key", "expiresIn": 86400, "metadata": { "organizationId": "org_abc123" } }' ``` **Response:** ```json { "name": "Platform Setup Key", "start": "sm_atk", "prefix": "sm_dalp_", "key": "sm_dalp_xxxxxxxxxxxxxxxx", "userId": "usr_abc123", "enabled": true, "rateLimitEnabled": true, "rateLimitTimeWindow": 60000, "rateLimitMax": 10000, "requestCount": 0, "expiresAt": "2024-01-16T10:00:00.000Z", "createdAt": "2024-01-15T10:00:00.000Z", "updatedAt": "2024-01-15T10:00:00.000Z", "permissions": { "user": ["create", "list", "set-role", "ban", "impersonate", "delete", "set-password", "get", "update"], "session": ["list", "revoke", "delete"], "organization": ["update", "delete"], "member": ["create", "update", "delete"], "invitation": ["create", "cancel"], "team": ["create", "update", "delete"], "ac": ["create", "read", "update", "delete"], "setting": ["read", "list", "upsert", "remove"], "system": ["read", "list", "create"], "exchangeRates": ["read", "list", "remove", "sync", "update"] }, "metadata": { "organizationId": "org_abc123" }, "id": "key_abc123" } ``` The full API key (`sm_dalp_...`) is shown only once. Store it immediately in a secure location. All subsequent requests will use this key in the `X-Api-Key` header. ### Create blockchain wallet [#create-blockchain-wallet] Create a blockchain wallet for the administrator: ```bash curl -X POST "https://your-platform.example.com/api/user/create-wallet" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" ``` **Response:** ```json { "wallet": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" } ``` Save the wallet address for reference. ### Secure with PIN [#secure-with-pin] Enable PIN-based wallet verification for secure transaction signing: ```bash curl -X POST "https://your-platform.example.com/api/auth/wallet/pincode/enable" \ -H "Content-Type: application/json" \ -H "X-Api-Key: YOUR_API_KEY" \ -d '{ "pincode": "123456" }' ``` **Response:** ```json { "success": true } ``` Choose a unique 6-digit PIN that you can remember. This PIN is required to authorize blockchain transactions. Store it securely and never share it. ### Save backup codes [#save-backup-codes] Generate backup recovery codes in case you lose access to your PIN: ```bash curl -X POST "https://your-platform.example.com/api/auth/wallet/secret-codes/generate" \ -H "Content-Type: application/json" \ -H "X-Api-Key: YOUR_API_KEY" \ -d '{}' ``` **Response:** ```json { "secretCodes": ["ABCD-1234-EFGH", "IJKL-5678-MNOP", "QRST-9012-UVWX", "YZAB-3456-CDEF", "GHIJ-7890-KLMN"], "rotated": false, "verificationId": "018f7b0a-1234-7890-abcd-ef0123456789" } ``` These codes are your only way to recover wallet access if you forget your PIN. Store them securely using a secrets manager (HashiCorp Vault, AWS Secrets Manager, etc.) or offline (printed in a secure location). Never store them in plaintext in code, logs, or unsecured cloud storage. Each code can only be used once. After securely storing the codes, send this request to confirm to the API that the secret codes have been stored securely. Echo the `verificationId` returned by `/generate` so the server can reject confirmations against a rotated-away set: ```bash curl -X POST "https://your-platform.example.com/api/auth/wallet/secret-codes/confirm" \ -H "Content-Type: application/json" \ -H "X-Api-Key: YOUR_API_KEY" \ -d '{ "stored": true, "verificationId": "018f7b0a-1234-7890-abcd-ef0123456789" }' ``` **Response:** ```json { "success": true } ``` ### Create on-chain identity [#create-on-chain-identity] Deploy your on-chain identity contract. This creates a smart contract that represents your identity on the blockchain: ```bash curl -X POST "https://your-platform.example.com/api/identity" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "walletVerification": { "secretVerificationCode": "123456", "verificationType": "PINCODE" } }' ``` **Response:** ```json { "id": "0x1234567890abcdef1234567890abcdef12345678", "account": { "id": "0xabcdef1234567890abcdef1234567890abcdef12", "contractName": null }, "isContract": false, "hasIdentity": false, "claims": [] } ``` The `id` is your identity contract address. The `hasIdentity` field will be `false` until the system is initialized and the identity is registered. ### Complete profile (optional) [#complete-profile-optional] Add your profile information for KYC purposes: ```bash curl -X POST "https://your-platform.example.com/api/user/usr_abc123/kyc/upsert" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "firstName": "Platform", "lastName": "Admin", "dob": "1990-01-15T00:00:00.000Z", "country": "BE", "residencyStatus": "resident", "nationalId": "123-45-6789" }' ``` **Response:** ```json { "changed": true, "currentVersion": { "id": "kyc_version_123", "number": 1, "contentHash": "0xabcdef...", "createdAt": "2024-01-15T10:00:00.000Z" }, "profile": { "id": "kyc_profile_123", "userId": "usr_abc123", "firstName": "Platform", "lastName": "Admin", "dob": "1990-01-15T00:00:00.000Z", "country": "BE", "residencyStatus": "resident", "nationalId": "123-45-6789", "createdAt": "2024-01-15T10:00:00.000Z", "updatedAt": "2024-01-15T10:00:00.000Z" } } ``` ### Initialize the system [#initialize-the-system] Deploy the core platform infrastructure. This is the most critical step and may take several minutes on public chains: ```bash curl -X POST "https://your-platform.example.com/api/systems" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "walletVerification": { "secretVerificationCode": "123456", "verificationType": "PINCODE" } }' ``` **Response:** ```json { "systemAddress": "0x9876543210fedcba9876543210fedcba98765432" } ``` This deploys: * **Identity Registry** — manages user identities * **Access Manager** — controls role-based permissions * **System Registries** — tracks deployed contracts * **Compliance Framework** — enforces regulatory rules #### Poll for deployment status [#poll-for-deployment-status] On public chains, monitor the deployment status: ```bash curl -X GET "https://your-platform.example.com/api/system/default" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response (key fields for status polling):** ```json { "id": "0x9876543210fedcba9876543210fedcba98765432", "status": "completed", "userIdentityRegistered": true, "canResume": false, "updatedAt": "2024-01-15T10:05:00.000Z", "deployedInTransaction": "0x00feaa...", "tokenFactoryRegistry": { "id": "0x...", "tokenFactories": [] } // ... additional contract registries and configuration } ``` The full response includes all deployed contract addresses, compliance modules, and access control settings. For polling, check these key fields: | Field | Value | Meaning | | ------------------------ | --------------- | ------------------------------------------------- | | `status` | `bootstrapping` | Deployment in progress | | `status` | `completed` | Deployment finished | | `userIdentityRegistered` | `true` | Your identity is registered in the system | | `canResume` | `true` | Deployment can be resumed (if it was interrupted) | The system is ready when `status` is `completed` and `userIdentityRegistered` is `true`. System initialization may take several minutes on public blockchains. Poll the status endpoint every few seconds until `status` is `completed` and `userIdentityRegistered` is `true`. ### Configure base currency [#configure-base-currency] Set the platform's base currency for asset valuations: ```bash curl -X POST "https://your-platform.example.com/api/settings" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "key": "BASE_CURRENCY", "value": "USD" }' ``` **Response:** ```json "USD" ``` Supported currencies: `EUR`, `USD`, `GBP`, `CHF`, `JPY`, `AUD`, `CAD`, `SGD`, `HKD` After setting the currency, sync exchange rates: ```bash curl -X POST "https://your-platform.example.com/api/exchange-rates/sync" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "force": false }' ``` **Response:** ```json { "success": true, "ratesUpdated": 132, "syncedAt": "2024-01-15T10:00:00.000Z" } ``` ### Enable asset types [#enable-asset-types] Deploy token factories for each asset type you want to support: ```bash curl -X POST "https://your-platform.example.com/api/system/token-factory" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "factories": [ { "type": "equity", "name": "Equity" } ], "walletVerification": { "secretVerificationCode": "123456", "verificationType": "PINCODE" } }' ``` **Response (abbreviated):** ```json { "id": "0x9876543210fedcba9876543210fedcba98765432", "tokenFactoryRegistry": { "id": "0xFDD42C4e9Bd27cCA35b38C2925f1ED7EAE1AAc5b", "tokenFactories": [ { "id": "0xA93F2f761DB1B3F8dc0ed6EcC9d38Bf15C8f562B", "name": "Equity", "typeId": "equity" } ] } // ... full system configuration } ``` The response returns the complete system configuration. The newly created factory appears in `tokenFactoryRegistry.tokenFactories`. Available asset types: * `equity` — Tokenized shares and stock * `bond` — Fixed income securities * `fund` — Investment fund units * `stablecoin` — Stable value tokens * `deposit` — Tokenized deposits * `real-estate` — Property tokens * `precious-metal` — Commodity-backed tokens ## Request parameters [#request-parameters] ### Sign up endpoint [#sign-up-endpoint] | Parameter | Type | Required | Description | | ---------- | ------ | -------- | ----------------------------------------------- | | `name` | string | Yes | Display name for the administrator | | `email` | string | Yes | Email address (must be unique) | | `password` | string | Yes | Account password (min 8 characters recommended) | ### API key creation [#api-key-creation] | Parameter | Type | Required | Description | | ----------- | ------ | -------- | --------------------------------------------------- | | `name` | string | Yes | Descriptive name for the API key | | `expiresIn` | number | No | Expiration time in seconds (default: no expiration) | | `metadata` | object | No | Custom metadata to attach to the key | ### Wallet verification object [#wallet-verification-object] | Field | Type | Required | Description | | ------------------------ | ------ | -------- | ----------------------------------- | | `secretVerificationCode` | string | Yes | The PIN code, OTP, or recovery code | | `verificationType` | string | Yes | `PINCODE`, `OTP`, or `SECRET_CODES` | ### KYC profile [#kyc-profile] | Parameter | Type | Required | Description | | ----------------- | ------ | -------- | --------------------------------------------------------- | | `userId` (path) | string | Yes | User ID to update KYC for (in URL path) | | `firstName` | string | No | First name (max 64 characters) | | `lastName` | string | No | Last name (max 64 characters) | | `dob` | string | No | Date of birth (ISO 8601, must be 18+ years old) | | `country` | string | No | ISO country code (e.g., `BE`, `DE`, `GB`) | | `residencyStatus` | string | No | `resident`, `non_resident`, `dual_resident`, or `unknown` | | `nationalId` | string | No | National ID number (max 50 characters) | ### Factory creation [#factory-creation] | Parameter | Type | Required | Description | | ----------------------------------- | ------ | -------- | ------------------------------------------------- | | `factories` | array | Yes | Array of factory objects (1-10 factories) | | `factories[].type` | string | Yes | Asset type (see available types above) | | `factories[].name` | string | Yes | Display name for the factory (max 100 characters) | | `factories[].factoryImplementation` | string | No | Custom factory implementation address | | `factories[].tokenImplementation` | string | No | Custom token implementation address | | `walletVerification` | object | Yes | Wallet verification (see above) | ## Best practices [#best-practices] ### Security considerations [#security-considerations] * **Secrets management** — Use a dedicated secrets manager (Vault, AWS Secrets Manager) for credentials * **Minimal exposure** — Delete passwords from memory after use; avoid logging sensitive data * **API key rotation** — Create short-lived API keys and rotate regularly * **PIN uniqueness** — Use a unique PIN not shared with other services * **Secure backup** — Store recovery codes in a secrets manager or offline; never in plaintext in version control or unsecured storage ### Transaction timing [#transaction-timing] * **Wait for confirmations** — On public chains, wait for sufficient block confirmations * **Poll status endpoints** — Use the `system/read` endpoint to monitor deployment status * **Handle timeouts** — System initialization can take 5-10 minutes on congested networks ### Error handling [#error-handling] * **Retry with backoff** — Implement exponential backoff for transient failures * **Idempotency** — Most operations are idempotent; retrying is safe * **Transaction failures** — Check wallet has sufficient gas before operations ## Troubleshooting [#troubleshooting] | Issue | Solution | | ----------------------------- | ------------------------------------------------------------------------------------------ | | `401 Unauthorized` | Session expired or API key invalid. Re-authenticate or create a new API key. | | `403 Forbidden` | Account lacks required permissions. First user should have admin privileges automatically. | | `400 Email already exists` | Email is taken. Use a different email or sign in to existing account. | | `400 Invalid pincode` | PIN must be exactly 6 digits. | | Identity creation fails | Ensure wallet has sufficient gas for contract deployment. | | System initialization timeout | Normal on public chains. Poll `/api/system/default` until status is `deployed`. | | Factory creation fails | System must be fully deployed first. Check system status before creating factories. | | Transaction reverted | Verify PIN is correct. Check wallet has sufficient gas. | | API key not working | Verify key hasn't expired. Check `X-Api-Key` header is set correctly. | ## Related guides [#related-guides] * [Add Administrators](/docs/developer-guides/platform-setup/add-admins) — Grant administrative permissions via API * [Change Admin Roles](/docs/developer-guides/platform-setup/change-admin-roles) — Modify or revoke role assignments via API * [Getting Started](/docs/developer-guides/api-integration/getting-started) — API key setup and authentication * [API Reference](/docs/developer-guides/api-integration/api-reference) — Full OpenAPI specification * [First Administrator Setup (User Guide)](/docs/user-guides/platform-setup/first-admin-setup) — Web interface approach # Platform setup overview Source: https://docs.settlemint.com/docs/developer-guides/platform-setup/platform-overview API-based platform setup, administrator role management, and system configuration. This overview covers platform setup through the API, administrator role management, and key concepts for configuring your asset tokenization platform programmatically. Use this as a reference when automating platform administration, integrating with provisioning systems, or building custom tooling. Platform dashboard after initial setup For the web interface approach, see [Platform Overview (User Guide)](/docs/user-guides/platform-setup/platform-overview). ## Setup workflow [#setup-workflow] The platform setup follows this sequence: 1. **[First administrator setup](/docs/developer-guides/platform-setup/first-admin-setup)** - Initialize platform and create first admin 2. **[Add administrators](/docs/developer-guides/platform-setup/add-admins)** - Grant roles to team members 3. **[Change admin roles](/docs/developer-guides/platform-setup/change-admin-roles)** - Modify permissions as needed 4. **Configure compliance** - Set up verification framework and policies 5. **Create assets** - Begin tokenizing real-world assets ## Key concepts [#key-concepts] ### Platform vs asset administration [#platform-vs-asset-administration] * **Platform administrators** - Manage system-wide operations and configuration * **Asset administrators** - Control individual token operations (assigned per asset) * **Platform roles** - Apply across the entire platform * **Asset roles** - Specific to individual tokens ### Permission model [#permission-model] * **On-chain permissions** - All roles stored in smart contracts * **Principle of least privilege** - Grant minimum necessary permissions * **Separation of duties** - Divide critical functions among multiple admins * **Immediate effect** - Role changes take effect instantly This overview covers **platform administrator roles** only. Asset-specific roles (like Asset Operator, Custodian, Supply Management) are assigned per token and covered in asset creation guides. ## Available platform roles [#available-platform-roles] | Role | Description | Key Permissions | | ------------------------------- | ----------------------------- | -------------------------------------------------- | | **Permission manager** | Permission management | Manage other administrators' roles and permissions | | **System manager** | System configuration | Deploy contracts, manage system settings | | **Asset manager** | Asset creation and management | Create tokens, configure asset properties | | **Identity manager** | User administration | Invite users, manage user identities | | **Verification issuer** | Issue verifications | Create and manage verification records | | **Verification policy manager** | Trust framework configuration | Configure trusted issuers and verification topics | | **Compliance manager** | Global compliance rules | Set platform-wide compliance policies | | **Addon manager** | Platform extension management | Install and configure platform addons | ## Setup guides [#setup-guides] ### Platform initialization [#platform-initialization] * **[First Admin Setup](/docs/developer-guides/platform-setup/first-admin-setup)** - Create first administrator and initialize platform infrastructure ### Administrator management [#administrator-management] * **[Add Administrators](/docs/developer-guides/platform-setup/add-admins)** - Grant platform roles to team members * **[Change Admin Roles](/docs/developer-guides/platform-setup/change-admin-roles)** - Modify existing role assignments # Configure equity collateral Source: https://docs.settlemint.com/docs/developer-guides/runbooks/configure-equity-collateral Configure collateral backing requirements for equity tokens via the Collateral Compliance Module API, enforcing minimum reserve ratios before minting shares. The module validates that the token's identity holds sufficient collateral claims before allowing new tokens to be minted. The Collateral Compliance Module enforces collateral requirements for equity tokens by validating on-chain collateral claims before allowing mints. This ensures that equity shares are backed by verifiable reserves at a configurable ratio, providing transparency and trust for investors. ## Overview [#overview] The CollateralComplianceModule checks that the token contract's OnchainID identity holds a valid collateral claim before permitting minting operations. The claim must contain a collateral amount that meets or exceeds the required ratio relative to the post-mint total supply. **Key features:** * **Configurable proof topic** – Specify which ERC-735 claim topic represents collateral proofs * **Adjustable collateral ratio** – Set ratios from 0% (disabled) to 200% (over-collateralized) in basis points * **Additional trusted issuers** – Extend the global trusted issuer registry with token-specific issuers * **Mint-only enforcement** – Only validates minting operations; regular transfers are unrestricted **Use cases:** * Private equity funds requiring 100% asset backing * Over-collateralized structures (150-200%) for investor protection * Regulated securities requiring verified reserve attestations * Tokenized real estate with collateral claims representing property valuations *** ## Collateral configuration workflow [#collateral-configuration-workflow] This diagram shows the complete API workflow for configuring collateral requirements on an equity token: **API calls in this workflow:** * **GET /system/default** – Retrieve the system configuration including the Collateral Compliance Module address from the compliance module registry * **GET /system/claim-topics** – Retrieve the registered collateral claim topic ID from the system * **POST /token/:address/add-compliance-module** – Register Collateral Compliance Module using the retrieved address, topic ID, initial ratio, and trusted issuers * **PUT /token/:address/compliance-module-params** – Adjust collateral ratio or trusted issuers after deployment (optional) * **POST /token/:address/claim/issue** – Add collateral claim to the token's identity with amount and expiry * **GET /token/:address** – Verify module configuration and claim validity before minting *** ## Prerequisites [#prerequisites] Before configuring collateral requirements, ensure you have: 1. **Deployed equity token** – Created via `tokenCreate` with `type: "equity"` 2. **Governance role** – Required to add and configure compliance modules 3. **API key** – See [Getting started](/docs/developer-guides/api-integration/getting-started) 4. **Collateral claim infrastructure** – The token's OnchainID identity must receive collateral claims from a trusted issuer (covered in [Step 4](#step-4-issue-collateral-claim-to-token-identity)) **Note:** The Collateral Compliance Module address is retrieved dynamically from the system via `systemRead` (Step 1 of the workflow). *** ## Step 1: Get Collateral Compliance Module address [#step-1-get-collateral-compliance-module-address] Before adding the Collateral Compliance Module to your token, retrieve its deployed address from the system's compliance module registry. ### Retrieve module address from system [#retrieve-module-address-from-system] ```ts file=/src/examples/configure-equity-collateral.ts#L89-L106 body: { params: { typeId: "collateral", module: collateralModuleAddress, values: { proofTopic: collateralTopic.topicId, ratioBps: 10_000, trustedIssuers: [], }, }, walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Collateral module added (100% ratio)"); ``` **Expected result:** * Returns the deployed Collateral Compliance Module address from the system * This address is required when adding the module to your token in Step 3 **Key points:** * Use `"default"` as the `systemAddress` to get the system used by the dApp * The `complianceModuleRegistry` contains all registered compliance modules * Filter by `typeId === "collateral"` to find the collateral module * The `module` field contains the deployed contract address *** ## Step 2: Get collateral topic ID [#step-2-get-collateral-topic-id] Retrieve the registered collateral claim topic ID from the system. This ID identifies which ERC-735 claim topic represents collateral proofs. ### Retrieve claim topics [#retrieve-claim-topics] ```ts file=/src/examples/configure-equity-collateral.ts#L108-L117 await dalp.token.configureComplianceModule({ params: { tokenAddress: equityAddress }, body: { params: { typeId: "collateral", module: collateralModuleAddress, values: { proofTopic: collateralTopic.topicId, ratioBps: 15_000, trustedIssuers: [], ``` **Expected result:** * Returns the collateral topic ID registered in the system * This ID is required for the next step when configuring the compliance module *** ## Step 3: Add Collateral Compliance Module [#step-3-add-collateral-compliance-module] Add the Collateral Compliance Module to your equity token using `tokenAddComplianceModule`. This configures the module with the collateral topic ID, initial collateral ratio, and trusted issuers. ### Configuration parameters [#configuration-parameters] The module accepts three parameters via the `values` object: | Parameter | Type | Description | Example | | ---------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | | `proofTopic` | BigInt | ERC-735 claim topic ID for collateral proofs (use `keccak256("COLLATERAL")` or custom topic) | `"123456789..."` | | `ratioBps` | number | Collateral ratio in basis points. 0 = disabled, 10000 = 100%, 20000 = 200%. Required collateral = supply × ratio / 10000 | `10000` (100% backing) | | `trustedIssuers` | address\[] | **Optional.** Additional trusted issuers for the proof topic. Only required if the claim issuer is not already registered as a global trusted issuer for the collateral topic. These are merged with global and subject-specific issuers. | `[]` (use global) or `["0xABCD..."]` | ### TypeScript example [#typescript-example] ```ts file=/src/examples/configure-equity-collateral.ts#L119-L143 }, walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Collateral parameters updated (150% ratio)"); // Step 9: Unpause the equity await dalp.token.unpause({ params: { tokenAddress: equityAddress }, body: { walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Equity unpaused"); // Step 10: Issue collateral claim to token identity const expiryTimestamp = Math.floor(Date.now() / 1000) + 365 * 24 * 60 * 60; await dalp.token.claimIssue({ ``` **Expected result:** * The module is registered on the equity token's compliance contract * Future mints will validate collateral claims against the configured ratio * The token data is returned with updated compliance modules *** ## Step 4: Update collateral parameters (optional) [#step-4-update-collateral-parameters-optional] If you need to adjust the collateral ratio or trusted issuers after initial configuration, use `tokenSetComplianceModuleParams`. ### When to update parameters [#when-to-update-parameters] * **Increase collateral ratio** – Strengthen investor protections during market volatility * **Decrease collateral ratio** – Adjust after successful track record or regulatory changes * **Add trusted issuers** – Include additional attestors if they're not registered as global trusted issuers for the collateral topic * **Change proof topic** – Switch to a different claim topic (requires coordination with claim issuers) ### TypeScript example [#typescript-example-1] ```ts file=/src/examples/configure-equity-collateral.ts#L145-L168 body: { claim: { topic: "collateral", data: { amount: 1_500_000_000_000_000_000_000n, expiryTimestamp: expiryTimestamp.toString(), }, }, walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Collateral claim issued"); // Step 11: Mint shares (validates collateral) await dalp.token.mint({ params: { tokenAddress: equityAddress }, body: { recipients: [myWallet], amounts: [100n], walletVerification: { secretVerificationCode: pincode, ``` **Note:** Parameter updates take effect immediately for subsequent minting operations. Existing token holders are not affected. *** ## Step 5: Issue collateral claim to token identity [#step-5-issue-collateral-claim-to-token-identity] The Collateral Compliance Module validates claims on the **token's OnchainID identity**, not individual investor identities. Before minting, you must add a collateral claim to the token identity using the governance role. ### Claim structure [#claim-structure] Collateral claims contain two fields: * `amount` – Collateral amount as string (e.g., `"1000000000000000000000"` for 1000 tokens with 18 decimals) * `expiryTimestamp` – Unix timestamp as string when the claim expires (must be > current time) ### Issue the claim via API [#issue-the-claim-via-api] Use `tokenClaimIssue` to add a collateral claim to the token's identity. This requires the **governance role** on the token. ```ts file=/src/examples/configure-equity-collateral.ts#L191-L227 ``` **Key points:** * **No trusted issuer required** – The governance role authorizes claim issuance directly * **Automatic on-chain encoding** – The API handles ABI encoding of `(uint256 amount, uint256 expiry)` * **Claim ownership** – The claim is issued **to** the token's identity, **by** the caller's identity * **Validation** – The smart contract's `canAddClaim` function verifies the caller has the governance role *** ## Step 6: Verify configuration [#step-6-verify-configuration] After adding the module and issuing collateral claims, verify the configuration before attempting to mint. ### Check token compliance modules [#check-token-compliance-modules] ```ts file=/src/examples/configure-equity-collateral.ts#L183-L189 ``` **Note:** The generated OpenAPI client may not include all type information for compliance modules. Use `tokenRead` to retrieve the token data and inspect the compliance configuration. The full example script demonstrates the complete verification workflow. *** ## Understanding collateral validation [#understanding-collateral-validation] The Collateral Compliance Module performs validation during minting operations using a multi-step process. ### Validation steps [#validation-steps] 1. **Mint detection** – Module checks if `from == address(0)` (minting operation). Regular transfers skip validation. 2. **Ratio check** – If `ratioBps == 0`, collateral enforcement is disabled and minting proceeds. 3. **Identity resolution** – Module loads the token's OnchainID identity address from the token contract. 4. **Issuer aggregation** – Module builds a combined list of trusted issuers: * Global issuers from the trusted issuer registry for the proof topic * Subject-specific issuers authorized for the token identity * Additional issuers from the module configuration 5. **Claim search** – Module queries all claims on the token's identity matching the proof topic and validates: * Claim is issued by a trusted issuer * Claim signature is valid (verified with issuer's `isClaimValid`) * Claim data is properly ABI-encoded as `(uint256 amount, uint256 expiry)` * Claim has not expired (`expiry > block.timestamp`) 6. **Collateral calculation** – Module calculates required collateral using ceiling division: ```solidity uint256 postSupply = currentSupply + mintAmount; uint256 requiredCollateral = (postSupply * ratioBps + 9999) / 10000; ``` 7. **Comparison** – If `claimAmount >= requiredCollateral`, minting proceeds. Otherwise, the transaction reverts with `"ComplianceCheckFailed: Insufficient collateral for mint"`. ### Best claim selection [#best-claim-selection] If multiple valid collateral claims exist on the token's identity, the module uses the **highest collateral amount** to maximize the chance of passing validation. This allows issuers to maintain multiple attestations from different sources. *** ## Full example script [#full-example-script] Complete TypeScript script demonstrating collateral configuration and validation: ```ts file=/src/examples/configure-equity-collateral.ts import { createDalpClient } from "@settlemint/dalp-sdk"; async function main() { // Step 1: Create the SDK client const dalp = createDalpClient({ url: "https://your-platform.example.com", apiKey: "YOUR_API_KEY", }); const pincode = "YOUR_PINCODE"; // Step 2: Get your wallet address const me = await dalp.user.me({}); const myWallet = me.data.wallet; if (!myWallet) { throw new Error("No wallet found. Create a wallet first via the DALP dashboard."); } console.log("Wallet:", myWallet); // Step 3: Create equity token const equity = await dalp.token.create({ body: { type: "equity", name: "Collateralized Series A Shares", symbol: "CSAS", decimals: 0, countryCode: "840", priceCurrency: "USD", basePrice: "10.00", class: "COMMON_EQUITY", category: "VOTING_COMMON_STOCK", initialModulePairs: [], walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); if ("transactionId" in equity) { console.log("Equity creation queued:", equity.transactionId); return; } const equityAddress = equity.data.id; console.log("Equity created:", equityAddress); // Step 4: Grant token roles await dalp.token.grantRole({ params: { tokenAddress: equityAddress }, body: { account: myWallet, roles: ["supplyManagement", "emergency", "governance"], walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Roles granted"); // Step 5: Get Collateral Compliance Module address from system const system = await dalp.system.read({ params: { systemAddress: "default" } }); const registry = system.data.complianceModuleRegistry; if (!registry) { throw new Error("Compliance module registry not found. Ensure the system is fully deployed."); } const collateralModule = registry.complianceModules.find((m) => m.typeId === "collateral"); if (!collateralModule) { throw new Error( "Collateral compliance module not registered in system. Check system admin has deployed the module." ); } const collateralModuleAddress = collateralModule.module; console.log("Collateral module:", collateralModuleAddress); // Step 6: Get topic ID for collateral claims const topics = await dalp.system.claimTopics.list({ query: {} }); const collateralTopic = topics.data.find((t) => t.name === "collateral"); if (!collateralTopic) { throw new Error("Collateral topic not found in registry. Ensure the topic is registered by a claimPolicyManager."); } console.log("Collateral topic ID:", collateralTopic.topicId); // Step 7: Add Collateral Compliance Module await dalp.token.installComplianceModule({ params: { tokenAddress: equityAddress }, body: { params: { typeId: "collateral", module: collateralModuleAddress, values: { proofTopic: collateralTopic.topicId, ratioBps: 10_000, trustedIssuers: [], }, }, walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Collateral module added (100% ratio)"); // Step 8 (Optional): Update collateral parameters to 150% await dalp.token.configureComplianceModule({ params: { tokenAddress: equityAddress }, body: { params: { typeId: "collateral", module: collateralModuleAddress, values: { proofTopic: collateralTopic.topicId, ratioBps: 15_000, trustedIssuers: [], }, }, walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Collateral parameters updated (150% ratio)"); // Step 9: Unpause the equity await dalp.token.unpause({ params: { tokenAddress: equityAddress }, body: { walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Equity unpaused"); // Step 10: Issue collateral claim to token identity const expiryTimestamp = Math.floor(Date.now() / 1000) + 365 * 24 * 60 * 60; await dalp.token.claimIssue({ params: { tokenAddress: equityAddress }, body: { claim: { topic: "collateral", data: { amount: 1_500_000_000_000_000_000_000n, expiryTimestamp: expiryTimestamp.toString(), }, }, walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Collateral claim issued"); // Step 11: Mint shares (validates collateral) await dalp.token.mint({ params: { tokenAddress: equityAddress }, body: { recipients: [myWallet], amounts: [100n], walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Mint succeeded — collateral validation passed"); } await main(); ``` The full script above can be copied and adapted for a collateral-backed equity setup. *** ## Troubleshooting [#troubleshooting] ### "ComplianceCheckFailed: Insufficient collateral for mint" [#compliancecheckfailed-insufficient-collateral-for-mint] **Cause:** The collateral amount in the claim is less than the required amount for the requested mint. **Solution:** 1. Calculate required collateral: ```ts const currentSupply = 1000; // Current total supply const mintAmount = 500; // Requested mint const ratioBps = 10000; // 100% ratio const postSupply = currentSupply + mintAmount; // 1500 const required = Math.ceil((postSupply * ratioBps) / 10000); // 1500 ``` 2. Issue a new collateral claim with sufficient amount or update existing claim 3. Retry minting operation ### "Proof topic cannot be zero" [#proof-topic-cannot-be-zero] **Cause:** The `proofTopic` parameter is set to `0` when adding the module. **Solution:** Specify a valid ERC-735 claim topic ID (keccak256 hash of topic name or custom numeric ID). ### "Ratio cannot exceed 20000 (200%)" [#ratio-cannot-exceed-20000-200] **Cause:** The `ratioBps` parameter exceeds the maximum allowed value. **Solution:** Set `ratioBps` to a value between `0` and `20000`. For 200% over-collateralization, use `20000`. ### "Trusted issuer cannot be zero address" [#trusted-issuer-cannot-be-zero-address] **Cause:** The `trustedIssuers` array contains `0x0000000000000000000000000000000000000000`. **Solution:** Remove zero addresses from the `trustedIssuers` array or leave it empty to rely only on global trusted issuers. ### Mint succeeds but collateral not validated [#mint-succeeds-but-collateral-not-validated] **Cause:** The `ratioBps` is set to `0`, which disables collateral enforcement. **Solution:** Set `ratioBps` to a non-zero value (e.g., `10000` for 100% backing) via `tokenSetComplianceModuleParams`. ### Claim exists but validation fails [#claim-exists-but-validation-fails] **Cause:** The claim may be: * Issued by an untrusted issuer (not in global registry or module config) * Expired (`expiry <= block.timestamp`) * Improperly encoded (not matching `(uint256, uint256)` format) * Revoked or invalid according to issuer's `isClaimValid` function **Solution:** 1. Verify the issuer address is in the trusted issuer registry for the collateral topic 2. Check claim expiry timestamp is in the future 3. Confirm claim data encoding matches `abi.encode(amount, expiry)` 4. Verify issuer's `isClaimValid` returns true for the claim *** ## Next steps [#next-steps] * **[Identity and compliance system](/docs/architecture/security/identity-compliance)** – Understand claim-based verification and trusted issuer registries * **[Compliance module architecture](/docs/architecture/security/compliance)** – Learn how compliance modules integrate with token operations * **[Create and mint equities](/docs/developer-guides/runbooks/create-mint-equities)** – Deploy equity tokens with initial compliance configuration * **[API reference](/docs/developer-guides/api-integration/api-reference)** – Explore all compliance module endpoints # Deploy and mint a bond Source: https://docs.settlemint.com/docs/developer-guides/runbooks/create-mint-bonds Step-by-step guide for creating and minting bond tokens using the DALP TypeScript client. ## Prerequisites [#prerequisites] Before running these commands, you need to: 1. **Platform URL** - Know your DALP platform URL (e.g., `https://your-platform.example.com`) 2. **Deploy DALP** - Have a running DALP instance (local or hosted) 3. **Sign up** - Create a user account through the DALP UI with email/password 4. **Enable PINCODE** - Set up PINCODE during onboarding. Manage it from **Account → Wallet**. 5. **Admin role** - Your account must have `admin` role to grant system roles (Step 3) 6. **Stablecoin (denomination asset)** - Have a deployed stablecoin contract address. You can create one using the [Stablecoin Guide](/docs/developer-guides/runbooks/create-mint-stablecoins) or use an existing stablecoin contract address **Note:** Your wallet address is available via DALP while signing up, or you can get it from Step 2. You'll need your wallet address and a deployed stablecoin contract address (denomination asset) for this guide. *** ## Quick reference [#quick-reference] | Step | What | Method | | ---- | ------------------ | ---------------------------- | | 1 | Initialize client | `initializeClient` | | 2 | Get user info | `userMe` | | 3 | Grant system roles | Grant `tokenManager` role | | 4 | Create bond | `tokenCreate` (type: "bond") | | 5 | Grant token roles | `tokenGrantRole` | | 6 | Unpause | `tokenUnpause` | | 7 | Mint bonds | `tokenMint` | | 8 | Verify | `tokenHolders` | *** ## Bond token lifecycle flow [#bond-token-lifecycle-flow] This diagram shows the complete bond deployment and minting workflow: **Bond-specific requirements:** * **denominationAsset** – Stablecoin contract address for redemptions (required) * **faceValue** – Redemption value per bond at maturity * **maturityDate** – Future ISO timestamp when bonds become redeemable * **System roles** – Requires `tokenManager` before creation * **Token roles** – `supplyManagement` for minting, `emergency` for unpausing *** Bond creation in the Asset Designer ## Step-by-step commands [#step-by-step-commands] ### Step 1: Initialize client [#step-1-initialize-client] Initialize the OpenAPI client with your platform URL and API key. ```ts file=/src/examples/create-mint-bond.ts#L1-L23 import { createDalpClient } from "@settlemint/dalp-sdk"; async function main() { // Step 1: Create the SDK client // Replace these placeholders with your actual values const dalp = createDalpClient({ url: "https://your-platform.example.com", apiKey: "YOUR_API_KEY", }); const pincode = "YOUR_PINCODE"; const stablecoinAddress = "YOUR_STABLECOIN_CONTRACT_ADDRESS"; // Step 2: Get your wallet address const me = await dalp.user.me({}); const myWallet = me.data.wallet; if (!myWallet) { throw new Error("No wallet found. Create a wallet first via the DALP dashboard."); } console.log("Wallet:", myWallet); // Step 3: Set up system roles // Follow the Set Up Roles guide to: // - Grant yourself 'tokenManager' system role ```
Client helper implementation The `initializeClient` helper configures the generated OpenAPI client with your API key. Implementation: ```ts file=/src/examples/client.ts import { createDalpClient } from "@settlemint/dalp-sdk"; // Create the SDK client — replace with your actual values const dalp = createDalpClient({ url: "https://your-platform.example.com", apiKey: "sm_dalp_xxxxxxxxxxxxxxxx", }); // All methods are fully typed with auto-complete const me = await dalp.user.me({}); console.log("Wallet:", me.data.wallet); const tokens = await dalp.token.list({ query: {} }); console.log("Tokens:", tokens.data.length); ```
*** ### Step 2: check you're logged in [#step-2-check-youre-logged-in] ```ts file=/src/examples/create-mint-bond.ts#L25-L32 // Step 4: Create bond token const bond = await dalp.token.create({ body: { type: "bond", name: "Test Corporate Bond", symbol: "TCBD", decimals: 18, ``` **Save your wallet address** - you'll need it! *** ### Step 3: Set up system roles [#step-3-set-up-system-roles] **Required**: Grant yourself the necessary system roles. 1. **Grant system roles**: * `tokenManager` - Required to create tokens **Note:** Only users with `admin` role can grant system roles. If you don't have admin access, ask your system administrator to grant you the `tokenManager` role. *** ### Step 4: Create bond token [#step-4-create-bond-token] ```ts file=/src/examples/create-mint-bond.ts#L39-L67 walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); if ("transactionId" in bond) { console.log("Bond creation queued:", bond.transactionId); return; } const tokenAddress = bond.data.id; console.log("Bond created:", tokenAddress); // Step 5: Grant token roles await dalp.token.grantRole({ params: { tokenAddress }, body: { accounts: [myWallet], role: "supplyManagement", walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); await dalp.token.grantRole({ params: { tokenAddress }, body: { ``` **Parameters:** * `type`: Must be `"bond"` * `name`: Bond name (e.g., "Test Corporate Bond") * `symbol`: Bond symbol (e.g., "TCBD") * `decimals`: Usually `18` for bonds * `countryCode`: ISO country code (840 = USA, 056 = Belgium, 276 = Germany) * `cap`: Maximum supply using `toBigDecimal("1000000", 18)` = 1M bonds * `faceValue`: Redemption value per bond using `toBigDecimal("1000", 18)` = 1000 tokens * `maturityDate`: When the bond matures (ISO string) * `denominationAsset`: Your stablecoin contract address from the [stablecoin guide](/docs/developer-guides/runbooks/create-mint-stablecoins) * `initialModulePairs`: Compliance modules (empty array `[]` for basic setup) **Expected:** Returns bond data with `id` (contract address) **SAVE THE CONTRACT ADDRESS** from the response! You need it for Step 5. *** ### Step 5: Grant token roles [#step-5-grant-token-roles] **REQUIRED**: Grant yourself `supplyManagement` (for minting) and `emergency` (for unpausing) roles on the bond contract. **Note:** When you create a token, you automatically receive the `admin` role (which allows you to grant other roles) and the `governance` role. You must grant yourself `supplyManagement` and `emergency` roles before minting and unpausing. ```ts file=/src/examples/create-mint-bond.ts#L69-L83 role: "emergency", walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Roles granted"); // Step 6: Unpause the bond await dalp.token.unpause({ params: { tokenAddress }, body: { walletVerification: { secretVerificationCode: pincode, ``` **Expected:** The role grant transaction completes. Wait for transaction confirmation before proceeding to Step 6. *** ### Step 6: Unpause the bond [#step-6-unpause-the-bond] New tokens start paused. Unpause to enable transfers: ```ts file=/src/examples/create-mint-bond.ts#L85-L97 }, }, }); console.log("Bond unpaused"); // Step 7: Mint bonds — 100 bonds await dalp.token.mint({ params: { tokenAddress }, body: { recipients: [myWallet], amounts: [100_000_000_000_000_000_000n], walletVerification: { secretVerificationCode: pincode, ``` **Note:** This step requires the `emergency` role from Step 5. Make sure Step 5 completed successfully and the transaction was confirmed before unpausing. *** ### Step 7: Mint bonds [#step-7-mint-bonds] ```ts file=/src/examples/create-mint-bond.ts#L99-L113 }, }, }); console.log("Minted bonds"); // Step 8: Verify holders const holders = await dalp.token.holders({ params: { tokenAddress }, query: { limit: 200 } }); console.log("Holders:", holders.data); } await main(); ``` **Parameters:** * `tokenAddress`: Your bond contract address (in path) * `recipients`: Array of recipient wallet address(es) * `amounts`: Array of amounts using `toBigDecimal("100", 18)` = 100 bonds **Expected:** Returns transaction hash. **Note:** This step requires the `supplyManagement` role from Step 5. *** ### Step 8: Verify the mint [#step-8-verify-the-mint] ```ts file=/src/examples/create-mint-bond.ts#L115-L120 ``` **Expected:** Shows bond holders with their balances. *** ## Full script [#full-script] ```ts file=/src/examples/create-mint-bond.ts import { createDalpClient } from "@settlemint/dalp-sdk"; async function main() { // Step 1: Create the SDK client // Replace these placeholders with your actual values const dalp = createDalpClient({ url: "https://your-platform.example.com", apiKey: "YOUR_API_KEY", }); const pincode = "YOUR_PINCODE"; const stablecoinAddress = "YOUR_STABLECOIN_CONTRACT_ADDRESS"; // Step 2: Get your wallet address const me = await dalp.user.me({}); const myWallet = me.data.wallet; if (!myWallet) { throw new Error("No wallet found. Create a wallet first via the DALP dashboard."); } console.log("Wallet:", myWallet); // Step 3: Set up system roles // Follow the Set Up Roles guide to: // - Grant yourself 'tokenManager' system role // See: /docs/developer-guides/runbooks/setup-roles // Step 4: Create bond token const bond = await dalp.token.create({ body: { type: "bond", name: "Test Corporate Bond", symbol: "TCBD", decimals: 18, countryCode: "840", cap: 1_000_000_000_000_000_000_000_000n, faceValue: 1_000_000_000_000_000_000_000n, maturityDate: new Date("2026-12-31T23:59:59Z").toISOString(), denominationAsset: stablecoinAddress, initialModulePairs: [], walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); if ("transactionId" in bond) { console.log("Bond creation queued:", bond.transactionId); return; } const tokenAddress = bond.data.id; console.log("Bond created:", tokenAddress); // Step 5: Grant token roles await dalp.token.grantRole({ params: { tokenAddress }, body: { accounts: [myWallet], role: "supplyManagement", walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); await dalp.token.grantRole({ params: { tokenAddress }, body: { accounts: [myWallet], role: "emergency", walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Roles granted"); // Step 6: Unpause the bond await dalp.token.unpause({ params: { tokenAddress }, body: { walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Bond unpaused"); // Step 7: Mint bonds — 100 bonds await dalp.token.mint({ params: { tokenAddress }, body: { recipients: [myWallet], amounts: [100_000_000_000_000_000_000n], walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Minted bonds"); // Step 8: Verify holders const holders = await dalp.token.holders({ params: { tokenAddress }, query: { limit: 200 } }); console.log("Holders:", holders.data); } await main(); ``` *** Deployed bond token details ## Troubleshooting [#troubleshooting] **"Authentication missing"** → Check your API key in `initializeClient`. **"PINCODE\_INVALID"** → Reconfirm your PINCODE. **"USER\_NOT\_AUTHORIZED" / "tokenManager required"** → Grant the `tokenManager` role (requires admin access). **"Permission denied"** → Grant the required token roles (`supplyManagement`, `emergency`). **"Token is paused"** → Make sure Step 6 (unpause) succeeded and you have `emergency` role. **"Invalid denomination asset"** → Verify your stablecoin contract address from the [stablecoin guide](/docs/developer-guides/runbooks/create-mint-stablecoins). **"Maturity date must be in the future"** → Use a future date. # Deploy and mint a deposit Source: https://docs.settlemint.com/docs/developer-guides/runbooks/create-mint-deposits Step-by-step guide for creating and minting deposit tokens using the DALP TypeScript client. ## Prerequisites [#prerequisites] Before running these commands, you need to: 1. **Platform URL** - Know your DALP platform URL (e.g., `https://your-platform.example.com`) 2. **Deploy DALP** - Have a running DALP instance (local or hosted) 3. **Sign up** - Create a user account through the DALP UI with email/password 4. **Enable PINCODE** - Set up PINCODE during onboarding. Manage it from **Account → Wallet**. 5. **Admin role** - Your account must have `admin` role to grant system roles (Step 3) 6. **Deposit product design** - Term structure, denomination asset, early withdrawal settings from the user guide 7. **Vault + denomination asset plan** - Know the ERC-20 currency you will lock in DALP vaults (USDC, institutional stablecoin, etc.) **Note:** Your wallet address is available via DALP while signing up, or you can get it from Step 2. You'll need your wallet address, deposit decimals that match the denomination asset (the user guide emphasizes matching decimals to avoid accounting drift), and `priceCurrency` (ISO currency code) and `basePrice` (string) for liability tracking dashboards. *** ## Quick reference [#quick-reference] | Step | What | Method | | ---- | ------------------------- | --------------------------------- | | 1 | Initialize Client | `initializeClient` | | 2 | Get user info | `userMe` | | 3 | Grant system roles | Grant `tokenManager` role | | 4 | Create deposit token | `tokenCreate` (type: `"deposit"`) | | 5 | Grant token roles | `tokenGrantRole` | | 6 | Unpause | `tokenUnpause` | | 7 | Mint deposit certificates | `tokenMint` | | 8 | Verify holders/liability | `tokenHolders` | *** ## Deposit token lifecycle flow [#deposit-token-lifecycle-flow] This diagram shows the complete deposit certificate deployment and minting workflow: **Deposit-specific requirements:** * **Identity registration** – Recipients must have registered identities before minting * **Decimals alignment** – Match token decimals to denomination asset to prevent accounting drift * **Liability tracking** – Configure `priceCurrency` and `basePrice` for dashboard integration * **System roles** – Requires `tokenManager` before creation * **Token roles** – `supplyManagement` for minting, `emergency` for unpausing *** ## Step-by-step commands [#step-by-step-commands] ### Step 1: Initialize Client [#step-1-initialize-client] Initialize the OpenAPI client with your platform URL and API key. ```ts file=/src/examples/create-mint-deposit.ts#L1-L22 import { createDalpClient } from "@settlemint/dalp-sdk"; async function main() { // Step 1: Create the SDK client const dalp = createDalpClient({ url: "https://your-platform.example.com", apiKey: "YOUR_API_KEY", }); const pincode = "YOUR_PINCODE"; // Step 2: Get your wallet address const me = await dalp.user.me({}); const myWallet = me.data.wallet; if (!myWallet) { throw new Error("No wallet found. Create a wallet first via the DALP dashboard."); } console.log("Wallet:", myWallet); // Step 3: Set up system roles // Follow the Set Up Roles guide to: // - Grant yourself 'tokenManager' system role // - Register your identity (CRITICAL: required before minting) ```
Client helper implementation The `initializeClient` helper configures the generated OpenAPI client with your API key. Implementation: ```ts file=/src/examples/client.ts import { createDalpClient } from "@settlemint/dalp-sdk"; // Create the SDK client — replace with your actual values const dalp = createDalpClient({ url: "https://your-platform.example.com", apiKey: "sm_dalp_xxxxxxxxxxxxxxxx", }); // All methods are fully typed with auto-complete const me = await dalp.user.me({}); console.log("Wallet:", me.data.wallet); const tokens = await dalp.token.list({ query: {} }); console.log("Tokens:", tokens.data.length); ```
*** ### Step 2: check you're logged in [#step-2-check-youre-logged-in] ```ts file=/src/examples/create-mint-deposit.ts#L24-L31 // Step 4: Create deposit token const deposit = await dalp.token.create({ body: { type: "deposit", name: "12M USD CD", symbol: "CD12", decimals: 18, ``` **Save your wallet address** - you'll need it! *** ### Step 3: set up system roles [#step-3-set-up-system-roles] **REQUIRED**: Complete these steps to grant yourself the necessary system roles. 1. **Grant system roles**: * `tokenManager` - Required to create tokens * `claimPolicyManager` or `complianceManager` - Optional, if you plan to automate collateral attestation or custom compliance 2. **Register identity**: * **CRITICAL**: You must register your identity before minting deposit tokens. This is required by the ERC-3643 standard. * Use `systemIdentityCreate` to register your wallet address in the identity registry. * If minting to other recipients, they must also be registered before Step 7. **Note:** Only users with `admin` role can grant system roles. If you don't have admin access, ask your system administrator to grant you the `tokenManager` role. *** ### Step 4: create deposit token [#step-4-create-deposit-token] ```ts file=/src/examples/create-mint-deposit.ts#L39-L64 }, }, }); if ("transactionId" in deposit) { console.log("Deposit creation queued:", deposit.transactionId); return; } const tokenAddress = deposit.data.id; console.log("Deposit created:", tokenAddress); // Step 5: Grant token roles await dalp.token.grantRole({ params: { tokenAddress }, body: { accounts: [myWallet], role: "supplyManagement", walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); await dalp.token.grantRole({ params: { tokenAddress }, body: { ``` **Parameters:** * `type`: Must be `"deposit"` * `name`: Deposit name (e.g., "12M USD CD") * `symbol`: Deposit symbol (e.g., "CD12") * `decimals`: Match the denomination asset decimals (guide section "Denomination asset selection") * `countryCode`: ISO country code (840 = USA, 056 = Belgium, 276 = Germany) * `priceCurrency`: ISO currency code (e.g., "USD") * `basePrice`: Fiat price per certificate using `toBigDecimal("1", 18)` = USD 1.00 * `initialModulePairs`: Compliance modules (empty array `[]` for basic setup) **Expected:** Returns deposit data with `id` (contract address) **SAVE THE CONTRACT ADDRESS** from the response! You need it for Step 5. > Vault linking, denomination asset approvals, and early-withdrawal automation continue via the workflows described in the deposit user guide. This API guide covers contract deployment plus minting. *** ### Step 5: grant token roles [#step-5-grant-token-roles] **REQUIRED**: Grant yourself `supplyManagement` (for minting) and `emergency` (for unpausing) roles on the deposit contract. **Note:** When you create a token, you automatically receive the `admin` role (which allows you to grant other roles) and the `governance` role. You must grant yourself `supplyManagement` and `emergency` roles before minting and unpausing. ```ts file=/src/examples/create-mint-deposit.ts#L66-L80 role: "emergency", walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Roles granted"); // Step 6: Unpause the deposit await dalp.token.unpause({ params: { tokenAddress }, body: { walletVerification: { secretVerificationCode: pincode, ``` **Expected:** The role grant transaction completes. Wait for transaction confirmation before proceeding to Step 6. *** ### Step 6: unpause the deposit [#step-6-unpause-the-deposit] New tokens start paused. Unpause to enable transfers: ```ts file=/src/examples/create-mint-deposit.ts#L82-L94 }, }, }); console.log("Deposit unpaused"); // Step 7: Mint deposit certificates — 50,000 units // IMPORTANT: Recipient must be registered in identity registry await dalp.token.mint({ params: { tokenAddress }, body: { recipients: [myWallet], amounts: [50_000_000_000_000_000_000_000n], walletVerification: { ``` **Note:** This step requires the `emergency` role from Step 5. Make sure Step 5 completed successfully and the transaction was confirmed before unpausing. *** ### Step 7: mint deposit certificates [#step-7-mint-deposit-certificates] **IMPORTANT:** The recipient wallet address must be registered in the identity registry before minting. If you're minting to your own wallet, ensure your identity is registered (see Step 3). For other recipients, register them using `systemIdentityCreate`. ```ts file=/src/examples/create-mint-deposit.ts#L96-L111 verificationType: "PINCODE", }, }, }); console.log("Minted deposit certificates"); // Step 8: Verify holders const holders = await dalp.token.holders({ params: { tokenAddress }, query: { limit: 200 } }); console.log("Holders:", holders.data); } await main(); ``` **Parameters:** * `tokenAddress`: Your deposit contract address (in path) * `recipients`: Array of recipient wallet address(es) - **must be verified in identity registry** * `amounts`: Array of amounts using `toBigDecimal("50000", 18)` = 50,000 deposit certificates **Expected:** Returns transaction hash. **Note:** This step requires the `supplyManagement` role from Step 5. If you get "RecipientNotVerified" error, register the recipient wallet in the identity registry first using `systemIdentityCreate`. *** ### Step 8: verify the mint [#step-8-verify-the-mint] ```ts file=/src/examples/create-mint-deposit.ts#L113-L118 ``` **Expected:** Shows deposit holders with their balances. Balances will line up with depositor positions shown in the Asset Management view; claims include the base price used by treasury dashboards. *** ## Full script [#full-script] ```ts file=/src/examples/create-mint-deposit.ts import { createDalpClient } from "@settlemint/dalp-sdk"; async function main() { // Step 1: Create the SDK client const dalp = createDalpClient({ url: "https://your-platform.example.com", apiKey: "YOUR_API_KEY", }); const pincode = "YOUR_PINCODE"; // Step 2: Get your wallet address const me = await dalp.user.me({}); const myWallet = me.data.wallet; if (!myWallet) { throw new Error("No wallet found. Create a wallet first via the DALP dashboard."); } console.log("Wallet:", myWallet); // Step 3: Set up system roles // Follow the Set Up Roles guide to: // - Grant yourself 'tokenManager' system role // - Register your identity (CRITICAL: required before minting) // See: /docs/developer-guides/runbooks/setup-roles // Step 4: Create deposit token const deposit = await dalp.token.create({ body: { type: "deposit", name: "12M USD CD", symbol: "CD12", decimals: 18, countryCode: "840", priceCurrency: "USD", basePrice: "1.00", initialModulePairs: [], walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); if ("transactionId" in deposit) { console.log("Deposit creation queued:", deposit.transactionId); return; } const tokenAddress = deposit.data.id; console.log("Deposit created:", tokenAddress); // Step 5: Grant token roles await dalp.token.grantRole({ params: { tokenAddress }, body: { accounts: [myWallet], role: "supplyManagement", walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); await dalp.token.grantRole({ params: { tokenAddress }, body: { accounts: [myWallet], role: "emergency", walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Roles granted"); // Step 6: Unpause the deposit await dalp.token.unpause({ params: { tokenAddress }, body: { walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Deposit unpaused"); // Step 7: Mint deposit certificates — 50,000 units // IMPORTANT: Recipient must be registered in identity registry await dalp.token.mint({ params: { tokenAddress }, body: { recipients: [myWallet], amounts: [50_000_000_000_000_000_000_000n], walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Minted deposit certificates"); // Step 8: Verify holders const holders = await dalp.token.holders({ params: { tokenAddress }, query: { limit: 200 } }); console.log("Holders:", holders.data); } await main(); ``` *** Tokenized deposits listing ## Troubleshooting [#troubleshooting] **"Authentication missing"** → Check your API key in `initializeClient`. **"PINCODE\_INVALID"** → Reconfirm your PINCODE. **"USER\_NOT\_AUTHORIZED" / "tokenManager required"** → Grant the `tokenManager` role (requires admin access). **"Permission denied"** → Grant the required token roles (`supplyManagement`, `emergency`). **"Token is paused"** → Make sure Step 6 (unpause) succeeded and you have `emergency` role. **"RecipientNotVerified"** → The recipient wallet must be registered in the identity registry before minting. Register the recipient wallet using `systemIdentityCreate`. **"Invalid country code"** → Provide a numeric ISO 3166-1 code (`840`, `056`, `276`, etc.). **Mismatched decimals vs. denomination asset** → Align `decimals` with the ERC-20 you plan to lock in vaults as described in the deposit guide's "Denomination asset selection." ## Related [#related] * [Deposit use case](/docs/executive-overview/use-cases/deposits) * [Developer runbooks](/docs/developer-guides) # Deploy and mint equity Source: https://docs.settlemint.com/docs/developer-guides/runbooks/create-mint-equities Step-by-step guide for creating and minting equity tokens using the DALP TypeScript client. ## Prerequisites [#prerequisites] Before running these commands, you need to: 1. **Platform URL** - Know your DALP platform URL (e.g., `https://your-platform.example.com`) 2. **Deploy DALP** - Have a running DALP instance (local or hosted) 3. **Sign up** - Create a user account through the DALP UI with email/password 4. **Enable PINCODE** - Set up PINCODE during onboarding. Manage it from **Account → Wallet**. 5. **Admin role** - Your account must have `admin` role to grant system roles (Step 3) **Note:** Your wallet address is available via DALP while signing up, or you can get it from Step 2. When creating the equity token (Step 4), you'll need to choose: * **Equity class** - Type of equity (e.g., `COMMON_EQUITY` for common stock, `PREFERRED_EQUITY` for preferred stock) * **Equity category** - Specific classification (e.g., `VOTING_COMMON_STOCK` for voting shares, `DUAL_CLASS_SHARES` for dual-class structures) * **Optional ISIN** - International Securities Identification Number if your shares are registered (e.g., `"US0378331005"`) You can use an empty array `[]` for `initialModulePairs` to start with basic compliance and configure modules later. To add collateral requirements, see [Configure equity collateral](/docs/developer-guides/runbooks/configure-equity-collateral). *** ## Quick reference [#quick-reference] | Step | What | Method | | ---- | --------------------------- | -------------------------------- | | 1 | Initialize Client | `initializeClient` | | 2 | Get user info | `userMe` | | 3 | Grant system roles | Grant `tokenManager` role | | 4 | Create equity token | `tokenCreate` (type: `"equity"`) | | 5 | Grant token roles | `tokenGrantRole` | | 6 | Unpause token | `tokenUnpause` | | 7 | Mint shares | `tokenMint` | | 8 | Verify holders and metadata | `tokenHolders` | *** ## Equity token lifecycle flow [#equity-token-lifecycle-flow] This diagram shows the complete equity token deployment and minting workflow: **Equity-specific requirements:** * **Class and category** – Choose from platform taxonomy (`COMMON_EQUITY`, `PREFERRED_EQUITY`, `VOTING_COMMON_STOCK`, etc.) * **ISIN** – Optional international securities identifier for registered shares * **Decimals** – Use `0` for whole-share cap table parity or `18` for fractional ownership * **Identity registration** – Recipients must have registered identities before minting * **System roles** – Requires `tokenManager` before creation * **Token roles** – `supplyManagement` for minting, `emergency` for unpausing *** ## Step-by-step commands [#step-by-step-commands] ### Step 1: Initialize Client [#step-1-initialize-client] Initialize the OpenAPI client with your platform URL and API key. ```ts file=/src/examples/create-mint-equity.ts#L1-L22 import { createDalpClient } from "@settlemint/dalp-sdk"; async function main() { // Step 1: Create the SDK client const dalp = createDalpClient({ url: "https://your-platform.example.com", apiKey: "YOUR_API_KEY", }); const pincode = "YOUR_PINCODE"; // Step 2: Get your wallet address const me = await dalp.user.me({}); const myWallet = me.data.wallet; if (!myWallet) { throw new Error("No wallet found. Create a wallet first via the DALP dashboard."); } console.log("Wallet:", myWallet); // Step 3: Set up system roles // Follow the Set Up Roles guide to grant yourself 'tokenManager' // Step 4: Create equity token ```
Client helper implementation The `initializeClient` helper configures the generated OpenAPI client with your API key. Implementation: ```ts file=/src/examples/client.ts import { createDalpClient } from "@settlemint/dalp-sdk"; // Create the SDK client — replace with your actual values const dalp = createDalpClient({ url: "https://your-platform.example.com", apiKey: "sm_dalp_xxxxxxxxxxxxxxxx", }); // All methods are fully typed with auto-complete const me = await dalp.user.me({}); console.log("Wallet:", me.data.wallet); const tokens = await dalp.token.list({ query: {} }); console.log("Tokens:", tokens.data.length); ```
*** ### Step 2: check you're logged in [#step-2-check-youre-logged-in] ```ts file=/src/examples/create-mint-equity.ts#L24-L31 body: { type: "equity", name: "Series B Voting Shares", symbol: "SBVS", decimals: 18, countryCode: "840", priceCurrency: "USD", basePrice: "10.00", ``` **Save your wallet address** - you'll need it! *** ### Step 3: set up system roles [#step-3-set-up-system-roles] **REQUIRED**: Complete these steps to grant yourself the necessary system roles. 1. **Grant system roles**: * `tokenManager` - Required to create tokens * `complianceManager` - Optional, if your compliance flow requires custom claim topics (Reg D vs. Reg S, KYC tiers, ROFR modules) 2. **Register identity**: * **CRITICAL**: You must register your identity before minting equity tokens. This is required by the ERC-3643 standard. * Use `systemIdentityCreate` to register your wallet address in the identity registry. * If minting to other recipients, they must also be registered before Step 7. **Note:** Only users with `admin` role can grant system roles. If you don't have admin access, ask your system administrator to grant you the `tokenManager` role. *** ### Step 4: create equity token [#step-4-create-equity-token] ```ts file=/src/examples/create-mint-equity.ts#L38-L66 verificationType: "PINCODE", }, }, }); if ("transactionId" in equity) { console.log("Equity creation queued:", equity.transactionId); return; } const tokenAddress = equity.data.id; console.log("Equity created:", tokenAddress); // Step 5: Grant token roles await dalp.token.grantRole({ params: { tokenAddress }, body: { accounts: [myWallet], role: "supplyManagement", walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); await dalp.token.grantRole({ params: { tokenAddress }, body: { accounts: [myWallet], role: "emergency", ``` **Parameters:** * `type`: Must be `"equity"` * `name`: Equity name (e.g., "Series B Voting Shares") * `symbol`: Equity symbol (e.g., "SBVS") * `decimals`: Use `0` for whole-share cap table parity (per the equity user guide) or `18` for fractionalized ownership * `countryCode`: ISO country code (840 = USA, 056 = Belgium, 276 = Germany) * `priceCurrency`: ISO currency code (e.g., "USD") * `basePrice`: Reference valuation per share using `toBigDecimal("10", 18)` = USD 10.00 * `class`: One of the `equityClasses` values (`COMMON_EQUITY`, `PREFERRED_EQUITY`, `SECTOR_INDUSTRY_EQUITY`, etc.) * `category`: One of the `equityCategories` values (`VOTING_COMMON_STOCK`, `DUAL_CLASS_SHARES`, etc.) * `uniqueIdentifier`: Optional ISIN for reporting (omit for private or pre-ISIN rounds) * `initialModulePairs`: Compliance modules (empty array `[]` for basic setup) **Expected:** Returns equity data with `id` (contract address) **SAVE THE CONTRACT ADDRESS** from the response! You need it for Step 5. *** ### Step 5: grant token roles [#step-5-grant-token-roles] **REQUIRED**: Grant yourself `supplyManagement` (for minting) and `emergency` (for unpausing) roles on the equity contract. **Note:** When you create a token, you automatically receive the `admin` role (which allows you to grant other roles) and the `governance` role. You must grant yourself `supplyManagement` and `emergency` roles before minting and unpausing. ```ts file=/src/examples/create-mint-equity.ts#L68-L82 secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Roles granted"); // Step 6: Unpause the equity await dalp.token.unpause({ params: { tokenAddress }, body: { walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, ``` **Expected:** The role grant transaction completes. Wait for transaction confirmation before proceeding to Step 6. **Note:** Add `governance` to the roles array if you plan to change compliance modules or ROFR settings via API. *** ### Step 6: unpause the equity [#step-6-unpause-the-equity] New tokens start paused. Unpause to enable transfers: ```ts file=/src/examples/create-mint-equity.ts#L84-L96 }); console.log("Equity unpaused"); // Step 7: Mint shares — 100 shares await dalp.token.mint({ params: { tokenAddress }, body: { recipients: [myWallet], amounts: [100_000_000_000_000_000_000n], walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, ``` **Note:** This step requires the `emergency` role from Step 5. Make sure Step 5 completed successfully and the transaction was confirmed before unpausing. *** ### Step 7: mint shares [#step-7-mint-shares] **IMPORTANT:** The recipient wallet address must be registered in the identity registry before minting. If you're minting to your own wallet, ensure your identity is registered (see Step 3). For other recipients, register them using `systemIdentityCreate`. ```ts file=/src/examples/create-mint-equity.ts#L98-L112 }); console.log("Minted shares"); // Step 8: Verify holders const holders = await dalp.token.holders({ params: { tokenAddress }, query: { limit: 200 } }); console.log("Holders:", holders.data); } await main(); ``` **Parameters:** * `tokenAddress`: Your equity contract address (in path) * `recipients`: Array of recipient wallet address(es) - **must be verified in identity registry** * `amounts`: Array of amounts using `toBigDecimal("100", 18)` = 100 shares **Expected:** Returns transaction hash. **Note:** This step requires the `supplyManagement` role from Step 5. If you get "RecipientNotVerified" error, register the recipient wallet in the identity registry first using `systemIdentityCreate`. *** ### Step 8: verify holders [#step-8-verify-holders] ```ts file=/src/examples/create-mint-equity.ts#L114-L119 ``` **Expected:** Shows equity holders with their balances. You should see the recipient with the minted balance plus identity claims that include the price reference, share class, and any compliance metadata. *** ## Full script [#full-script] ```ts file=/src/examples/create-mint-equity.ts import { createDalpClient } from "@settlemint/dalp-sdk"; async function main() { // Step 1: Create the SDK client const dalp = createDalpClient({ url: "https://your-platform.example.com", apiKey: "YOUR_API_KEY", }); const pincode = "YOUR_PINCODE"; // Step 2: Get your wallet address const me = await dalp.user.me({}); const myWallet = me.data.wallet; if (!myWallet) { throw new Error("No wallet found. Create a wallet first via the DALP dashboard."); } console.log("Wallet:", myWallet); // Step 3: Set up system roles // Follow the Set Up Roles guide to grant yourself 'tokenManager' // Step 4: Create equity token const equity = await dalp.token.create({ body: { type: "equity", name: "Series B Voting Shares", symbol: "SBVS", decimals: 18, countryCode: "840", priceCurrency: "USD", basePrice: "10.00", class: "COMMON_EQUITY", category: "VOTING_COMMON_STOCK", uniqueIdentifier: "US0378331005", initialModulePairs: [], walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); if ("transactionId" in equity) { console.log("Equity creation queued:", equity.transactionId); return; } const tokenAddress = equity.data.id; console.log("Equity created:", tokenAddress); // Step 5: Grant token roles await dalp.token.grantRole({ params: { tokenAddress }, body: { accounts: [myWallet], role: "supplyManagement", walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); await dalp.token.grantRole({ params: { tokenAddress }, body: { accounts: [myWallet], role: "emergency", walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Roles granted"); // Step 6: Unpause the equity await dalp.token.unpause({ params: { tokenAddress }, body: { walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Equity unpaused"); // Step 7: Mint shares — 100 shares await dalp.token.mint({ params: { tokenAddress }, body: { recipients: [myWallet], amounts: [100_000_000_000_000_000_000n], walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Minted shares"); // Step 8: Verify holders const holders = await dalp.token.holders({ params: { tokenAddress }, query: { limit: 200 } }); console.log("Holders:", holders.data); } await main(); ``` *** Equity token with on-chain governance parameters ## Troubleshooting [#troubleshooting] **"Authentication missing"** → Check your API key in `initializeClient`. **"PINCODE\_INVALID"** → Reconfirm your PINCODE. **"USER\_NOT\_AUTHORIZED" / "tokenManager required"** → Grant the `tokenManager` role (requires admin access). **"Permission denied"** → Grant the required token roles (`supplyManagement`, `emergency`). **"Token is paused"** → Make sure Step 6 (unpause) succeeded and you have `emergency` role. **"RecipientNotVerified"** → The recipient wallet must be registered in the identity registry before minting. Register the recipient wallet using `systemIdentityCreate`. **"Invalid enum value" for `class` or `category`** → Use values from the platform taxonomy (`COMMON_EQUITY`, `PREFERRED_EQUITY`, `VOTING_COMMON_STOCK`, etc.). # Deploy and mint a fund Source: https://docs.settlemint.com/docs/developer-guides/runbooks/create-mint-funds Step-by-step guide for creating and minting fund tokens using the DALP TypeScript client. ## Prerequisites [#prerequisites] Before running these commands, you need to: 1. **Platform URL** - Know your DALP platform URL (e.g., `https://your-platform.example.com`) 2. **Deploy DALP** - Have a running DALP instance (local or hosted) 3. **Sign up** - Create a user account through the DALP UI with email/password 4. **Enable PINCODE** - Set up PINCODE during onboarding. Manage it from **Account → Wallet**. 5. **Admin role** - Your account must have `admin` role to grant system roles (Step 3) **Note:** Your wallet address is available via DALP while signing up, or you can get it from Step 2. You'll need it for Steps 3, 5, and 7. *** ## Quick reference [#quick-reference] | Step | What | Method | | ---- | ---------------------------- | ------------------------------ | | 1 | Initialize Client | `initializeClient` | | 2 | Get user info | `userMe` | | 3 | Grant system roles | Grant `tokenManager` role | | 4 | Create fund token | `tokenCreate` (type: `"fund"`) | | 5 | Grant token roles | `tokenGrantRole` | | 6 | Unpause token | `tokenUnpause` | | 7 | Mint fund units | `tokenMint` | | 8 | Verify balances & NAV claims | `tokenHolders` | *** ## Fund token lifecycle flow [#fund-token-lifecycle-flow] This diagram shows the complete fund token deployment and minting workflow: **Fund-specific requirements:** * **Class and category** – Choose from fund taxonomy (`LONG_SHORT_EQUITY`, `VENTURE_CAPITAL`, `MULTI_STRATEGY`, etc.) * **Management fee** – Set `managementFeeBps` between 0 and 10,000 basis points (e.g., 150 = 1.5% annual) * **NAV tracking** – Configure `priceCurrency` and `basePrice` for net asset value reporting * **Identity registration** – Recipients must have registered identities before minting * **System roles** – Requires `tokenManager` before creation * **Token roles** – `supplyManagement` for minting/redemptions, `emergency` for unpausing *** ## Step-by-step commands [#step-by-step-commands] ### Step 1: Initialize Client [#step-1-initialize-client] Initialize the OpenAPI client with your platform URL and API key. ```ts file=/src/examples/create-mint-fund.ts#L1-L22 import { createDalpClient } from "@settlemint/dalp-sdk"; async function main() { // Step 1: Create the SDK client const dalp = createDalpClient({ url: "https://your-platform.example.com", apiKey: "YOUR_API_KEY", }); const pincode = "YOUR_PINCODE"; // Step 2: Get your wallet address const me = await dalp.user.me({}); const myWallet = me.data.wallet; if (!myWallet) { throw new Error("No wallet found. Create a wallet first via the DALP dashboard."); } console.log("Wallet:", myWallet); // Step 3: Set up system roles // Grant yourself 'tokenManager' and register your identity // Step 4: Create fund token ```
Client helper implementation The `initializeClient` helper configures the generated OpenAPI client with your API key. Implementation: ```ts file=/src/examples/client.ts import { createDalpClient } from "@settlemint/dalp-sdk"; // Create the SDK client — replace with your actual values const dalp = createDalpClient({ url: "https://your-platform.example.com", apiKey: "sm_dalp_xxxxxxxxxxxxxxxx", }); // All methods are fully typed with auto-complete const me = await dalp.user.me({}); console.log("Wallet:", me.data.wallet); const tokens = await dalp.token.list({ query: {} }); console.log("Tokens:", tokens.data.length); ```
*** ### Step 2: check you're logged in [#step-2-check-youre-logged-in] ```ts file=/src/examples/create-mint-fund.ts#L24-L31 body: { type: "fund", name: "Global Growth Fund Class A", symbol: "GGFA", decimals: 18, countryCode: "056", priceCurrency: "USD", basePrice: "100.00", ``` **Save your wallet address** - you'll need it! *** ### Step 3: set up system roles [#step-3-set-up-system-roles] **REQUIRED**: Grant yourself the `tokenManager` system role to create tokens. 1. **Grant system roles**: * `tokenManager` - Required to create tokens 2. **Register identity**: **REQUIRED before minting** * Use `systemIdentityCreate` to register your wallet address in the identity registry. **Note:** Only users with `admin` role can grant system roles. If you don't have admin access, ask your system administrator to grant you the `tokenManager` role and register your identity. *** ### Step 4: Create fund token [#step-4-create-fund-token] ```ts file=/src/examples/create-mint-fund.ts#L39-L68 verificationType: "PINCODE", }, }, }); if ("transactionId" in fund) { console.log("Fund creation queued:", fund.transactionId); return; } const tokenAddress = fund.data.id; console.log("Fund created:", tokenAddress); // Step 5: Grant token roles await dalp.token.grantRole({ params: { tokenAddress }, body: { accounts: [myWallet], role: "supplyManagement", walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); await dalp.token.grantRole({ params: { tokenAddress }, body: { accounts: [myWallet], role: "emergency", walletVerification: { ``` **Note:** Ensure you have the `tokenManager` system role from Step 3. **Parameters:** * `type`: Must be `"fund"` * `name`: Fund name (e.g., "Global Growth Fund Class A") * `symbol`: Fund symbol (e.g., "GGFA") * `decimals`: Use `18` for fractional units (recommended) or `0` for whole units only * `countryCode`: ISO country code (840 = USA, 056 = Belgium, 276 = Germany) * `priceCurrency`: ISO currency code (e.g., "USD") * `basePrice`: Reference NAV per unit using `toBigDecimal("100", 18)` = USD 100.00 * `class`: One of the `fundClasses` values (`LONG_SHORT_EQUITY`, `ABSOLUTE_RETURN`, `REGIONAL`, etc.) * `category`: One of the `fundCategories` values (`MULTI_STRATEGY`, `VENTURE_CAPITAL`, `EQUITY_HEDGE`, etc.) * `managementFeeBps`: Management fee in basis points (0-10,000). `150` = 1.5% annual fee * `uniqueIdentifier`: Optional ISIN for reporting (omit for private or unregistered funds) * `initialModulePairs`: Compliance modules (empty array `[]` for basic setup) **Expected:** Returns fund data with `id` (contract address) **SAVE THE CONTRACT ADDRESS** from the response! You need it for Step 5. *** ### Step 5: Grant token roles [#step-5-grant-token-roles] **REQUIRED**: Grant yourself `supplyManagement` (for minting/redemptions) and `emergency` (for unpausing) roles on the fund contract. **Note:** When you create a token, you automatically receive the `admin` role (which allows you to grant other roles) and the `governance` role. You must grant yourself `supplyManagement` and `emergency` roles before minting and unpausing. ```ts file=/src/examples/create-mint-fund.ts#L70-L84 verificationType: "PINCODE", }, }, }); console.log("Roles granted"); // Step 6: Unpause the fund await dalp.token.unpause({ params: { tokenAddress }, body: { walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, ``` **Expected:** The role grant transaction completes. Wait for transaction confirmation before proceeding to Step 6. **Note:** Add `governance` to the roles array if you plan to update valuation or redemption parameters via API. *** ### Step 6: Unpause the fund [#step-6-unpause-the-fund] New tokens start paused. Unpause to enable transfers: ```ts file=/src/examples/create-mint-fund.ts#L86-L98 console.log("Fund unpaused"); // Step 7: Mint fund units — 100 units await dalp.token.mint({ params: { tokenAddress }, body: { recipients: [myWallet], amounts: [100_000_000_000_000_000_000n], walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, ``` **Expected:** Transaction completes and fund is unpaused. *** ### Step 7: Mint fund units [#step-7-mint-fund-units] **IMPORTANT:** The recipient wallet address must be registered in the identity registry before minting. If you're minting to your own wallet, ensure your identity is registered (see Step 3). For other recipients, register them using `systemIdentityCreate`. ```ts file=/src/examples/create-mint-fund.ts#L100-L114 console.log("Minted fund units"); // Step 8: Verify holders const holders = await dalp.token.holders({ params: { tokenAddress }, query: { limit: 200 } }); console.log("Holders:", holders.data); } await main(); ``` **Parameters:** * `tokenAddress`: Your fund contract address (in path) * `recipients`: Array of recipient wallet address(es) - **must be verified in identity registry** * `amounts`: Array of amounts using `toBigDecimal("100", 18)` = 100 units **Expected:** Returns transaction hash. **Note:** This step requires the `supplyManagement` role from Step 5. If you get "RecipientNotVerified" error, register the recipient wallet in the identity registry first using `systemIdentityCreate`. *** ### Step 8: Verify holders [#step-8-verify-holders] ```ts file=/src/examples/create-mint-fund.ts#L116-L121 ``` **Expected:** Shows fund holders with their balances. You should see the recipient with the minted balance plus identity claims (NAV, management fee, optional ISIN), mirroring the dashboard view. *** ## Full script [#full-script] ```ts file=/src/examples/create-mint-fund.ts import { createDalpClient } from "@settlemint/dalp-sdk"; async function main() { // Step 1: Create the SDK client const dalp = createDalpClient({ url: "https://your-platform.example.com", apiKey: "YOUR_API_KEY", }); const pincode = "YOUR_PINCODE"; // Step 2: Get your wallet address const me = await dalp.user.me({}); const myWallet = me.data.wallet; if (!myWallet) { throw new Error("No wallet found. Create a wallet first via the DALP dashboard."); } console.log("Wallet:", myWallet); // Step 3: Set up system roles // Grant yourself 'tokenManager' and register your identity // Step 4: Create fund token const fund = await dalp.token.create({ body: { type: "fund", name: "Global Growth Fund Class A", symbol: "GGFA", decimals: 18, countryCode: "056", priceCurrency: "USD", basePrice: "100.00", class: "LONG_SHORT_EQUITY", category: "MULTI_STRATEGY", managementFeeBps: 150, uniqueIdentifier: "BE0003796134", initialModulePairs: [], walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); if ("transactionId" in fund) { console.log("Fund creation queued:", fund.transactionId); return; } const tokenAddress = fund.data.id; console.log("Fund created:", tokenAddress); // Step 5: Grant token roles await dalp.token.grantRole({ params: { tokenAddress }, body: { accounts: [myWallet], role: "supplyManagement", walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); await dalp.token.grantRole({ params: { tokenAddress }, body: { accounts: [myWallet], role: "emergency", walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Roles granted"); // Step 6: Unpause the fund await dalp.token.unpause({ params: { tokenAddress }, body: { walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Fund unpaused"); // Step 7: Mint fund units — 100 units await dalp.token.mint({ params: { tokenAddress }, body: { recipients: [myWallet], amounts: [100_000_000_000_000_000_000n], walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Minted fund units"); // Step 8: Verify holders const holders = await dalp.token.holders({ params: { tokenAddress }, query: { limit: 200 } }); console.log("Holders:", holders.data); } await main(); ``` *** Fund token with investment category and management fee metadata ## Troubleshooting [#troubleshooting] * **"Authentication missing"** → Check your API key in `initializeClient`. * **"PINCODE\_INVALID"** → Reset or re-enter PINCODE from Account → Wallet. * **"tokenManager required"** → Grant the `tokenManager` role (requires admin access). * **"Basis points cannot exceed 10000"** → Keep `managementFeeBps` between 0 and 10,000. * **"Invalid enum value" for class/category** → Use values from `fundClasses`/`fundCategories` (e.g., `LONG_SHORT_EQUITY`, `VENTURE_CAPITAL`). * **"Token is paused"** → Run Step 6 and ensure `emergency` role is assigned. Wait for transaction confirmation after granting roles. * **"Permission denied"** during mint → Confirm `supplyManagement` role from Step 5. * **"RecipientNotVerified"** → Register the recipient wallet in the identity registry using `systemIdentityCreate`. ## Related [#related] * [Private equity use case](/docs/executive-overview/use-cases/private-equity) * [Developer runbooks](/docs/developer-guides) # Deploy and mint a stablecoin Source: https://docs.settlemint.com/docs/developer-guides/runbooks/create-mint-stablecoins Step-by-step guide for creating and minting stablecoin tokens using the DALP TypeScript client. ## Prerequisites [#prerequisites] Before running these commands, you need to: 1. **Platform URL** - Know your DALP platform URL (e.g., `https://your-platform.example.com`) 2. **Deploy DALP** - Have a running DALP instance (local or hosted) 3. **Sign up** - Create a user account through the DALP UI with email/password 4. **Enable PINCODE** - Set up PINCODE during onboarding. Manage it from **Account → Wallet**. 5. **Admin role** - Your account must have `admin` role to grant system roles (Steps 3a and 3d) **Note:** Your wallet address is available via DALP while signing up, or you can get it from Step 2. You'll need it for Steps 3a, 3d, 5, and 8. *** ## Quick reference [#quick-reference] | Step | What | Method | | ---- | ----------------------------- | ------------------------------------------------ | | 1 | Initialize Client | `initializeClient` | | 2 | Get user info | `userMe` | | 3 | Set up roles & trusted issuer | Grant `tokenManager`, `claimPolicyManager` roles | | 4 | Create stablecoin | `tokenCreate` (type: "stablecoin") | | 5 | Grant token roles | `tokenGrantRole` | | 6 | Unpause token | `tokenUnpause` | | 7 | Add collateral | `tokenUpdateCollateral` | | 8 | Mint tokens | `tokenMint` | | 9 | Verify | `tokenHolders` | *** ## Stablecoin token lifecycle flow [#stablecoin-token-lifecycle-flow] This diagram shows the complete stablecoin deployment and minting workflow, including the required collateral step: **Stablecoin-specific requirements:** * **Trusted issuer registration** – You must register as a trusted issuer for the `collateral` claim topic before adding collateral * **Collateral** – Add sufficient collateral via `tokenUpdateCollateral` before minting (Step 7) * **System roles** – Requires `tokenManager` and `claimPolicyManager` * **Token roles** – `supplyManagement` for minting, `emergency` for unpausing * **Collateral claim** – Amount must cover total supply based on configured collateral ratio *** ## Step-by-step commands [#step-by-step-commands] ### Step 1: Initialize Client [#step-1-initialize-client] Initialize the OpenAPI client with your platform URL and API key. ```ts file=/src/examples/create-mint-stablecoin.ts#L1-L23 import { createDalpClient } from "@settlemint/dalp-sdk"; async function main() { // Step 1: Create the SDK client const dalp = createDalpClient({ url: "https://your-platform.example.com", apiKey: "YOUR_API_KEY", }); const pincode = "YOUR_PINCODE"; // Step 2: Get your wallet address const me = await dalp.user.me({}); const myWallet = me.data.wallet; if (!myWallet) { throw new Error("No wallet found. Create a wallet first via the DALP dashboard."); } console.log("Wallet:", myWallet); // Step 3: Set up system roles and trusted issuer status // Grant yourself 'tokenManager' and 'claimPolicyManager' // Register as a trusted issuer for the 'collateral' claim topic // Step 4: Create stablecoin token ```
Client helper implementation details Here's what the helper does behind the scenes: ```ts file=/src/examples/client.ts import { createDalpClient } from "@settlemint/dalp-sdk"; // Create the SDK client — replace with your actual values const dalp = createDalpClient({ url: "https://your-platform.example.com", apiKey: "sm_dalp_xxxxxxxxxxxxxxxx", }); // All methods are fully typed with auto-complete const me = await dalp.user.me({}); console.log("Wallet:", me.data.wallet); const tokens = await dalp.token.list({ query: {} }); console.log("Tokens:", tokens.data.length); ``` **Why use the helper?** It handles: * `initializeClient(baseUrl, apiKey)` – One-time setup for API authentication * `toBigDecimal()` / `fromBigDecimal()` – Precise numeric value helpers * SDK function re-exports – All API endpoints as direct imports **Tip:** Copy the `client.ts` file from `src/examples/client.ts` to your project to use the helper!
*** ### Step 2: check you're logged in [#step-2-check-youre-logged-in] ```ts file=/src/examples/create-mint-stablecoin.ts#L25-L32 body: { type: "stablecoin", name: "Test USD Coin", symbol: "TUSD", decimals: 18, countryCode: "840", priceCurrency: "USD", basePrice: "1.00", ``` **Save your wallet address** - you'll need it! *** ### Step 3: set up system roles and trusted issuer status [#step-3-set-up-system-roles-and-trusted-issuer-status] **REQUIRED**: Complete these steps to grant yourself the necessary system roles and register as a trusted issuer for collateral. 1. **Grant system roles**: * `tokenManager` - Required to create tokens * `claimPolicyManager` - Required to register as a trusted issuer 2. **Register as trusted issuer**: * Register your identity for the `collateral` claim topic **Note:** Only users with `admin` role can grant system roles. If you don't have admin access, ask your system administrator to grant you these roles and register you as a trusted issuer. *** ### Step 4: create a stablecoin token [#step-4-create-a-stablecoin-token] ```ts file=/src/examples/create-mint-stablecoin.ts#L40-L57 if ("transactionId" in stablecoin) { console.log("Stablecoin creation queued:", stablecoin.transactionId); return; } const tokenAddress = stablecoin.data.id; console.log("Stablecoin created:", tokenAddress); // Step 5: Grant token roles await dalp.token.grantRole({ params: { tokenAddress }, body: { accounts: [myWallet], role: "supplyManagement", walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, ``` **Parameters:** * `type`: Must be `"stablecoin"` * `name`: Token name (e.g., "Test USD Coin") * `symbol`: Token symbol (e.g., "TUSD") * `decimals`: Usually `6` for stablecoins (like USDC) or `18` * `countryCode`: ISO country code (840 = USA, 056 = Belgium, 276 = Germany) * `priceCurrency`: ISO currency code for liability tracking (e.g., "USD", "EUR", "GBP") * `basePrice`: Token peg value using `from(value, precision)` helper * Example: `from("1.00", 2)` = $1.00 with 2 decimal precision * Used with `priceCurrency` for liability tracking dashboards * `initialModulePairs`: Compliance modules (empty array `[]` for basic setup) **Expected:** Returns token data with `id` (contract address) **SAVE THE CONTRACT ADDRESS** from the response! You need it for Step 5. *** ### Step 5: grant token roles [#step-5-grant-token-roles] **REQUIRED**: Grant yourself `supplyManagement` (for minting) and `emergency` (for unpausing) roles on the stablecoin contract. ```ts file=/src/examples/create-mint-stablecoin.ts#L69-L81 }, }); console.log("Roles granted"); // Step 6: Unpause the stablecoin await dalp.token.unpause({ params: { tokenAddress }, body: { walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, ``` *** ### Step 6: unpause the token [#step-6-unpause-the-token] ```ts file=/src/examples/create-mint-stablecoin.ts#L83-L94 console.log("Stablecoin unpaused"); // Step 7: Add collateral (Required for stablecoins) const expiryDate = new Date(); expiryDate.setFullYear(expiryDate.getFullYear() + 1); await dalp.token.updateCollateral({ params: { tokenAddress }, body: { amount: 1_000_000_000_000_000_000_000_000n, expiryTimestamp: expiryDate.toISOString(), walletVerification: { ``` *** ### Step 7: add collateral (required for stablecoins) [#step-7-add-collateral-required-for-stablecoins] **IMPORTANT**: Stablecoins require collateral before minting. You must complete Step 3 first to register as a trusted issuer for the collateral claim topic. ```ts file=/src/examples/create-mint-stablecoin.ts#L96-L112 verificationType: "PINCODE", }, }, }); console.log("Collateral added"); // Step 8: Mint stablecoins — 1000 TUSD await dalp.token.mint({ params: { tokenAddress }, body: { recipients: [myWallet], amounts: [1_000_000_000_000_000_000_000n], walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, ``` **Parameters:** * `amount`: Collateral amount in smallest unit (e.g., `1000000000000n` = 1,000,000 tokens with 6 decimals) * `expiryTimestamp`: Future date when collateral expires **Expected:** Returns token data with updated collateral **Note:** If you get an error about not being a trusted issuer, register as a trusted issuer for the collateral claim topic (see Step 3). *** ### Step 8: mint stablecoins [#step-8-mint-stablecoins] ```ts file=/src/examples/create-mint-stablecoin.ts#L114-L129 console.log("Minted stablecoins"); // Step 9: Verify holders const holders = await dalp.token.holders({ params: { tokenAddress }, query: { limit: 200 } }); console.log("Holders:", holders.data); } await main(); ``` **Parameters:** * `contract`: Your stablecoin contract address * `recipients`: Array with recipient wallet address(es) * `amounts`: Array with amounts in **smallest unit** * To mint X tokens with 6 decimals, use: `X * 10^6` * Example: `1 * 10^6` = 1 TUSD * Example: `1000 * 10^6` = 1000 TUSD * For 18 decimals: `X * 10^18` (e.g., `1 * 10^18` = 1 token) **Expected:** Returns token data with updated `totalSupply` *** ### Step 9: verify the mint [#step-9-verify-the-mint] ```ts file=/src/examples/create-mint-stablecoin.ts#L131-L136 ``` **Expected:** Shows token holders with their balances *** ## Full script [#full-script] **Complete working script** - Replace the variables at the top (`YOUR_PLATFORM_URL`, `YOUR_API_KEY`, `YOUR_PINCODE`) and run: **Important:** * Step 3 requires granting system roles and registering as a trusted issuer * Step 3 in the script is a placeholder comment—you must complete the role setup steps manually before continuing * It assumes you have `admin` role to grant system roles. If you don't have admin access, you'll need your system administrator to grant you the `tokenManager` and `claimPolicyManager` roles, and register you as a trusted issuer for collateral ```ts file=/src/examples/create-mint-stablecoin.ts import { createDalpClient } from "@settlemint/dalp-sdk"; async function main() { // Step 1: Create the SDK client const dalp = createDalpClient({ url: "https://your-platform.example.com", apiKey: "YOUR_API_KEY", }); const pincode = "YOUR_PINCODE"; // Step 2: Get your wallet address const me = await dalp.user.me({}); const myWallet = me.data.wallet; if (!myWallet) { throw new Error("No wallet found. Create a wallet first via the DALP dashboard."); } console.log("Wallet:", myWallet); // Step 3: Set up system roles and trusted issuer status // Grant yourself 'tokenManager' and 'claimPolicyManager' // Register as a trusted issuer for the 'collateral' claim topic // Step 4: Create stablecoin token const stablecoin = await dalp.token.create({ body: { type: "stablecoin", name: "Test USD Coin", symbol: "TUSD", decimals: 18, countryCode: "840", priceCurrency: "USD", basePrice: "1.00", initialModulePairs: [], walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); if ("transactionId" in stablecoin) { console.log("Stablecoin creation queued:", stablecoin.transactionId); return; } const tokenAddress = stablecoin.data.id; console.log("Stablecoin created:", tokenAddress); // Step 5: Grant token roles await dalp.token.grantRole({ params: { tokenAddress }, body: { accounts: [myWallet], role: "supplyManagement", walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); await dalp.token.grantRole({ params: { tokenAddress }, body: { accounts: [myWallet], role: "emergency", walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Roles granted"); // Step 6: Unpause the stablecoin await dalp.token.unpause({ params: { tokenAddress }, body: { walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Stablecoin unpaused"); // Step 7: Add collateral (Required for stablecoins) const expiryDate = new Date(); expiryDate.setFullYear(expiryDate.getFullYear() + 1); await dalp.token.updateCollateral({ params: { tokenAddress }, body: { amount: 1_000_000_000_000_000_000_000_000n, expiryTimestamp: expiryDate.toISOString(), walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Collateral added"); // Step 8: Mint stablecoins — 1000 TUSD await dalp.token.mint({ params: { tokenAddress }, body: { recipients: [myWallet], amounts: [1_000_000_000_000_000_000_000n], walletVerification: { secretVerificationCode: pincode, verificationType: "PINCODE", }, }, }); console.log("Minted stablecoins"); // Step 9: Verify holders const holders = await dalp.token.holders({ params: { tokenAddress }, query: { limit: 200 } }); console.log("Holders:", holders.data); } await main(); ``` *** ## Common country codes [#common-country-codes] * 840 = USA * 056 = Belgium * 276 = Germany * 826 = UK * 392 = Japan *** ## Amount calculations [#amount-calculations] For stablecoins with **6 decimals** (like USDC), use the formula: **`amount * 10^6`** Examples: * 1 TUSD = `1 * 10^6` = `1000000` * 10 TUSD = `10 * 10^6` = `10000000` * 100 TUSD = `100 * 10^6` = `100000000` * 1000 TUSD = `1000 * 10^6` = `1000000000` For tokens with **18 decimals**, use the formula: **`amount * 10^18`** Examples: * 1 token = `1 * 10^18` = `1000000000000000000` * 10 tokens = `10 * 10^18` = `10000000000000000000` To mint X tokens, calculate: `X * 10^decimals` (where decimals = 6 for stablecoins like USDC, or 18 for other tokens) *** Stablecoin listing with collateral information ## Troubleshooting [#troubleshooting] **"Authentication missing"** → Check your API key in `initializeClient`. **"PINCODE\_INVALID"** → Reconfirm the PIN from **Account → Wallet** (`/account/wallet`) **"USER\_NOT\_AUTHORIZED" / "tokenManager required"** → Grant the `tokenManager` role (requires admin access) **"Permission denied"** → Grant the required token roles (`supplyManagement`, `emergency`) **"Token is paused"** → Make sure Step 6 (unpause) succeeded and you have `emergency` role **"InsufficientCollateral"** → Make sure Step 7 (add collateral) succeeded. You must be a trusted issuer for the collateral claim topic. **"You are not a trusted issuer for topic(s): collateral"** → Register as a trusted issuer for the collateral claim topic (see Step 3) **"RecipientNotVerified"** → Your wallet needs an identity registered before it can receive tokens. Register using `systemIdentityCreate`. **Need `jq`?** Install with: `brew install jq` (Mac) or `apt install jq` (Linux) ## Related [#related] * [Stablecoin use case](/docs/executive-overview/use-cases/stablecoins) * [Developer runbooks](/docs/developer-guides) # Equity tokenization Source: https://docs.settlemint.com/docs/developer-guides/runbooks/equity-tokenization Complete walkthrough for setting up a multi-organization demo environment with ACME Holdings equity tokenization via API. This comprehensive example demonstrates tokenizing equity shares in a real-world scenario with multiple organizations, proper compliance workflows, and complete user management. Follow this guide to set up a fully functional demo environment using the platform APIs. ## Prerequisites [#prerequisites] Before starting this example, ensure your blockchain network has the core platform contracts deployed: | Contract | Purpose | Deployed by | | ------------------------- | ------------------------------------------------------------------------------ | ----------------- | | **DALP Directory** | Registry and discovery service for all platform instances and their components | Platform deployer | | **DALP Identity Factory** | Creates on-chain identity contracts for users | Platform deployer | | **DALP System Factory** | Deploys organization-specific system contracts | Platform deployer | These contracts are typically deployed during initial platform setup by your implementation team. They provide the foundation for all organizations to build upon. **Additional requirements:** * Platform URL (e.g., `https://your-platform.example.com`) * API access token with appropriate roles * For public chains: ETH or native tokens for transaction gas fees (see gas requirements below) * Email addresses for all demo participants **Gas requirements for public chains:** * **Daniel Admin**: Moderate amount for system deployment and permission management * **Clara Compliance**: Small amount for user registration and verification issuance * **Olivia Operator**: Moderate amount for asset creation, permissions, minting, and transfers * **Colin Collateral**: Small amount for issuing collateral verifications * **Invited users**: Small amount for identity creation (if using invitation flow) ## Scenario Overview [#scenario-overview] This demonstration tokenizes 100,000 shares of ACME Holdings S.A., a Luxembourg company, using a multi-organization platform operated by Digital Securities S.A. ### Organizations [#organizations] | Organization | Role | Domain | Jurisdiction | | ------------------------------------ | ----------------------- | --------------------------- | ------------ | | **Digital Securities S.A.** | Platform operator | digital-securities.example | Luxembourg | | **ACME Holdings S.A.** | Issuer (equity owner) | acme-holdings.example | Luxembourg | | **Guardian Collateral Services Ltd** | Collateral agent | guardian-collateral.example | (generic) | | **Exchange Ltd** | Exchange/vault provider | exchange.example | (generic) | ### Key Personas [#key-personas] **Platform Operators (Digital Securities S.A.)** | Persona | Name | Email | Purpose | | ------------------ | ---------------- | --------------------------------------------------------------------------------------------- | ------------------------------ | | Platform Admin | Daniel Admin | [admin@digital-securities.example](mailto:admin@digital-securities.example) | System setup and configuration | | Asset Operator | Olivia Operator | [operator@digital-securities.example](mailto:operator@digital-securities.example) | Asset creation and management | | Compliance Officer | Clara Compliance | [compliance-kyc@digital-securities.example](mailto:compliance-kyc@digital-securities.example) | KYC and regulatory compliance | **External Partners** | Organization | Persona | Name | Email | Purpose | | ---------------------------- | --------------------- | ---------------- | --------------------------------------------------------------------------------------------------- | ----------------------- | | ACME Holdings S.A. | Issuer Representative | Ian Issuer | [issuer@acme-holdings.example](mailto:issuer@acme-holdings.example) | Legal equity owner | | Guardian Collateral Services | Collateral Agent | Colin Collateral | [collateral-agent@guardian-collateral.example](mailto:collateral-agent@guardian-collateral.example) | Collateral verification | | Exchange Ltd | Vault Provider | NovaX Vault | [vault@exchange.example](mailto:vault@exchange.example) | Token custody | ### ACME Equity Token Specifications [#acme-equity-token-specifications] **Real-world instrument:** | Property | Value | | ---------------------- | ------------------------------- | | **Issuer** | ACME Holdings S.A. (Luxembourg) | | **Security type** | Ordinary shares | | **Total shares** | 100,000 | | **Reference currency** | EUR | | **Price per share** | €0.71 | **On-chain representation:** | Property | Value | | ------------------------ | ------------------------------------ | | **Token name** | ACME Holdings Equity | | **Symbol** | ACME | | **Decimals** | 0 (1 token = 1 share) | | **Maximum supply** | 100,000 | | **Asset class** | EQUITY | | **ISIN** | LU0000ACME01 | | **Initial distribution** | 80,000 to issuer, 20,000 to exchange | ## Implementation steps [#implementation-steps] ### Platform initialization [#platform-initialization] **As the first user (Daniel Admin)**, start by setting up the platform administrator who will initialize the entire system. **See [First Administrator Setup](/docs/developer-guides/platform-setup/first-admin-setup) for API documentation.** **Configure with these specific values:** * **Email**: [admin@digital-securities.example](mailto:admin@digital-securities.example) * **Organization name**: Digital Securities S.A. * **Base currency**: EUR * **Asset factories to enable**: Equity, ... (add whatever asset types you want to support) For public chains: Ensure Daniel's wallet has sufficient native tokens (ETH, MATIC, etc.) for system deployment transactions before proceeding. As the first user, Daniel Admin will deploy all system contracts including identity registry, access manager, and asset factories. ### Grant additional administrative permissions [#grant-additional-administrative-permissions] **Continue as Daniel Admin.** After platform setup, Daniel Admin has the minimum required roles (Permission manager + System Manager) following the principle of least privilege. For this demo setup, he needs additional roles to configure the system. **See [Change Admin Roles](/docs/developer-guides/platform-setup/change-admin-roles) for API documentation.** **Add these roles to Daniel Admin:** * **Add-on Manager**: Manage platform extensions and modules * **Identity Manager**: Required to invite other users ### Invite platform team [#invite-platform-team] **Continue as Daniel Admin.** Add the other platform operators and external partners who will participate in this demo. **See [Create Users](/docs/developer-guides/user-management/create-users) for API documentation.** **Platform operators:** 1. **Olivia Operator** ([operator@digital-securities.example](mailto:operator@digital-securities.example)) * **Responsibilities**: Creates tokens, manages corporate actions, handles minting/burning * **Also needs**: [Wallet verification](/docs/developer-guides/user-management/create-users#create-api-key-optional) (to send transactions) 2. **Clara Compliance** ([compliance-kyc@digital-securities.example](mailto:compliance-kyc@digital-securities.example)) * **Responsibilities**: Manages KYC/AML verification, registers identities, issues verifications * **Also needs**: [Wallet verification](/docs/developer-guides/user-management/create-users#create-api-key-optional) (to send transactions) **External organizations:** 3. **Ian Issuer** ([issuer@acme-holdings.example](mailto:issuer@acme-holdings.example)) * **Responsibilities**: Legal owner of equity being tokenized 4. **Colin Collateral** ([collateral-agent@guardian-collateral.example](mailto:collateral-agent@guardian-collateral.example)) * **Responsibilities**: Independent verification of collateral backing * **Also needs**: [Wallet verification](/docs/developer-guides/user-management/create-users#create-api-key-optional) (to send transactions) 5. **NovaX Vault** ([vault@exchange.example](mailto:vault@exchange.example)) * **Responsibilities**: Institutional vault for token custody ### Assign platform admins [#assign-platform-admins] **Continue as Daniel Admin.** After team onboarding, assign specific roles to platform admins. **See [Add Administrators](/docs/developer-guides/platform-setup/add-admins) for API documentation.** **Olivia Operator** needs these roles: * **Asset Manager** - Create and configure new assets **Why these roles:** Olivia should be able to manage assets. **Clara Compliance** needs these roles: * **Identity Manager** - Manage the users registry, which users are known. * **Verification Issuer** - Issue KYC and compliance verifications * **Compliance Manager** - Will be able to manage global compliance rules * **Verification Policy Manager** - Configure verification topics and trusted issuers **Why these roles:** Clara manages all regulatory compliance and user verification. **Colin Collateral** needs these roles: * **Verification Issuer** - Issue collateral verifications **Why this role:** Colin's only function is verifying collateral backing. ### Configure trusted issuers [#configure-trusted-issuers] **Log in as Clara Compliance** to set up the verification framework by designating who can issue which types of verifications. For public chains: Ensure Clara's wallet has native tokens for trusted issuer configuration transactions. **See [Configure Trusted Issuers](/docs/developer-guides/compliance/configure-trusted-issuers) for API documentation.** **Clara Compliance as trusted issuer for investor-level verifications:** * **Know Your Customer (KYC)** - She verifies all investor identities * **Accredited Investor** - For US qualified investor status (optional, if needed) * **Qualified Institutional Investor** - For institutional investors under EU rules (optional, if needed) * **Anti-Money Laundering** - Source of funds verification (optional, if needed) * **Professional Investor** - For MiFID professional investor classification (optional, if needed) **Why Clara issues these:** As Compliance Officer, she's responsible for all investor-related verification. **Colin Collateral as trusted issuer for collateral verification:** * **Collateral** - Verifies that sufficient collateral backs the token **Why Colin issues this:** Guardian Collateral Services is the independent third party verifying collateral. **Olivia Operator as trusted issuer for asset-level verifications:** * **Base Price** - Provides reference pricing for the asset * **Unique Identifier** - Manages ISIN and internal reference numbers * **Asset Classification** - Confirms asset category and type * **Asset Location** - Confirms jurisdiction of underlying asset (optional, needed for real-estate) **Why Olivia issues these:** As Asset Operator, she manages asset metadata and classification. ### Register investors in registry [#register-investors-in-registry] **Log in as Clara Compliance** and register investors so they can receive tokens. **See [Register User](/docs/developer-guides/user-management/register-user) for API documentation.** **Register Ian Issuer:** * Choose **Luxembourg** as jurisdiction **Register NovaX Vault:** * Choose **Luxembourg** as jurisdiction **Result:** Both users now show "Registered" status and can receive KYC verifications. Platform operators (Daniel, Olivia, Clara) don't need registration unless they'll hold tokens. ### Issue KYC verifications [#issue-kyc-verifications] **Continue as Clara Compliance** and issue KYC verifications to investors. **See [Verify KYC](/docs/developer-guides/compliance/verify-kyc) for API documentation.** **Verify Ian Issuer:** * Issue **Know Your Customer** verification **Verify NovaX Vault:** * Issue **Know Your Customer** verification **Result:** Both users now have KYC verifications and can receive ACME tokens. ### Create ACME Equity Token [#create-acme-equity-token] Create the equity token using the Asset Designer with full compliance configuration. For public chains: Ensure Olivia's wallet has native tokens for asset creation and permission management transactions. **See [Create Asset](/docs/developer-guides/asset-creation/create-asset) for API documentation.** **Log in as Olivia Operator and configure:** **Step 1: Asset Class** * Select **Flexible Income** class * Choose **Equity** type **Step 2: Basic Details** * **Name**: ACME Holdings Equity * **Symbol**: ACME * **Decimals**: 0 (whole shares only) * **Jurisdiction**: Luxembourg **Step 3: Asset Classification** * **Category**: Common Equity * **Class**: Common Equity * **Unique Identifier**: LU0000ACME01 (ISIN format) * **Internal Reference**: ACME-EQ-001 **Step 4: Pricing** * **Currency**: EUR * **Base Price**: 0.71 (€0.71 per share) **Step 5: Compliance Modules** Enable **Smart Identity Verification**: * Required verification: "Know Your Customer" * Purpose: Only KYC-verified users can receive assets Enable **Collateral Requirement** (optional): * Topic: "Collateral" * Ratio: 100% (full collateral backing) * Purpose: Ensures backing for tokenized equity ### Configure token permissions [#configure-token-permissions] After asset creation, Olivia has the minimum required roles (Permission manager + Governance) following the principle of least privilege. For this demo, she needs additional token-specific roles. **See [Change Asset Admin Roles](/docs/developer-guides/asset-servicing/change-asset-admin-roles) for API documentation.** **Assign Olivia Operator these additional roles:** * **Custodian** - Execute transfers and forced transfers * **Emergency** - Pause/unpause token operations * **Supply Management** - Mint and burn token supply ### Issue collateral verification [#issue-collateral-verification] Colin must issue a collateral verification before minting is possible. This is a requirement when collateral modules are enabled. For public chains: Ensure Colin's wallet has native tokens for issuing collateral verification transactions. **See [Collateral](/docs/developer-guides/compliance/collateral) for API documentation.** **Log in as Colin Collateral and issue verification with these specific details:** * **Amount**: 100000 (covering full supply) * **Expiration**: 1 year from now **Purpose:** This confirms Guardian Collateral Services has verified sufficient backing for the full token supply. ### Unpause the token [#unpause-the-token] **Log in as Olivia Operator** and activate the ACME asset. **See [Pause/Unpause Asset](/docs/developer-guides/asset-servicing/pause-unpause-asset) for API documentation.** Collateral verification and token unpausing can be done in any order. Both are needed before minting, but they don't depend on each other. **Why Olivia can unpause:** She has the Emergency role on the ACME token, which is required for pause/unpause operations. ### Mint and distribute ACME tokens [#mint-and-distribute-acme-tokens] **Continue as Olivia Operator** and mint tokens to the issuer. **See [Mint Assets](/docs/developer-guides/asset-servicing/mint-assets) for API documentation.** For public chains: Ensure Olivia's wallet has sufficient native tokens for minting transactions, which can be gas-intensive for large amounts. **Mint to ACME Holdings:** * **Recipient**: Ian Issuer (select from contacts or use wallet address) * **Amount**: 100000 (full token supply) * **Purpose**: Initial distribution to issuer ### Execute forced transfer to exchange [#execute-forced-transfer-to-exchange] **Continue as Olivia Operator.** Transfer 20,000 tokens from the issuer to the exchange using forced transfer capability. For public chains: Ensure Olivia's wallet has native tokens for the forced transfer transaction. **See [Forced Transfer](/docs/developer-guides/asset-servicing/forced-transfer) for API documentation.** **Transfer configuration:** * **From address**: ACME Holdings (select from contacts) * **To address**: NovaX Exchange (select from contacts) * **Amount**: 20000 * **Purpose**: Allocate tokens to exchange for secondary market custody **Why use forced transfer:** This demonstrates custodian capabilities to move tokens between verified parties, which is important for corporate actions and compliance scenarios. **Why Olivia can execute this:** She has the Custodian role on the ACME token, which is required for forced transfer operations. ## Troubleshooting [#troubleshooting] | Issue | Solution | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Minting fails | Ensure collateral verification covers mint amount (if enabled). Verify all recipients have required KYC verifications. Check operator has Supply Management role. Confirm wallet has sufficient gas tokens (ETH, MATIC, etc.) for public chains. | | User cannot receive assets | Verify user is registered in identity registry. Ensure user has valid KYC verification. Check token compliance requirements. Confirm user's wallet address is correct. | | Verifications not trusted | Verify issuer is configured as trusted for specific topic. Check verification data format and expiration. Ensure issuer transaction was confirmed. Refresh page after configuration changes. | | Role assignment fails | Confirm you have role management permissions. Verify target user completed onboarding. Ensure user belongs to your organization (for internal roles). Check transaction has sufficient gas tokens for public chains. | For additional support, consult the individual feature guides or contact your implementation team. # Create users Source: https://docs.settlemint.com/docs/developer-guides/user-management/create-users Create platform users with automatic wallet and identity setup. Includes workflows for passive token holders and active users with API keys. The "Create User" API allows you to directly create user accounts with automatic wallet and identity setup. Unlike invitations, this creates accounts immediately without sending emails. **When you want to send API requests as that user** (minting, transferring, managing assets), you also need to create an API key and set up wallet verification for them using admin impersonation. For the web interface approach, see the [user guide](/docs/user-guides/user-management/create-users). ## Prerequisites [#prerequisites] * Platform URL (e.g., `https://your-platform.example.com`) * API key from a user with the **Identity Manager** role (see [Getting Started](/docs/developer-guides/api-integration/getting-started) for API key setup) * Wallet verification method enabled on your account (e.g., pincode or 2FA) * For users needing API access: Platform **admin** role (along with Identity Manager) ## When to use Create Users [#when-to-use-create-users] ### Recommended scenarios [#recommended-scenarios] * **Demo preparation** - Setting up users quickly for demonstrations * **Testing environments** - Creating test accounts with known credentials * **Passive token holders** - Users who only receive tokens and don't perform on-chain actions * **Training scenarios** - Preparing accounts for workshops or training ### When to use invitations instead [#when-to-use-invitations-instead] * **Production environments** - Users should control their own credentials * **Security-sensitive setups** - Users should generate their own wallets Creating users through this method does not send automatic emails. The administrator manages all account details and can share credentials manually as needed. ## User creation process [#user-creation-process] ### Prepare API request [#prepare-api-request] Construct the API request with the user's name and email: ```bash curl -X POST "https://your-platform.example.com/api/user/create" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "John Doe", "email": "john.doe@example.com", "walletVerification": { "secretVerificationCode": "123456" } }' ``` ### Execute API call [#execute-api-call] When you execute the request, the platform: 1. **Validates your permissions** - Confirms you have Identity Manager role 2. **Verifies your wallet** - Uses your pincode/2FA to authorize the blockchain transaction 3. **Creates the user account** - Generates a new account with a random password 4. **Deploys blockchain components** - Creates wallet (EOA) and deploys identity contract Creating a user triggers on-chain transactions to generate the wallet and deploy the identity contract. This may take a few seconds to confirm. ### Handle response [#handle-response] A successful response returns the created user with wallet and identity addresses: ```json { "id": "usr_abc123", "name": "John Doe", "email": "john.doe@example.com", "wallet": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "identity": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890" } ``` ### Verify user creation [#verify-user-creation] Confirm the user was created by querying the user list or fetching the user directly: ```bash # Search for the user by email curl -X GET "https://your-platform.example.com/api/user/search?query=john.doe@example.com" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response:** ```json [ { "id": "usr_abc123", "name": "John Doe", "wallet": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "role": "member" } ] ``` The user should appear with the wallet address from the creation response. ### Create API key (optional) [#create-api-key-optional] If this user needs to send API requests (minting, transferring, managing assets), create an API key for them using admin impersonation. Creating API keys for other users requires the platform **admin** role (along with Identity Manager). The first platform user automatically receives this role. When creating an API key for a user, you must include the organization ID in the metadata. Users created via this method belong to your organization (the admin's organization), so use your organization ID. **Get your organization ID:** If you followed the [First Administrator Setup](/docs/developer-guides/platform-setup/first-admin-setup) guide, you saved your organization ID in Step 2. If you need to retrieve it, call: ```bash curl -X GET "https://your-platform.example.com/api/auth/organization/list" \ -H "X-Api-Key: YOUR_ADMIN_API_KEY" ``` **Response:** ```json [ { "name": "Financial Institution S.A.", "slug": "financial-institution-sa", "logo": null, "createdAt": "2025-12-23T14:38:07.082Z", "metadata": null, "id": "org_abc123" } ] ``` Use the `id` field (`org_abc123` in this example) when creating the API key below. **Impersonate the user:** ```bash curl -i -c cookies.txt -X POST "https://your-platform.example.com/api/auth/admin/impersonate-user" \ -H "X-Api-Key: YOUR_ADMIN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "userId": "usr_abc123" }' ``` Use the `id` from the user creation response. The session cookie is saved to `cookies.txt`. **Create API key while impersonated:** ```bash curl -b cookies.txt -X POST "https://your-platform.example.com/api/auth/api-key/create" \ -H "Content-Type: application/json" \ -H "Origin: https://your-platform.example.com" \ -d '{ "name": "User Operations Key", "expiresIn": 31536000, "metadata": { "organizationId": "org_abc123" } }' ``` **Response:** ```json { "name": "User Operations Key", "start": "sm_atk", "prefix": "sm_dalp_", "key": "sm_dalp_xxxxxxxxxxxxxxxx", "userId": "usr_abc123", "enabled": true, "rateLimitEnabled": true, "rateLimitTimeWindow": 60000, "rateLimitMax": 10000, "requestCount": 0, "expiresAt": "2026-01-15T10:00:00.000Z", "createdAt": "2025-01-15T10:00:00.000Z", "updatedAt": "2025-01-15T10:00:00.000Z", "permissions": { "organization": [], "member": [], "invitation": [], "team": [], "ac": ["read"], "setting": ["read", "list"], "system": ["read", "list"], "exchangeRates": ["read", "list"] }, "metadata": { "organizationId": "org_abc123" }, "id": "key_abc123" } ``` The full API key is shown only once. Store it securely—you'll need it for the next step if setting up wallet verification. API keys need organization context to access system endpoints. The `organizationId` in the metadata ensures the API key can authenticate properly when calling endpoints like identity read, user management, and asset operations. Without this metadata, the API key will fail with 401 errors when calling system endpoints. **Stop impersonation:** ```bash curl -b cookies.txt -X POST "https://your-platform.example.com/api/auth/admin/stop-impersonating" \ -H "Origin: https://your-platform.example.com" ``` ### Set up wallet verification (required for API access) [#set-up-wallet-verification-required-for-api-access] If this user needs to use their API key for blockchain transactions or system operations, set up wallet verification (PIN + secret codes). **This requires an API key from the previous step.** Without wallet verification, the user's API key will fail with a NOT\_ONBOARDED error when calling most system endpoints. **Enable PIN:** Using the user's API key, configure their PIN: ```bash curl -X POST "https://your-platform.example.com/api/auth/wallet/pincode/enable" \ -H "Content-Type: application/json" \ -H "X-Api-Key: sm_dalp_user_xxxxxxxx" \ -d '{ "pincode": "123456" }' ``` **Response:** ```json { "success": true } ``` **Generate secret recovery codes:** Using the user's API key, generate backup recovery codes: ```bash curl -X POST "https://your-platform.example.com/api/auth/wallet/secret-codes/generate" \ -H "Content-Type: application/json" \ -H "X-Api-Key: sm_dalp_user_xxxxxxxx" \ -d '{}' ``` **Response:** ```json { "secretCodes": ["ABCD-1234-EFGH", "IJKL-5678-MNOP", "QRST-9012-UVWX", "YZAB-3456-CDEF", "GHIJ-7890-KLMN"], "rotated": false, "verificationId": "018f7b0a-1234-7890-abcd-ef0123456789" } ``` **Confirm secret codes were stored:** After securely storing the codes, confirm to the API that they've been saved. Echo the `verificationId` returned by `/generate` so the server can reject confirmations against a rotated-away set: ```bash curl -X POST "https://your-platform.example.com/api/auth/wallet/secret-codes/confirm" \ -H "Content-Type: application/json" \ -H "X-Api-Key: sm_dalp_user_xxxxxxxx" \ -d '{ "stored": true, "verificationId": "018f7b0a-1234-7890-abcd-ef0123456789" }' ``` **Response:** ```json { "success": true } ``` Store the user's API key, PIN, and secret recovery codes in a secrets manager (HashiCorp Vault, AWS Secrets Manager, etc.). If handing off to users, use secure channels like encrypted messaging or password manager invites. ## Request parameters [#request-parameters] | Parameter | Type | Required | Description | | -------------------- | ------ | -------- | --------------------------------------------------------------------------------------- | | `name` | string | Yes | Display name (person or company) | | `email` | string | Yes | Email address for the account (must be unique) | | `walletVerification` | object | Yes | Verification code for your account (the caller) to authorize the blockchain transaction | ### Wallet verification object [#wallet-verification-object] | Field | Type | Description | | ------------------------ | ------ | ---------------------------------------------- | | `secretVerificationCode` | string | 6-digit pincode or TOTP code | | `verificationType` | string | "PINCODE" (default), "SECRET\_CODES", or "OTP" | ## Response fields [#response-fields] | Field | Type | Description | | ---------- | ------ | ----------------------------------------- | | `id` | string | User ID | | `name` | string | Display name | | `email` | string | Email address | | `wallet` | string | Created wallet address (0x...) | | `identity` | string | Created identity contract address (0x...) | ## Post-creation steps [#post-creation-steps] 1. **Provide platform access** - Share the platform URL with users so they can access via "Forgot password" 2. **Enroll in registries** - Add users to relevant asset registries via [Register User](/docs/developer-guides/user-management/register-user) 3. **Assign administrative roles** - Grant platform admin roles if needed via [Add Administrators](/docs/developer-guides/platform-setup/add-admins) 4. **Verify identity** - Complete KYC verification for compliance if required User passwords are randomly generated and unknown to administrators. Users must use the "Forgot password" flow to set their own secure password. ## What gets created automatically [#what-gets-created-automatically] ### Account setup [#account-setup] * User account with email as username * Secure random password (must be reset) ### Blockchain components [#blockchain-components] * **Wallet address** - New externally owned account (EOA) * **Private key storage** - Securely encrypted * **On-chain identity** - Smart contract deployed * **Identity registration** - Registered as pending in identity registry ## Limitations and considerations [#limitations-and-considerations] * **Password reset required** - Extra step for user access * **No email notifications** - No clean invitation emails for users ## Comparison of user creation methods [#comparison-of-user-creation-methods] | Aspect | Create User | Invite User | | ----------------------- | -------------------- | -------------------------- | | **Speed** | Immediate | Depends on user response | | **API access** | Optional (via admin) | User creates own | | **Security** | Admin-managed | User-controlled | | **Best for** | Demos, test accounts | Production, external users | | **Admin role required** | For API key: Yes | No | | **Organization** | Your org only | Can create own orgs | | **Setup** | Automatic | User-guided | ## Troubleshooting [#troubleshooting] | Issue | Solution | | ---------------------------------------------- | ----------------------------------------------------------------- | | `401 Unauthorized` | API key is invalid, expired, or disabled | | `403 USER_NOT_AUTHORIZED` | Ensure your account has `identityManager` role (Identity Manager) | | `400 Email already exists` | User already exists; use `user.search` to find them | | `400 Invalid wallet verification` | Check your pincode/2FA code is correct | | `500 Insufficient gas` | Check blockchain node has sufficient funds for deployment | | `403 YOU_ARE_NOT_ALLOWED_TO_IMPERSONATE_USERS` | Requires platform admin role for API key creation | | Impersonation session expired | Re-impersonate the user before creating the API key | ## Related guides [#related-guides] * [Register User](/docs/developer-guides/user-management/register-user) - Register identity in jurisdiction * [First Administrator Setup](/docs/developer-guides/platform-setup/first-admin-setup) - Initial platform setup with admin role * [Add Administrators](/docs/developer-guides/platform-setup/add-admins) - Grant admin roles to additional users * [Getting Started](/docs/developer-guides/api-integration/getting-started) - API key setup basics # Register user Source: https://docs.settlemint.com/docs/developer-guides/user-management/register-user Register users in the identity registry via API for compliance tracking and token eligibility. This guide explains how to register users in the identity registry via API - a required step for compliance tracking and token eligibility. For the web interface approach, see the [user guide](/docs/user-guides/user-management/register-user). ## Prerequisites [#prerequisites] * Platform URL (e.g., `https://your-platform.example.com`) * API key from a user with the **Identity Manager** role (see [Getting Started](/docs/developer-guides/api-integration/getting-started) for API key setup) * Wallet verification method enabled on your account (e.g., pincode or 2FA) * User must have an on-chain identity (see [Create Users](/docs/developer-guides/user-management/create-users)) ## About identity registry [#about-identity-registry] The identity registry is an on-chain system that: * Tracks all verified identities for your organization * Links wallet addresses to identity contracts * Enforces jurisdictional rules ### Identity states [#identity-states] | State | Description | Can receive assets | | ------------------------ | ---------------------------------- | ------------------ | | **Pending Registration** | Has identity but not in registry | No | | **Registered** | In registry, awaiting verification | Depends on rules | | **Verified** | Has required verifications | Yes | ## Registering users [#registering-users] ### Identify user to register [#identify-user-to-register] The user must already have an on-chain identity. Query the user to get their wallet address: ```bash curl -X GET "https://your-platform.example.com/api/user/search?query=user@example.com" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response:** ```json [ { "id": "usr_abc123", "name": "John Doe", "wallet": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "role": "member" } ] ``` Save the `wallet` address from the first result for the registration step. ### Select jurisdiction [#select-jurisdiction] Choose the ISO 3166-1 alpha-2 country code for the user's jurisdiction. Country selection can be used in compliance rules but doesn't automatically apply specific rules. Choose based on user's legal residence or incorporation, not temporary location. ### Execute registration [#execute-registration] Register the identity in the selected jurisdiction: ```bash curl -X PUT "https://your-platform.example.com/api/system/identity" \ -H "X-Api-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "wallet": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "country": "BE", "walletVerification": { "secretVerificationCode": "YOUR_PINCODE" } }' ``` **Response:** ```json { "id": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890", "account": { "id": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "contractName": null }, "isContract": false, "hasIdentity": true, "registered": { "isRegistered": true, "country": "BE" }, "claims": [] } ``` ### Verify registration [#verify-registration] Query the identity to confirm the user is now registered. Use the `/api/system/identity/by-wallet` endpoint (not `/api/user/search`, which excludes identity data): ```bash curl -X GET "https://your-platform.example.com/api/system/identity/by-wallet/0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" \ -H "X-Api-Key: YOUR_API_KEY" ``` **Response when registered:** ```json { "id": "0x8e5F72f6E5b3B4D1234567890AbCdEf1234567890", "account": { "id": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "contractName": null }, "isContract": false, "hasIdentity": true, "registered": { "isRegistered": true, "country": "BE" }, "claims": [] } ``` **Key fields to verify:** * `registered.isRegistered` = `true` (confirms registration) * `registered.country` = the country code you specified (e.g., "BE") ## Request parameters [#request-parameters] | Parameter | Type | Required | Description | | -------------------- | ------ | -------- | ------------------------------------------------------------------- | | `wallet` | string | Yes | Wallet address of user to register (0x...) | | `country` | string | Yes | ISO 3166-1 alpha-2 country code (e.g., "BE", "DE") | | `walletVerification` | object | Yes | Your wallet verification (PINCODE or TOTP) to authorize transaction | ### Wallet verification object [#wallet-verification-object] | Field | Type | Description | | ------------------------ | ------ | ---------------------------------------------- | | `secretVerificationCode` | string | 6-digit pincode or TOTP code | | `verificationType` | string | "PINCODE" (default), "SECRET\_CODES", or "OTP" | ## Best practices [#best-practices] ### Jurisdiction selection [#jurisdiction-selection] * Use user's legal residence or incorporation * Verify jurisdiction through documentation * Consider tax and regulatory implications * Update if user's status changes ## Integration with compliance [#integration-with-compliance] ### Token transfers [#token-transfers] * Registered identities can receive assets (with proper verifications) * Non-registered identities are blocked from asset transactions ### Compliance modules [#compliance-modules] * Identity verification module checks registry registration * Transfer restrictions enforce registration requirements * Compliance reporting tracks registered vs. non-registered users ### Verification process [#verification-process] * Registration is prerequisite for issuing verifications * Verifications can only be added to registered identities * Multiple verifications can be issued per registered identity ## Troubleshooting [#troubleshooting] | Issue | Solution | | --------------------------------- | ------------------------------------------------------------------------------------------------------------- | | `401 Unauthorized` | API key is invalid, expired, or disabled | | `403 USER_NOT_AUTHORIZED` | Ensure your account has `identityManager` role (Identity Manager) | | `400 Identity not found` | User must have an on-chain identity; see [Create Users](/docs/developer-guides/user-management/create-users) | | `400 Identity already registered` | User already registered; proceed to KYC verification | | Invalid country code | Use ISO 3166-1 alpha-2 codes: BE, DE, GB, FR, etc. | | Cannot verify registration | Use `/api/system/identity/by-wallet/{wallet}` endpoint, not `/api/user/search` (which excludes identity data) | ## Related guides [#related-guides] * [Create Users](/docs/developer-guides/user-management/create-users) - Create user accounts with wallet and identity * [Verify KYC](/docs/developer-guides/compliance/verify-kyc) - Issue verifications after registration * [Getting Started](/docs/developer-guides/api-integration/getting-started) - API key setup * [Register User (User Guide)](/docs/user-guides/user-management/register-user) - Web interface approach # Compliance & security Source: https://docs.settlemint.com/docs/executive-overview/compliance-security DALP embeds regulatory controls into transaction execution so regulated asset programmes can enforce eligibility, identity, and transfer rules before state changes settle. ## Key terms [#key-terms] * **[ERC-3643](/docs/executive-overview/glossary#erc-3643)** – Token standard embedding compliance checks in transfer execution * **[OnchainID](/docs/executive-overview/glossary#onchainid)** – Decentralized identity protocol for portable investor credentials * **[HSM](/docs/executive-overview/glossary#hsm)** – Hardware Security Module providing tamper-resistant key storage * **[Multi-signature](/docs/executive-overview/glossary#multi-signature-wallet)** – Wallet requiring multiple approvals for transactions ## Why institutions need compliance-first architecture [#why-institutions-need-compliance-first-architecture] Compliance must be foundational, not an afterthought. Traditional securities have compliance processes that evolved over decades through painful, costly failures. Transfer agents verify eligibility before updating ownership records because regulators mandated it after investors lost money. Custodians enforce security controls before releasing assets because someone once walked away with millions. Risk committees approve platforms that demonstrate control, auditability, and regulatory alignment from day one, not platforms promising to "add compliance later." DALP was architected with this reality as the foundational requirement. Blockchain technology improves traditional processes only when the platform implements enforceable controls. For regulated assets, the important questions are practical: can the operator trace decisions, verify identity checks, and show that transfer rules were evaluated before settlement? DALP treats those controls as embedded infrastructure rather than optional application logic. ## Regulatory compliance by design [#regulatory-compliance-by-design] ### Compliance happens in the transfer path, not after it [#compliance-happens-in-the-transfer-path-not-after-it] Every token transfer in DALP executes through compliance checks before any state changes occur. This isn't a best practice recommendation you can skip under deadline pressure. It is how the ERC-3643 standard works at the protocol level. When Alice tries to transfer bond tokens to Bob, the transaction either completes fully or reverts completely. There's no partial transfer, no "pending compliance review," no cleanup operation to reverse an improper transaction. The smart contract verifies whether Alice's wallet links to a verified identity, whether Bob's wallet links to a verified identity, and whether Bob meets the specific eligibility requirements for this asset: accreditation status, jurisdiction restrictions, or institutional qualifications. The system checks whether the transfer violates holding limits, lockup periods, or concentration rules that prevent a single investor from dominating ownership. Finally, the contract confirms no asset-wide restrictions are currently in force, such as trading halts or emergency freezes imposed by compliance officers. If any check fails, the transfer reverts immediately. The blockchain state doesn't change, so there is no "undo" process for the failed transfer, and the transaction emits a reason code explaining which rule prevented execution. This ex-ante control gives operators an auditable record of the rule evaluation before settlement. Compliance policy templates provide jurisdiction-aware regulatory controls. ### Identity registry and portable credentials eliminate repetitive verification [#identity-registry-and-portable-credentials-eliminate-repetitive-verification] Investors verify identity once. DALP implements reusable digital identity that works across all assets an investor is eligible to hold. When an investor completes KYC/AML verification for one bond offering, that verification credential carries forward automatically to other investments on the platform. Investors don't waste time repeating identity checks; their digital identity accumulates verified credentials from trusted verifiers that travel with them. Identity credentials are attestations about investor status signed by trusted verifiers like KYC providers, legal counsel, or regulatory custodians. These credentials might prove someone completed KYC verification to Level 2 standards on a specific date, confirm an entity qualifies as an accredited investor under Regulation D, attest an individual resides in a permitted jurisdiction, or verify an institution meets qualified institutional buyer thresholds. Credentials are revocable if circumstances change, such as sanctions list additions, and have expiration dates requiring periodic renewal, like annual accreditation refresh requirements. The Identity Registry maintains the authoritative list of verified investors. Only registered, verified identities can hold compliant assets; unknown wallet addresses are rejected before a transfer can succeed. This architecture keeps identity checks in the transfer path without publishing investor identity files on-chain. Claims and proofs needed for token eligibility are available to the contracts, while detailed personally identifiable information remains with the trusted verifier or the deployment's off-chain systems. DALP uses the OnchainID protocol for decentralized identity credentials. See [Identity & Compliance Architecture](/docs/architecture/security/identity-compliance) for technical implementation details on claim structures, verification flows, and privacy-preserving designs. On-chain identity records make compliance credentials portable across assets. ### Jurisdictional rule templates standardize rule configuration [#jurisdictional-rule-templates-standardize-rule-configuration] Different jurisdictions have fundamentally different rules that you can't ignore. US Regulation D requires accredited investors for private placements with specific income and net worth thresholds. EU MiFID II has its own investor classification schemes that don't map cleanly to US definitions. Singapore MAS imposes fit-and-proper requirements under distinct regulatory frameworks. When regulations change, platforms need a controlled way to update the policies they apply to tokenized assets. DALP's Compliance Engine represents jurisdictional requirements as configurable rule modules that compliance officers activate for specific assets. The Rule Library provides jurisdictional templates as reusable starting points for common controls, while the editable live unit is the per-token or per-asset module parameters applied to an asset. Configurable compliance modules can enforce geographic restrictions through country whitelists or blacklists, investor limits capping the maximum number of holders or concentration per holder, transfer restrictions implementing lock-up periods and vesting schedules, holding requirements preventing transfers before minimum holding periods expire, and trading venue restrictions limiting transfers to approved exchanges. Treat rule changes for live assets as governed operational changes: updating a template does not mutate existing asset policy state, so teams review affected assets, explicitly re-apply or adjust the asset's module parameters, and verify the resulting transfer behavior before relying on the updated controls. ### Audit trails provide regulatory examination evidence [#audit-trails-provide-regulatory-examination-evidence] DALP generates the evidence trail that regulators require during examination. The platform creates immutable audit records for every compliance decision with the information regulators actually request during investigations. Each record captures the transaction ID identifying which transfer or operation was evaluated, the precise timestamp with block number showing exactly when the check occurred, the parties involved identifying who initiated the transaction and who the counterparty was, and which OnchainID claims were evaluated to verify identity and eligibility. The system records which compliance modules were checked and their results, whether the outcome allowed or denied the transaction with specific reason codes, and any administrator actions if manual overrides occurred, documenting who approved the override and their documented justification. Regulators reviewing your compliance program can query these records programmatically and export machine-readable reports showing every transaction attempt, every eligibility check, and every compliance decision over any requested time period. This isn't compliance theater where you produce documents after regulators ask uncomfortable questions. This is the evidence base that survives regulatory examination because it's cryptographically tamper-proof, automatically generated without human intervention, and complete from day one of operations. Your observability dashboards provide real-time visibility into compliance metrics such as transaction approval rates, common rejection reasons, and identity verification throughput so you spot patterns before they become regulatory problems. Operational dashboards surface active identities, trusted issuers, and verification activity in real time. ## Identity verification and KYC/AML integration [#identity-verification-and-kycaml-integration] ### How investor onboarding works [#how-investor-onboarding-works] DALP integrates with professional KYC/AML providers who specialize in identity verification, sanctions screening, politically exposed person (PEP) checks, and adverse media monitoring. You're not building verification infrastructure from scratch or trusting unverified self-attestations that regulators reject. The platform routes verification to specialists with established regulatory relationships and proven track records. Investors visit your white-labeled onboarding portal branded to your organization and provide personal information along with required documentation like passports and proof of address. The platform routes verification requests to integrated KYC providers who perform identity verification, sanctions checks, and PEP screening with results returning to your platform including risk scoring and attestations. Your compliance officer reviews results and either approves or rejects the application based on your risk policies. Approved investors receive identity claims added to their OnchainID and their wallet address gets registered in the Identity Registry, granting them access to interact with compliant assets. For institutional investors, the process extends to corporate KYC/KYB verification including beneficial ownership verification, entity structure documentation, authorized signer verification, and institutional due diligence questionnaires. The framework handles both individual and institutional onboarding through the same architectural pattern with different verification requirements. ### Ongoing monitoring maintains claim accuracy [#ongoing-monitoring-maintains-claim-accuracy] Identity verification isn't one-and-done because investor circumstances change over time. DALP supports periodic reverification requirements, continuous monitoring for adverse events like sanctions list additions, and claim expiration requiring renewal to ensure credentials remain current. If a KYC provider flags an investor due to sanctions list addition or criminal proceedings, their claims get revoked automatically and immediately. Revoked claims don't confiscate tokens or seize assets. Investors retain ownership of their holdings. However, they cannot transfer tokens until the compliance issue is resolved and their identity is re-verified. This is exactly how regulated securities should behave when investor eligibility changes. Accreditation claims have expiration dates because a verified accredited investor from 2022 might not qualify in 2025 due to changed financial circumstances. The platform enforces claim freshness requirements and prompts reverification when credentials near expiration, preventing expired credentials from enabling improper transfers. ### Privacy and data protection balance transparency with protection [#privacy-and-data-protection-balance-transparency-with-protection] Privacy regulations constrain how identity information is handled, while regulated assets still need identity and eligibility checks. DALP separates on-chain eligibility claims from off-chain identity documents so token contracts can verify required claims without exposing the underlying personal data on a public ledger. Detailed identity documents stay in the deployment's identity or verification systems, protected by the access controls and retention policies that the operator configures. On-chain transaction history remains part of the ledger. Off-chain personal data can be managed through the deployment's data-retention, access-control, and data-residency processes. Legal teams should map those controls to the privacy obligations that apply to the asset programme and jurisdiction. ## Security architecture and threat mitigation [#security-architecture-and-threat-mitigation] ### Multi-signature governance eliminates single points of failure [#multi-signature-governance-eliminates-single-points-of-failure] DALP assumes individual credentials will eventually be compromised through phishing, social engineering, device theft, or insider threats. Security is layered so that no single person can cause catastrophic loss even if their credentials are fully compromised. Multi-signature treasury controls require M-of-N approval for sensitive operations, ensuring multiple independent parties must coordinate for any high-risk action. Token minting or burning requires 2-of-3 administrator approval so no single admin can inflate supply or destroy value unilaterally. Large transfers require 3-of-5 treasury approval with thresholds set based on your risk tolerance. Emergency freeze operations require 2-of-2 executive approval ensuring both operational and compliance leadership agree before halting trading. Smart contract upgrades require 3-of-5 governance approval preventing a single compromised account from deploying malicious contract logic. Role-Based Access Control defines exactly what each role can do, following the principle of least privilege where users receive only the permissions necessary for their responsibilities. Super admins configure platform settings, assign roles, and manage global configurations but can't directly move assets. Compliance officers approve investors, manage identity credentials, and configure compliance rules but can't access treasury funds. Treasury managers process asset operations, handle distribution processing, and manage reserves but can't change compliance rules or approve new investors. Support agents have read-only access to assist users but cannot modify any state or approve transactions. Auditors have read-only access to all records for compliance review without operational permissions. ### Institutional custody controls protect high-value keys [#institutional-custody-controls-protect-high-value-keys] Private keys controlling substantial value require protection meeting the same standards banks apply to cryptographic material. DALP supports custody-aware signing routes that delegate transaction signing and broadcasting to configured providers instead of concentrating control in a single application hot wallet. Fireblocks and DFNS integrations can handle provider-native signing, broadcasting, nonce management, and approval polling. When a provider policy requires approval, DALP keeps the transaction in its tracked workflow while the provider-side approval completes or is rejected. Custody integrations help address the primary digital asset risk, private key theft or loss, by keeping signing inside provider-managed controls and approval policies. This addresses the single biggest objection risk committees raise when evaluating blockchain platforms: "How do we protect the keys?" See [Security Architecture](/docs/architecture/security) for the broader security model, authentication controls, and compliance architecture. ### Network security and monitoring detect attacks before damage occurs [#network-security-and-monitoring-detect-attacks-before-damage-occurs] Production security hardening includes TLS encryption for all API communications using modern cipher suites that prevent man-in-the-middle attacks. Default API authentication uses session and scoped API-key controls; deployments that install OAuth 2.0/OIDC plugins can add enterprise identity federation, but those plugins are not active by default. Rate limiting prevents abuse and denial-of-service attacks by throttling suspicious traffic patterns. IP allowlisting restricts administrative operations to known networks, preventing remote attacks even with stolen credentials. DDoS protection through Cloudflare or equivalent edge networks absorbs attack traffic before it reaches your infrastructure. Web application firewalls protect against common vulnerabilities like SQL injection and cross-site scripting. Secrets management via HashiCorp Vault or cloud provider secret stores prevents credentials from appearing in code or configuration files. Monitoring and incident response capabilities ensure you detect problems quickly and respond effectively. SIEM integration sends security events to centralized monitoring systems where correlation rules detect complex attack patterns. Alert rules identify suspicious patterns like repeated failed login attempts or unusual transaction volumes that might indicate account compromise. On-call rotations ensure 24/7 incident response capability so security events receive immediate attention regardless of time or day. Incident playbooks define precise response procedures for security events, eliminating confusion during high-stress situations. Quarterly tabletop exercises test incident response readiness and identify procedural weaknesses before real incidents occur. Your observability dashboards provide real-time security monitoring showing authentication attempt patterns, transaction velocity by user, rule evaluation latency that might indicate attacks, and system health metrics across all components. This visibility transforms security from reactive firefighting to proactive threat detection. ### Operational security and recovery procedures handle inevitable failures [#operational-security-and-recovery-procedures-handle-inevitable-failures] Bad things will happen because they always do in production systems. Systems will fail due to infrastructure problems or software bugs. Keys might be compromised through sophisticated attacks. Staff will make mistakes under pressure or through simple human error. DALP includes documented procedures for recovery that turn potential disasters into manageable incidents. Key compromise response procedures start by detecting compromise through automated monitoring or self-reporting, immediately freezing affected operations to prevent further damage, executing key rotation procedures to invalidate compromised credentials, investigating the scope of compromise to understand what attackers accessed, notifying affected parties per regulatory disclosure requirements, and documenting the incident for regulators and auditors with timeline and remediation steps. System failure recovery relies on high-availability deployment across multiple availability zones ensuring no single infrastructure failure causes total outage, automated failover for database and API services that activates without human intervention, regular database backups with tested restore procedures validated through periodic recovery drills, disaster recovery sites in separate geographic regions protecting against regional disasters, and documented recovery time objectives (RTO) and recovery point objectives (RPO) that set clear expectations for restoration timelines. Smart contract upgrade procedures use proxy patterns allowing logic updates without changing token contract addresses that would break integrations. Upgrades require multi-signature governance approval preventing unauthorized changes. Changes deploy first to testnet for validation with thorough testing before production exposure. Mainnet upgrades happen during planned maintenance windows with advance user notification. Rollback capability exists if issues are discovered post-deployment, enabling quick recovery from upgrade problems. ## Standards, certifications, and regulatory alignment [#standards-certifications-and-regulatory-alignment] ### Industry standards provide interoperability and best practices [#industry-standards-provide-interoperability-and-best-practices] DALP implements standards that enable interoperability with existing financial infrastructure and demonstrate alignment with industry best practices. The platform implements ERC-3643 for permissioned token transfers with embedded compliance and uses FIDO2/WebAuthn as an active strong-authentication standard that eliminates password vulnerabilities. OpenID Connect identity federation and OAuth 2.0 authorization for secure API access are available through installable authentication plugins and are not active by default in every deployment. These aren't checkbox features. They are architectural choices that determine how the platform integrates with broader financial infrastructure and whether institutions can adopt it within existing operational frameworks. ### Regulatory framework alignment supports global deployment [#regulatory-framework-alignment-supports-global-deployment] DALP provides configurable controls that regulated asset programmes can map to their jurisdiction-specific obligations. Compliance teams can configure identity requirements, transfer restrictions, investor limits, holding rules, and audit records for each asset programme. The same architecture can support different regulatory contexts, but the legal interpretation and final control design stay with the operator and its counsel. DALP does not make a deployment automatically compliant. It gives operators the technical control points and evidence trails they need to implement, test, and review their compliance model for the relevant jurisdiction. ### Security certifications demonstrate operational maturity [#security-certifications-demonstrate-operational-maturity] Organizations deploying DALP typically pursue certifications that demonstrate operational maturity to regulators, auditors, and institutional customers. SOC 2 Type II attestation covers service organization controls for security, availability, and confidentiality through independent audit. ISO 27001 information security management system certification demonstrates systematic security practices. Smart contract audits provide third-party security review of contract code by specialized blockchain security firms. Penetration testing through regular external security assessments identifies vulnerabilities before attackers exploit them. Regulatory examinations through cooperation with securities regulators reviewing operations validate that compliance claims match operational reality. These aren't automatic with the platform. They are organizational certifications your deployment pursues with DALP's architecture supporting the requirements rather than fighting against them. ## What this means for adoption [#what-this-means-for-adoption] Risk committees approve platforms that demonstrate control through evidence, not promises. DALP provides audit trails for eligibility checks, identity verification, and rule evaluation. Security controls with multi-signature operations and custody-aware signing routes reduce single-key risk. Configurable rule modules help teams align asset controls with their legal and operating model. Privacy-aware identity design keeps personal data off-chain while still letting token contracts verify required claims. When you present DALP to your risk committee, you're presenting a platform designed from inception for regulated financial instruments with proper institutional controls, not a developer experiment retrofitted with compliance features after launch. The observability dashboards give your operations team real-time visibility into system health, compliance metrics, and security events. The monitoring capabilities detect problems before they become regulatory incidents. Compliance controls are part of transaction execution. Security is built into the operating model. Privacy starts with clear separation between on-chain claims and off-chain identity records. ## Where to next [#where-to-next] * **[DALP overview](/docs/executive-overview/dalp-overview)** – Platform features and capabilities across the asset lifecycle * **[Architecture](/docs/architecture/overview)** – Technical details on system design and component interactions * **[Glossary](/docs/executive-overview/glossary)** – Key terms and definitions for compliance and security concepts # Platform capabilities Source: https://docs.settlemint.com/docs/executive-overview/dalp-overview DALP combines issuance, compliance, custody controls, settlement, servicing, exception handling, and operating evidence in one platform for regulated digital asset operations after launch. ## Key terms [#key-terms] * **[DALP](/docs/executive-overview/glossary#dalp)** – Digital Asset Lifecycle Platform, SettleMint's production DALP implementation * **[ERC-3643](/docs/executive-overview/glossary#erc-3643)** – Token standard for permissioned securities with embedded compliance * **[SMART Protocol](/docs/executive-overview/glossary#smart-protocol)** – SettleMint Adaptable Regulated Token protocol providing unified compliance * **[Multi-signature wallet](/docs/executive-overview/glossary#multi-signature-wallet)** – Wallet requiring multiple approvals for transactions ## What the Digital Asset Lifecycle Platform is [#what-the-digital-asset-lifecycle-platform-is] The SettleMint Digital Asset Lifecycle Platform (DALP) is working software for regulated digital asset operations after launch. Institutions use it to create assets, enforce compliance, control approvals, coordinate settlement, service assets, handle exceptions, and retain operating evidence through the asset lifecycle. Issuance is only the starting point. DALP provides the controls institutions need once an asset is live: compliance checks before transfers execute, role-based operations, custody-aware approval flows, settlement handling, servicing actions, emergency controls, and audit-ready records in one integrated platform. The full-stack includes smart contracts implementing compliance-aware tokens, a modern web application for issuers, administrators, and investors, backend APIs and services for integration, blockchain indexing for real-time ownership registries, database schemas for off-chain data, deployment infrastructure for production operations, and developer documentation and SDKs for customization. The platform is opinionated about architecture, unified lifecycle management with embedded compliance, but flexible about deployment. Run it on-premises, in your cloud infrastructure, or as dedicated SaaS. Deploy to Ethereum, Polygon, Hyperledger Besu, Quorum, or any EVM-compatible network. Customize the user interface, integrate with your systems, and extend the smart contracts for asset-specific requirements. Cross-asset insights summarize platform value across all deployed asset classes. ## Key features and capabilities [#key-features-and-capabilities] ### Regulated operations after launch [#regulated-operations-after-launch] DALP is designed for the operational phase that begins after an asset is issued. That phase includes transfer approvals, custody-policy boundaries, settlement coordination, servicing events, exception handling, emergency controls, production monitoring, and audit evidence. These controls sit in the same platform as asset configuration and compliance, so operations teams do not have to reconcile separate systems to understand what happened. For regulated institutions, this matters because most operating risk appears after the first asset goes live. The questions are practical: who can approve a transfer, which compliance rule blocked it, whether every settlement leg was approved, how an expired settlement is withdrawn, which emergency role paused an asset, and what evidence remains for compliance and audit review. DALP treats those as platform workflows, not manual back-office work. ### Complete lifecycle management [#complete-lifecycle-management] DALP's implementation of DALP principles delivers **integrated lifecycle management** that competitors can't match. These capabilities aren't add-ons or integrations, they're architected into the platform from the ground up: **The asset lifecycle flows through five integrated phases**: Issuance creates the token with embedded compliance from deployment. Compliance enforces rules at every transfer, validating identity claims and regulatory requirements. Custody secures assets in multi-signature vaults with maker-checker workflows. Settlement executes atomic transfers where cash and tokens move together or both revert. Servicing automates yield calculations, dividend distributions, and redemptions. Each phase references the same control plane, no reconciliation between vendors. **Delivery versus Payment (DvP)**: Atomic settlement ensures asset and cash transfer simultaneously or both revert. No settlement risk. No reconciliation. True T+0 finality. The XvP settlement system coordinates multi-party exchanges where every leg executes together, if any party's transfer fails, the entire settlement reverts. This eliminates counterparty risk and the need for trusted intermediaries. **Secure Treasury Vaults**: Multi-signature custody with role-based access control. Configurable quorum requirements ensure no single person can move assets unilaterally. The vault system provides maker-checker workflows where one admin proposes transactions and others approve before execution. Emergency pause capabilities protect against compromised accounts. Full audit trails track every proposal, approval, and execution. **Scheduled Yield Management**: Fixed yield schedules calculate dividend, interest, and coupon entitlements automatically without manual processing. Configure payment schedules once, and the platform calculates distributions on payment dates. Token holders claim their yields directly through smart contracts with cryptographic proof of entitlement. No spreadsheets, no reconciliation, no manual wire transfers. These three capabilities, **DvP settlement, vault custody, and scheduled yield management**, form the operational backbone that separates lifecycle platforms with DvP, vault custody, and yield management from basic token issuance tools. While competitors offer pieces through multiple vendors requiring integration, DALP delivers unified lifecycle management where these capabilities work together. ### Multi-asset support from day one [#multi-asset-support-from-day-one] Seven asset classes ship as ready-to-deploy templates, not future roadmap promises: **Bonds (Debt Instruments)**: Fixed or floating rate, with maturity dates, coupon payment schedules, redemption mechanics, and collateral tracking. The platform calculates interest entitlements on payment dates for token holders to claim, and handles redemption at maturity without manual reconciliation. **Equities (Company Shares)**: Common or preferred shares with voting rights, dividend distributions, cap table management, and shareholder governance. Voting power automatically derives from token holdings at snapshot blocks, and votes are tallied on-chain without spreadsheets. **Funds (Investment Units)**: Open-ended or closed-end fund structures with NAV tracking, management fee calculation, performance monitoring, subscription processing, and redemption workflows. The platform maintains real-time holder registries and automates fund administration that traditionally takes days. **Stablecoins (Fiat-Pegged Tokens)**: Tokenized representations of fiat currencies with reserve management, peg maintenance, minting controls tied to actual deposits, and burning processes for redemptions. Issuers get transparency into collateral ratios and automated compliance with reserve requirements. **Deposits (Certificates)**: Time-locked deposit certificates with collateral verification, maturity tracking, and interest accrual. These bridge traditional banking products into programmable assets with automated lifecycle management. Each asset type implements the SMART Protocol (SettleMint Adaptable Regulated Token), which means they all share compliance infrastructure, custody controls, and operational tooling despite having different economic terms and lifecycle events. **Every asset type gains access to DALP's lifecycle capabilities**: bonds use DvP for primary issuance and secondary trading, vaults for secure treasury management, and yield schedules for coupon calculations; equities use vaults for corporate treasury and yield schedules for dividend entitlement tracking; funds rely on DvP for subscription/redemption settlement and vaults for asset custody. The lifecycle management isn't separate, it's how these assets operate. Bond issuance and lifecycle servicing on a unified asset management console. ### Regulatory compliance embedded in the architecture [#regulatory-compliance-embedded-in-the-architecture] Compliance isn't a dashboard feature you turn on after deploying tokens. It's in the token's DNA through the ERC-3643 standard implementation. The Identity Registry maintains verified investor profiles with KYC/AML status, accreditation levels, and jurisdictional eligibility. An investor completes verification once, and their identity travels with them across all assets they're eligible to hold. The Compliance Engine evaluates every transfer before execution, checking whether the sender is verified, whether the recipient meets eligibility requirements, whether the transfer violates holding limits or lockup periods, and whether jurisdictional rules permit the transaction. Non-compliant transfers revert with clear reason codes explaining why. The Rule Library provides a configurable framework for jurisdiction-specific compliance. The platform supports templates for Regulation D and Regulation S (US), MiFID II and MiCA (Europe), MAS frameworks (Singapore), and FCA requirements (UK). Compliance officers configure rules through UI controls rather than writing smart contract code. The Audit Trail captures every decision: which rules were evaluated, which identity claims were verified, which administrators approved exceptions, with immutable timestamps and cryptographic proof. Regulators get machine-readable evidence, not manually compiled spreadsheets. ### Multi-layer security and custody [#multi-layer-security-and-custody] Multi-signature wallets require configurable quorum approval for treasury operations. No single person can move assets unilaterally. The platform enforces maker-checker workflows where one admin proposes a transaction and others approve before execution. Custody-aware signing routes let institutions delegate signing and transaction broadcasting to configured providers instead of relying on a single application hot wallet. Current DALP integrations cover Fireblocks and DFNS provider-native broadcasting, approval polling, and signer operations. Role-Based Access Control (RBAC) defines who can perform which operations: token creation, compliance approval, treasury transactions, administrative settings. Permissions map to organizational hierarchies with proper segregation of duties. Institutions can use those provider-side policy controls while DALP coordinates asset lifecycle workflows, transaction state, and confirmation tracking in the platform. The platform assumes keys will be stolen, employees will make mistakes, and external attacks will occur. Security is defense in depth: multiple layers that must all fail before assets are at risk. Equity issuance and lifecycle management using the same DALP control plane. ### Modern user experience across personas [#modern-user-experience-across-personas] The **Issuer Portal** walks asset managers through token creation with a multi-step wizard that handles configuration, compliance setup, legal documentation uploads, and deployment. Preview every setting before finalizing. Deploy to testnet first, validate behavior, then promote to mainnet when ready. The **Investor Portal** gives token holders real-time visibility into their holdings, transaction history, pending corporate actions, and upcoming events. No waiting for quarterly statements. No calling customer service to check your balance. Everything is transparent and current. The **Admin Console** centralizes operations: pending compliance approvals, KYC verifications, whitelist management, corporate action scheduling, reporting and analytics. Compliance officers and operations teams have purpose-built tools for their workflows. The **Developer Portal** provides API documentation, SDK downloads, code examples, sandbox environments for testing, and transaction status guidance for long-running blockchain operations. Developers can distinguish immediate responses that include data and transaction hashes from asynchronous responses that include a transaction ID, current status, and polling URL. Technical teams can integrate DALP into existing systems without reverse-engineering undocumented behavior. The Asset Console theme system lets operators align the browser experience with their institutional brand while keeping product workflows unchanged. Detailed branding controls are covered in the [Asset Console customization guidance](/docs/architecture/components/platform/asset-console#customization). ### Scalable architecture for production workloads [#scalable-architecture-for-production-workloads] The platform uses modern microservices architecture with independent scaling for each component. The web application, API server, blockchain indexer, and database tier scale independently based on load. TanStack-based frontend provides instant navigation and optimistic updates. Users don't wait for blockchain confirmations to see UI updates; the interface predicts outcomes and updates immediately while settlement completes in the background. Drizzle ORM with PostgreSQL manages off-chain data with strong consistency guarantees. DALP's native indexer keeps blockchain events queryable within seconds of on-chain finality. Redis caching accelerates frequent queries. Expensive operations get cached aggressively so dashboards load instantly even with thousands of assets and tens of thousands of holders. Kubernetes deployment via Helm charts enables cloud-native operations with autoscaling, rolling updates, health monitoring, and self-healing. Deploy to any Kubernetes environment: public cloud, private cloud, or on-premises. ### Deployable observability stack [#deployable-observability-stack] DALP provides a Helm observability chart for deployments that enable and configure the stack. The chart can deploy VictoriaMetrics for metrics, Loki for logs, Tempo for traces, and Grafana dashboard configuration for common self-hosted operations. When the observability chart and relevant exporters are enabled, dashboards can show what operations teams need: * Transaction throughput and success rates * Compliance check performance and failure reasons * System availability and response times * Asset-level activity and holder statistics Alert notifications and routing depend on the deployment's notification configuration. Operators can troubleshoot issues by viewing correlated system behavior across enabled telemetry sources in one interface. **Cost advantage**: Using deployable open-source telemetry components can reduce the need for separate monitoring SaaS contracts for common operational views, while enterprise integrations and retention policies remain deployment choices. DALP's optional observability chart includes VictoriaMetrics, Loki, Tempo, and Grafana. See [Observability Architecture](/docs/architecture/operability/observability) for the deployment boundary, retention configuration, and custom dashboard creation. ### Banking and payment integration [#banking-and-payment-integration] The platform supports treasury workflows where operators verify fiat deposit evidence, process tokenized-cash issuance, and coordinate redemptions with the off-chain payment systems their institution uses. DALP records the on-chain asset movement and settlement state; payment-file generation and banking-network connectivity remain integration responsibilities unless a deployment connects those systems explicitly. Multi-currency support handles assets denominated in different fiat currencies with proper tracking. The same platform manages USD bonds, EUR stablecoins, and SGD deposit certificates without requiring separate deployments. Payment versus Payment (PvP) settlement coordinates multi-leg transactions where one token exchanges for another token, with atomicity guarantees ensuring both legs complete or both revert. ## How DALP delivers value [#how-dalp-delivers-value] DALP is organized around core business functions rather than technical components: **User-facing applications** provide role-specific interfaces: * Issuer Portal for creating and managing tokenized assets * Investor Portal for viewing holdings and claiming distributions * Admin Console for compliance officers and operations teams * Developer Portal for technical integrations **Business logic layer** coordinates workflows: * Asset lifecycle orchestration from issuance through redemption * Compliance verification before every transaction * Integration with banking systems, KYC providers, and custody services **Record-keeping infrastructure** maintains authoritative data: * Immutable ownership ledger (blockchain-based) * Fast-access transaction history and reporting database * Real-time indexing for instant portfolio views **External system connections** enable end-to-end workflows: * Banking rails for fiat on/off ramps * KYC/AML providers for identity verification * Custody services for HSM-backed key management with insurance and regulatory compliance * Document storage for offering materials and legal files The platform is architected so every component contributes to one or more business outcomes: faster issuance, lower operational costs, regulatory compliance confidence, or better investor experience. See the [Platform Overview](/docs/architecture/overview) documentation for detailed component diagrams, API specifications, smart contract interfaces, and deployment topology options. ## Benefits and tangible outcomes [#benefits-and-tangible-outcomes] **Faster time to market**: Issuers go from term sheet to live token in days instead of months. Templates handle compliance structure, factory contracts deploy tokens automatically, and the platform eliminates most custom development. **Reduced operational overhead**: Corporate actions that took teams of people and multiple days execute with minimal manual work. Dividend entitlements, coupon calculations, NAV updates, and redemptions happen programmatically without manual spreadsheet work or reconciliation. Token holders claim their distributions on-demand. **Compliance confidence**: Non-compliant transactions don't execute because eligibility checks happen before execution. Regulators see a platform architected for control. Risk committees approve deployments faster when the architecture demonstrates proper controls. **Better investor experience**: Real-time holdings visibility, instant settlement, on-demand yield claiming, and transparent audit trails replace quarterly statements and opaque processes. Investor support tickets drop because the platform provides self-service transparency. **Lower total cost of ownership**: One platform replacing multiple vendors means one contract to negotiate, one security review, one integration project, one support relationship. Procurement cycles shrink from months to weeks. ## Who's using DALP and for what [#whos-using-dalp-and-for-what] Production deployments span multiple use cases. Asset managers tokenize private fund units to automate administration and enable secondary trading. Banks issue deposit certificates as programmable tokens with automated maturity processing. Corporations explore tokenized bonds for direct-to-investor capital raising with embedded compliance. Geography matters less than regulatory clarity. European institutions leveraging MiCA frameworks, Singapore financial institutions under MAS oversight, and Gulf Cooperation Council markets with clear tokenization guidelines are moving fastest. The US market is more cautious but accelerating as regulatory frameworks solidify. Scale varies from pilot programs managing tens of millions to institutional deployments handling hundreds of millions in tokenized assets. The platform architecture supports both with the same codebase and operational model. ## What this means for your organization [#what-this-means-for-your-organization] If you're exploring tokenization, DALP provides a complete platform rather than forcing you to become a systems integrator. If you're already running a pilot on separate vendor systems, DALP offers a migration path to unified lifecycle management with embedded compliance. If you're a developer, DALP gives you modern APIs, comprehensive documentation, and working reference implementations. If you're an operator, DALP provides purpose-built tools for daily workflows rather than generic blockchain explorers. If you're a risk officer, DALP demonstrates defense-in-depth security with audit trails that regulatory frameworks require. If you're a compliance officer, DALP embeds policy into the enforcement path rather than relying on post-transaction checks to catch violations. The platform is specifically architected for regulated financial instruments with institutional requirements. If you're tokenizing securities, funds, bonds, or deposits with real compliance obligations, DALP implements what you need. ## Where to next [#where-to-next] * **[Use cases](/docs/executive-overview/use-cases)** – Real-world scenarios across asset classes * **[Compliance & security](/docs/executive-overview/compliance-security)** – Regulatory and security architecture details * **[Glossary](/docs/executive-overview/glossary)** – Key terms and definitions # Lifecycle platform approach Source: https://docs.settlemint.com/docs/executive-overview/dalp-solution Digital Asset Lifecycle Platforms (DALPs) provide regulated digital asset operations infrastructure for post-launch asset control, unified architecture, and production operations. ## Key terms [#key-terms] * **[DALP](/docs/executive-overview/glossary#dalp)** – Digital Asset Lifecycle Platform providing integrated infrastructure for the complete asset lifecycle * **[Atomic operations](/docs/executive-overview/glossary#atomic-operations)** – Transactions that execute completely or not at all, preventing partial state * **[Unified registry](/docs/executive-overview/glossary#registry)** – Single authoritative record of ownership and compliance state * **[DvP](/docs/executive-overview/glossary#dvp)** – Delivery versus payment ensuring simultaneous exchange of asset and cash ## What makes a platform a DALP [#what-makes-a-platform-a-dalp] A Digital Asset Lifecycle Platform collapses issuance, compliance, custody controls, settlement, servicing, exception handling, and operating evidence into one integrated system with a single control plane. It is unified infrastructure where the ownership registry, business rules, approvals, and operational records stay synchronized by design. An asset lifecycle includes creation with specific terms, investor onboarding and verification, primary distribution with eligibility checks, secondary trading with continued compliance enforcement, corporate actions with automated distribution, continuous reporting and audit trails, and eventual maturity, redemption, or sunset. A DALP handles all of these phases on one platform with consistent security, unified data, and coordinated workflows. Traditional approaches involve different systems, different vendors, different data models, and different points of failure at each phase. Institutions require unified architecture where risk committees can identify one single source of truth, the platform's unified registry, rather than requiring daily reconciliation across multiple databases. ## The six laws of a DALP (non-negotiables) [#the-six-laws-of-a-dalp-non-negotiables] If a platform doesn't implement all six of these principles, it's not a DALP. These principles separate real lifecycle platforms from repackaged point solutions: ### 1. unified lifecycle core [#1-unified-lifecycle-core] Issuance, onboarding, compliance, custody, trading, settlement, servicing, and reporting operate on one shared source of truth. Every state change, whether it's a token transfer, a compliance approval, a corporate action, or an access control update, updates a single authoritative registry. No nightly batch reconciliation jobs exist to align different databases. There's no "eventual consistency" where your trading system thinks someone owns 1,000 tokens but your compliance system thinks they own 900 and your custody provider hasn't updated yet. When a regulator asks "who owned this asset on this date?" there's one definitive answer backed by immutable evidence. When an auditor asks "how do you know this transfer was compliant?" the platform shows exactly which rules were checked, which identity claims were verified, and which approval workflow executed, all in the same system that managed the transfer itself. ### 2. compliance by design [#2-compliance-by-design] KYC, KYB, accreditation checks, and jurisdictional policies are embedded in the asset's transfer path, not bolted on afterward. Every transaction enforces eligibility before it executes. Non-compliant transfers revert immediately rather than requiring cleanup after they've been recorded on-chain. Identity verification happens once, and the proof travels with the investor across all assets on the platform. A verified accredited investor doesn't need to re-verify for each new fund they invest in. Their credential is reusable, revocable if circumstances change, and auditable at any point. Compliance rules are configurable by asset and by jurisdiction, executing through a unified policy engine. The rule engine is the single enforcement point. Jurisdictional templates provide starting points for activating controls, but live-asset updates remain governed, asset-scoped changes: updating a template does not mutate existing asset policy state, so teams explicitly review affected assets, re-apply or adjust module parameters, and verify the resulting transfer behavior. ### 3. custody, settlement, and day-two operations [#3-custody-settlement-and-day-two-operations] Token creation is not the hard part for regulated institutions. The hard part is operating the asset after launch: controlling approvals, enforcing custody policies, handling settlement states, servicing holders, managing exceptions, and producing evidence for audit. DALP provides that operating infrastructure inside the asset lifecycle rather than leaving it to disconnected tools. **Vault-based custody**: Multi-signature approval workflows with configurable quorum rules and role-based access controls. Compatible with HSM-backed signers (enterprise custody providers can sign with HSM-protected keys). DALP's vault system implements maker-checker workflows where proposals require multiple approvals before execution. Emergency pause capabilities protect against compromised accounts. Regulated custodian integration lets institutions delegate key management to specialists while maintaining visibility and control through the platform. The platform supports bring-your-own-custodian models rather than forcing institutions to trust platform custody. **Atomic DvP settlement**: Delivery versus payment (DvP) where both legs, asset and cash, execute together or both revert. No window exists where one party has received value and the other hasn't. DALP's XvP settlement system extends this to multi-party exchanges where any number of participants can exchange tokens atomically. If any leg fails, the entire settlement reverts. This eliminates counterparty risk and the need for trusted intermediaries. This requires cash to be on-chain via tokenized deposits, regulated stablecoins, or Central Bank Digital Currency when available. DALP provides the settlement infrastructure that coordinates these atomic exchanges. **Scheduled yield management**: Fixed yield schedules eliminate manual calculation of dividends, interest payments, and coupon entitlements. Configure payment schedules once during issuance, and DALP calculates entitlements automatically on payment dates. Token holders claim their yields directly through smart contracts with cryptographic proof of entitlement. No spreadsheets, no reconciliation, no manual wire transfers to thousands of investors. Payment operations remain bounded by the systems connected to the deployment. DALP coordinates the on-chain asset, tokenized-cash, and settlement state; core banking, payment-network, and reconciliation-message flows are integrated around that state when required by the institution. ### 4. enterprise deployment and control [#4-enterprise-deployment-and-control] On-premises installation, bring-your-own-cloud deployment, or dedicated SaaS with isolated infrastructure, institutions choose what fits their risk and compliance requirements. The platform adapts to enterprise standards rather than dictating architecture. SSO via SAML or OIDC connects to existing identity providers. MFA enforcement aligns with corporate security policies. Role-based access control (RBAC) or attribute-based access control (ABAC) maps to organizational hierarchies. Employee onboarding and offboarding flows through existing IAM systems. Audit logging captures every access, every action, every approval with immutable evidence. SIEM integration sends security events to centralized monitoring. Data residency requirements get met by deploying in specific geographic regions or customer-controlled data centers. Disaster recovery and business continuity procedures follow enterprise standards. Theme customization lets institutions present the Asset Console with their own branding while using the same operational workflows. See the [Asset Console customization guidance](/docs/architecture/components/platform/asset-console#customization) for the supported controls. ### 5. developer and operator instrumentation [#5-developer-and-operator-instrumentation] Modern APIs and SDKs with comprehensive documentation, typed interfaces, sandbox environments, and versioning policies let developers integrate quickly. REST endpoints provide flexible data access. Operations that finish in the request path return immediate data with transaction hashes; operations accepted for asynchronous processing return a transaction ID, current status, and status URL for polling long-running blockchain work. The platform ships with pre-built UI components and reference architectures, not just APIs. Investor onboarding flows, asset type templates, and reference implementations can be customized rather than built from scratch. Operational dashboards and monitoring surfaces give teams visibility into API and blockchain activity, pending approvals, blocked transactions with reason codes, custody balances, settlement status, and compliance alerts. Operations teams can monitor daily activity without developer support and can investigate exceptions against the same records that drive the asset workflow. A rule library with jurisdiction-specific compliance templates reduces policy configuration from months to days. Compliance officers select template modules that experts have already built and vetted rather than translating regulations into smart contract logic from scratch. ### 6. proof through metrics [#6-proof-through-metrics] Institutions don't invest in infrastructure based on promises. They need measurable outcomes proving the platform delivers on its commitments. Target metrics that matter: * **Near-total T+0 settlement:** 99% or more transactions settle same-day with atomic DvP * **Zero compliance breaches:** No transactions execute that violate eligibility requirements * **High first-attempt success:** 99%+ of legitimate transactions succeed without manual intervention * **Rapid onboarding:** KYC turnaround under one business day for standard cases * **Enterprise uptime:** 99.9% availability for production operations These aren't stretch goals. They're minimum acceptable performance for production financial infrastructure. When your platform achieves them consistently, you've built something institutions can depend on. Geographic jurisdiction map provides visibility into where tokenized assets are held globally. ## How unified architecture scales [#how-unified-architecture-scales] A DALP provides unified infrastructure where lifecycle phases integrate because they are architected together. External systems still connect through APIs, but the core lifecycle, from issuance through redemption, flows through one coherent platform. **Integration complexity stays flat.** Adding a new asset class, compliance rule, or feature happens inside one codebase. There is no cross-vendor API coordination, no waiting for third-party release cycles, no multi-system regression testing. **One source of truth eliminates reconciliation.** The trading view of ownership, the custody balance, and the compliance status all reference the same registry. There are no nightly batch jobs aligning different databases and no reconciliation reports consuming full-time resources. **Security boundaries are minimized.** One platform means one security review, one set of API credentials, one audit surface. Security teams evaluate one vendor's practices rather than monitoring five platforms' security advisories. **Ownership is clear.** When something breaks, there is one support line. There is no vendor blame game, no multi-party incident coordination, no ambiguity about who is responsible for what. ## How a DALP architecture works [#how-a-dalp-architecture-works] A DALP stores data in one registry. Every component works against that single source of truth. Compliance checks and ledger updates execute together atomically. Custody, settlement, and asset movement coordinate as workflows where the platform ensures both legs complete or both revert. ### Traditional multi-vendor approach [#traditional-multi-vendor-approach] Multiple vendors create multiple integration points, each requiring separate development, testing, and maintenance. Data synchronization happens through API calls, requiring constant reconciliation. ### DALP approach: unified architecture [#dalp-approach-unified-architecture] The DALP approach eliminates integration complexity by providing a unified core where all lifecycle phases operate against a single source of truth. Components are architected together, ensuring atomic operations and consistent state across the entire platform. Centralized asset management with real-time metrics across all deployed tokens. ## When to use a DALP [#when-to-use-a-dalp] **Choose a DALP when:** * Issuing regulated securities requiring audit trails and immutable compliance evidence * Managing multiple asset types (bonds, equity, funds) on unified infrastructure * Requiring atomic settlement with delivery-versus-payment guarantees * Operating under strict enterprise security, audit, and deployment controls * Needing custody with maker-checker approvals, recovery procedures, and role separation * Supporting institutional transaction volumes beyond initial deployments **Point solutions may suffice when:** * Building experimental/pilot projects with no regulatory requirements * Tokenizing single asset types with simple transfer rules * Operating exclusively on public chains with no custody requirements * Accepting manual corporate action processes and off-chain reconciliation ## What this means for your next tokenization project [#what-this-means-for-your-next-tokenization-project] When evaluating platforms, ask these questions: 1. **Is there a single source of truth for ownership?** If the answer involves reconciling multiple systems, it's not a DALP. 2. **Are compliance rules enforced before or after transfers execute?** If checks happen asynchronously after settlement, it's not a DALP. 3. **Can the platform demonstrate atomic settlement** where both asset and cash legs succeed together or fail together? If not, it's not a DALP. 4. **Does the platform support my deployment requirements?** On-premises, bring-your-own-cloud, or dedicated infrastructure with enterprise IAM integration? If you're forced into multi-tenant SaaS on a public chain, it's not a DALP. 5. **What's the developer and operator experience?** Complete APIs, SDKs, sandbox environments, operational dashboards, and audit tooling? Or sparse documentation and feature requests? 6. **Can you point to production deployments** with measurable outcomes demonstrating T+0 settlement, zero compliance breaches, and enterprise uptime? Or pilot stage with future promises? The DALP category exists because institutions need more than token creation. They need integrated lifecycle infrastructure architected specifically for regulated financial instruments with institutional requirements. SettleMint's Digital Asset Lifecycle Platform is a production implementation of DALP principles, a full-stack platform handling issuance, compliance, custody, settlement, and servicing as one coordinated system. ## Key takeaways [#key-takeaways] * **Unified architecture eliminates integration tax** - DALPs collapse five+ vendor relationships into one platform with a single source of truth * **Compliance-by-design prevents violations** - Ex-ante controls embedded in transfer logic ensure rules execute before transactions settle * **Atomic settlement removes counterparty risk** - DvP settlement guarantees both legs complete or both revert, eliminating reconciliation gaps * **Enterprise deployment flexibility** - On-premises, bring-your-own-cloud, or dedicated SaaS options meet institutional control requirements * **Measurable outcomes matter** - Demand specific results (99%+ T+0 settlement, zero compliance breaches) rather than roadmap promises The six DALP laws provide a non-negotiable checklist: platforms missing any principle fall short of institutional requirements. ## Where to next [#where-to-next] * **[DALP overview](/docs/executive-overview/dalp-overview)** – How the Digital Asset Lifecycle Platform implements DALP principles * **[Compliance & security](/docs/executive-overview/compliance-security)** – Regulatory and security controls in practice * **[Glossary](/docs/executive-overview/glossary)** – Key terms and definitions # Glossary Source: https://docs.settlemint.com/docs/executive-overview/glossary Key terminology for digital asset platforms and tokenization. These terms connect directly to DALP lifecycle capabilities—from DvP settlement and vault custody to automated yield distribution—giving you the vocabulary to evaluate platform features and operational dashboards. ## Concept network [#concept-network] The diagram below shows how key DALP concepts interconnect to enable compliant, automated digital asset operations: **Key relationships:** * **DALP integrates** lifecycle capabilities (DvP settlement, vault custody, yield distribution) with compliance infrastructure (ERC-3643 standard) * **DvP uses Vault** for atomic settlement—both asset and payment custody ensures simultaneous exchange without counterparty risk * **ERC-3643 requires OnchainID** to link wallet addresses to verified identities, enabling compliance checks before every transfer * **OnchainID enables Compliance Modules** by providing verified claims (KYC status, accreditation, jurisdiction) that feed rule evaluation * **Yield Management uses Vault** for secure distribution of coupon payments, dividends, and returns to token holders ## Core tokenization concepts [#core-tokenization-concepts] The foundation of digital asset platforms rests on a few key innovations that make traditional finance programmable. These concepts underpin every feature in DALP—from compliance automation to instant settlement—and appear throughout observability dashboards when you're monitoring platform health. **Asset tokenization**: Converting ownership rights in real-world assets into digital tokens on a blockchain. The token represents the same legal rights as traditional instruments but enables instant transfer and automated compliance. For example, a bond token carries identical coupon payment rights but settles through DvP vaults rather than wire transfers. **Blockchain**: A distributed ledger technology where transactions are recorded in blocks and cryptographically linked. Provides immutable audit trails and eliminates single points of failure. Every transaction visible in your observability dashboards is anchored to a specific block for tamper-proof verification. **Smart contract**: Self-executing code on a blockchain that automatically enforces rules and executes transactions when conditions are met. Enables programmable compliance and automated corporate actions. For instance, a bond smart contract distributes coupon payments to all token holders proportionally without manual intervention—you'll see these distributions as events in the yield management dashboard. **Token**: A digital representation of an asset on a blockchain. Security tokens represent ownership or financial rights in regulated assets like bonds, equities, or fund units. Each token interaction (transfer, lock, redemption) generates metrics tracked in the cap table and compliance dashboards. ## Compliance and identity [#compliance-and-identity] Regulated assets demand verified identities and automated rule enforcement at every transfer. This section covers the identity infrastructure that makes compliance invisible to users while remaining auditable for regulators. When you review compliance metrics in observability dashboards, these are the mechanisms generating those pass/fail statistics. **ERC-3643**: A token standard specifically designed for permissioned securities. Ensures compliance checks happen before every transfer by linking tokens to verified identities. The compliance dashboard shows real-time rule evaluation stats powered by this standard. **OnchainID**: A decentralized identity protocol where verified claims about an investor (KYC status, accreditation, jurisdiction) are cryptographically linked to their wallet address. These claims feed the compliance engine that decides whether a transfer can proceed. **Identity registry**: A system that maps wallet addresses to verified identities and eligibility criteria. Only registered addresses can interact with compliant assets. Observability dashboards track registration rates and identity verification status across your investor base. **KYC/AML**: Know Your Customer and Anti-Money Laundering procedures required by regulators. Verifies investor identity and screens against sanctions lists. Completion rates and verification latency appear as metrics in operational dashboards. **Accredited investor**: An individual or entity meeting financial thresholds (income, net worth, or sophistication) allowing participation in private securities offerings under US regulations. Identity claims capture accreditation status, enabling automated eligibility checks at transfer time. ## Platform architecture [#platform-architecture] DALP integrates lifecycle management, compliance, custody, and settlement into one coherent system—eliminating the vendor sprawl that creates operational gaps and blind spots. When the observability chart and relevant exporters are enabled, these architectural components can feed centralized dashboards for platform health and operational visibility. **DALP (Digital Asset Lifecycle Platform)**: Unified infrastructure managing the complete lifecycle of tokenized assets from issuance through redemption. Integrates compliance, custody, settlement, and servicing in one system rather than cobbling together multiple vendors. Monitoring coverage depends on the observability chart, enabled telemetry exporters, and configured dashboards for the target deployment. **SMART Protocol (SettleMint Adaptable Regulated Token)**: The compliance framework underlying all DALP assets. Extends ERC-3643 with modular compliance rules and identity management. Every compliance check visible in dashboards runs through SMART Protocol validation logic. **Compliance engine**: The policy enforcement system that evaluates every transfer against configurable rules before execution. Non-compliant transactions revert automatically. Observability dashboards track rule evaluation latency, pass rates, and rejection reasons for troubleshooting and audits. **Factory contract**: A smart contract template that deploys new asset tokens with pre-configured compliance and governance structures, accelerating time to market. Deployment metrics (time to issue, configuration errors) feed into operational dashboards. ## Settlement and operations [#settlement-and-operations] Traditional settlement introduces counterparty risk and multi-day delays. DALP uses atomic DvP settlement through secure vaults to eliminate these friction points. When you monitor settlement latency in observability dashboards, you're watching these mechanisms execute in real time—typically under two seconds per transaction. **T+0 settlement**: Same-day settlement where ownership transfers complete on the day of the transaction, versus traditional T+2 (two business days after trade date). DvP mechanisms in DALP achieve settlement in seconds, surfacing latency metrics in performance dashboards. **Atomic settlement / DvP (Delivery versus Payment)**: Simultaneous exchange where the asset and payment both complete or both fail together, eliminating counterparty risk during settlement windows. DALP vaults hold assets and payment tokens until both parties meet conditions—dashboards show vault utilization and settlement success rates. **Stablecoin**: A cryptocurrency pegged to fiat currency (like USD) at a 1:1 ratio, typically backed by reserves. Enables on-chain payments and settlement within DvP vaults. Liquidity and reserve metrics appear in treasury dashboards. **Gas fee**: Transaction cost on a blockchain network, paid to validators who process and confirm transactions. Observability dashboards track gas consumption per operation type (transfer, yield distribution, redemption) to optimize transaction batching and cost efficiency. ## Corporate actions [#corporate-actions] Automated yield distribution and corporate action processing distinguish DALP from basic tokenization platforms. These capabilities execute through smart contracts that enforce fairness and auditability—every coupon payment, NAV update, or redemption generates events and metrics visible in lifecycle dashboards. **Coupon payment**: Periodic interest payment on a bond, distributed to all token holders proportionally to their holdings. DALP's yield management module automates distribution through smart contracts, surfacing payment schedules, execution status, and recipient breakdowns in observability dashboards. **NAV (Net Asset Value)**: The per-share value of a fund, calculated by dividing total assets minus liabilities by the number of shares outstanding. NAV updates trigger revaluation events tracked in fund performance dashboards, feeding into redemption calculations. **Cap table**: Capitalization table showing all holders of an asset and their ownership percentages. Blockchain provides real-time cap table visibility—DALP dashboards offer live views of holder distributions, concentration metrics, and transfer velocity. **Redemption**: The process of converting tokens back to cash or underlying assets, typically at maturity for bonds or upon fund exit. Redemption workflows in DALP use DvP vaults to ensure atomic exchange, with success rates and settlement times tracked in lifecycle dashboards. ## Regulatory frameworks [#regulatory-frameworks] Operating across jurisdictions requires navigating different regulatory regimes. These frameworks shape compliance rule configuration in DALP—when you review compliance dashboards segmented by jurisdiction, you're seeing how these regulations translate into automated policy enforcement. **MiCA (Markets in Crypto-Assets)**: European Union regulation providing comprehensive framework for crypto-assets including security tokens and stablecoins. Compliance rules for EU investors enforce MiCA requirements at transfer time. **Regulation D**: US SEC rule allowing private securities offerings to accredited investors without full registration requirements. Identity claims capture Reg D eligibility, enabling automated checks that appear as rule evaluations in compliance dashboards. **Regulation S**: US SEC rule permitting securities offerings outside the United States without registration, subject to restrictions. Geographic claims and holding period rules enforce Reg S compliance automatically. **MAS (Monetary Authority of Singapore)**: Singapore's central bank and financial regulatory authority, which has established clear frameworks for digital assets. MAS-compliant deployments configure jurisdiction-specific rules visible in regional compliance dashboards. ## Security and custody [#security-and-custody] Institutional custody demands defense-in-depth: multisig controls, HSM key storage, and granular RBAC permissions. DALP vaults implement these safeguards while remaining auditable through observability dashboards that track signing operations, key rotation, and access patterns. **Multi-signature wallet (multisig)**: A wallet requiring multiple private key signatures to authorize transactions, implementing maker-checker controls and segregation of duties. DvP vaults use multisig for treasury operations—dashboards track signature collection latency and approval workflows. **Hardware Security Module (HSM)**: Tamper-resistant physical device that securely stores cryptographic keys and performs signing operations, meeting bank-grade security requirements. Observability dashboards monitor HSM health, signing latency, and key usage patterns for anomaly detection. **Role-based access control (RBAC)**: Permission system where user capabilities are determined by assigned roles, ensuring proper segregation of duties. RBAC policies govern who can issue assets, approve transfers, or configure compliance rules—access logs and permission audits appear in security dashboards. **Private key**: Cryptographic secret that controls access to a blockchain wallet. Losing the private key means losing access to assets permanently. Institutional deployments store keys in HSMs and enforce key rotation policies tracked through security dashboards. ## Where to next [#where-to-next] * [Introduction](/docs/executive-overview/introduction) – Start with what tokenization means for your business * [Market challenges](/docs/executive-overview/market-challenges) – Understand the pain points DALP addresses * [Architecture section](/docs/architecture/overview) – Dive deeper into technical implementation # Introduction Source: https://docs.settlemint.com/docs/executive-overview/introduction Asset tokenization converts ownership rights in real-world assets into digital tokens on a blockchain. This introduction explains what tokenization means, why institutions are adopting it, and what's driving growth in this market. You'll understand the core value proposition and market dynamics that make this relevant for your organization. ## Key concepts [#key-concepts] Before continuing, understand these business concepts: * **Digital token**: Electronic certificate representing asset ownership * **Automated compliance**: Rules enforced instantly without manual intervention * **Immutable record**: Transaction history that cannot be altered or deleted * **Instant settlement**: Ownership transfers completing in seconds, not days Looking for implementation details? See the [Architecture section](/docs/architecture/overview) for technical specifications on ERC-3643 tokens, smart contracts, and blockchain infrastructure. For complete definitions, see the [Glossary](/docs/executive-overview/glossary). The DALP Dashboard provides at-a-glance visibility into total AUM, active assets, and key platform metrics. DALP sign-in flow with passkey-based access for institutional users. ## What asset tokenization means [#what-asset-tokenization-means] Asset tokenization replaces paper certificates and database entries with digital tokens on a blockchain. Instead of ownership living in a transfer agent's database or a legal registry, it's recorded on a distributed ledger. The token carries the same legal rights as the traditional instrument. What changes is how ownership is recorded, transferred, and managed. Transfers happen instantly across networks. Settlement completes in seconds rather than days. Compliance rules execute automatically through code. This isn't experimental technology. The tokenized real-world asset market reached over $50 billion in 2024 (excluding stablecoins). Projections suggest it will exceed $500 billion in 2025. Yet that remains a small fraction of the $230 trillion global asset pool. The gap exists because infrastructure hasn't caught up to institutional requirements. ## Why businesses care [#why-businesses-care] ### Liquidity in illiquid markets [#liquidity-in-illiquid-markets] Traditional securities markets face significant liquidity constraints. Private equity lockups stretch for years. Real estate requires finding buyers willing to purchase entire assets. Fund units trade quarterly at best. Tokenization fractionalizes ownership. A $10 million commercial property can have 10,000 token holders each owning $1,000 worth. Secondary markets become feasible because transaction costs drop and settlement happens instantly. Holders can exit positions without waiting for fund wind-down schedules. ### Real-time transparency [#real-time-transparency] Every token holder can see their position in real time. The blockchain provides an immutable audit trail of every transfer, every compliance check, every corporate action. No three-week wait for a quarterly statement that's already outdated when it arrives. Benefits across stakeholders: * **Issuers**: Real-time cap table visibility * **Compliance officers**: Exact holder data—who, where, eligibility status * **Auditors**: Machine-readable proof instead of spreadsheet reconciliation ### Operational automation through lifecycle management [#operational-automation-through-lifecycle-management] Corporate actions—dividends, coupon payments, voting rights, redemptions—typically involve manual processes with spreadsheets, email chains, and reconciliation work. Tokenization eliminates this operational overhead by automating the entire lifecycle. **DALP delivers operational capabilities that reduce costs and errors:** * **Scheduled yield management**: Bond coupons, equity dividends, and fund distributions calculate automatically. Configure once during issuance; the system handles all future payments without manual intervention. Token holders receive their distributions on-demand without waiting for wire transfers. * **Simultaneous settlement**: Asset and cash transfer together or not at all— eliminating the settlement risk window where one party has paid but the other hasn't delivered. Zero counterparty risk means no need for expensive intermediaries. * **Secure treasury controls**: Treasury operations require multiple approvals from designated signers. No single person can move assets unilaterally, protecting against internal fraud and external compromise. Operations teams that spend 40+ hours per month on distributions, reconciliation, and manual transfers reduce this to monitoring automated processes. The cost savings from eliminating manual work typically cover platform costs within the first year. Examples in practice: * **Bond coupon payment**: System calculates what each holder is owed on payment dates; holders claim their payments directly * **Shareholder vote**: System determines voting power automatically from holdings; votes are tallied electronically without manual spreadsheets * **Secondary trade**: Asset and payment happen simultaneously—both parties receive their value at the exact same moment No manual lists, no missed notifications, no reconciliation errors. See [smart contract architecture](/docs/architecture/components/asset-contracts) for technical details on yield schedule contracts, atomic settlement protocols, and multi-signature vault implementations. ### Built-in compliance [#built-in-compliance] Compliance rules are embedded directly into the transfer process. Before any ownership change executes, the system automatically verifies: * Recipient identity verification status * Jurisdiction eligibility * Holding limits and lockup periods If a rule is violated, the transfer is blocked instantly. There's no after-the-fact scramble to reverse an illegal transaction or costly regulatory violation. This preventative control architecture is what regulators expect and what risk committees require for approval. **Cost avoidance**: A single compliance violation in traditional securities can result in regulatory fines ranging from hundreds of thousands to millions of dollars, plus reputational damage. Automated preventative controls eliminate this risk category entirely. Learn how ERC-3643 compliance modules, OnchainID identity protocols, and rule engines implement these controls in the [Compliance Architecture](/docs/architecture/security/identity-compliance) documentation. ## Market momentum [#market-momentum] ### Regulatory clarity emerging [#regulatory-clarity-emerging] The European Union's Markets in Crypto-Assets (MiCA) regulation provides clear frameworks for tokenized instruments. Singapore's Monetary Authority continues to approve institutional pilots. Even the SEC has approved spot Bitcoin ETFs and is engaging with security token frameworks. Regulation is catching up. Institutions need clear legal ground to operate. The frameworks are arriving. ### Major institutions deploying production systems [#major-institutions-deploying-production-systems] JPMorgan's Onyx platform processes billions in repo transactions daily using blockchain rails. Siemens issued a €60 million digital bond directly on-chain. Franklin Templeton runs an on-chain money market fund. These aren't experiments. They're production deployments managing real money for real clients. The infrastructure is maturing: * Custody providers offer HSM-backed key management with insurance and regulatory compliance * Settlement networks build bridges to traditional payment rails The next step is a unified platform that handles the entire lifecycle from issuance through ongoing servicing without forcing institutions to integrate five different vendors. ## Production examples [#production-examples] **Government bonds**: The European Investment Bank issued €100 million in digital bonds on Ethereum in 2021, settling with tokenized euros. Settlement time dropped from T+2 to near-instant, with all legal documentation cryptographically linked to the token metadata. **Private credit**: Multiple platforms now tokenize private credit instruments, allowing institutional investors to trade positions that traditionally locked up capital for years. Secondary markets emerged because 24/7 settlement and fractional ownership made previously illiquid assets tradable. **Real estate fractionalization**: Commercial property worth millions gets divided into thousands of tokens, opening institutional-quality real estate to smaller investors while maintaining comprehensive compliance controls. Rental income distributions happen automatically on preset schedules. **Fund administration**: Asset managers are tokenizing fund units to automate the entire lifecycle: subscription, NAV calculation, management fee collection, performance tracking, and redemption. Operations teams go from 40-hour monthly closes to real-time reporting. ## What institutions need to adopt at scale [#what-institutions-need-to-adopt-at-scale] ### Unified infrastructure [#unified-infrastructure] Institutional tokenization requires token creation, KYC/AML, custody, settlement, and reporting on a single platform with one source of truth, one security review, and one support relationship. ### Embedded compliance [#embedded-compliance] Compliance checks must execute before transfers — not after. When eligibility rules are embedded in the transfer path, non-compliant transactions revert immediately. Regulators see ex-ante controls, not post-transfer cleanup. ### Multi-signature custody [#multi-signature-custody] Single-key wallets do not meet institutional standards. Platforms need maker-checker approvals, HSM compatibility, formal recovery procedures, and integration with established custodians. ### Atomic settlement [#atomic-settlement] The token moves instantly on-chain, but the cash leg must settle atomically alongside it. Delivery versus payment (DvP) — where both legs succeed together or revert together — eliminates counterparty risk and reconciliation work. ### Enterprise deployment controls [#enterprise-deployment-controls] Banks require on-premises deployment options, single sign-on integration, multi-factor authentication, detailed audit logs, and data residency guarantees. The platform must adapt to enterprise IT standards, not the other way around. ## Why this moment matters [#why-this-moment-matters] The infrastructure pieces are finally aligning: * Regulatory clarity emerging across major jurisdictions * Institutional custody providers live * Settlement networks operational * Blockchain technology matured past "experiment" phase Success requires integrated infrastructure that handles the entire lifecycle without forcing institutions to become blockchain experts or system integrators. The Digital Asset Lifecycle Platform provides this unified infrastructure. The next section explores the specific challenges institutions face when trying to tokenize assets today, setting up why a unified Digital Asset Lifecycle Platform approach is necessary. ## Where to next [#where-to-next] * [Market challenges](/docs/executive-overview/market-challenges) – Understand the pain points in current approaches * [DALP solution](/docs/executive-overview/dalp-solution) – Learn how unified platforms solve these problems * [Glossary](/docs/executive-overview/glossary) – Define unfamiliar terms # What institutions require Source: https://docs.settlemint.com/docs/executive-overview/market-challenges Institutional digital asset programs require more than issuance. This page explains the operating capabilities needed after launch: embedded compliance, approval governance, custody-policy controls, settlement handling, servicing, exception management, observability, and audit evidence. ## Key terms [#key-terms] * **Integration point**: Connection between separate systems requiring custom development and ongoing maintenance * **Ex-ante control**: Compliance checks that happen before a transaction executes, preventing violations * **Ex-post control**: Compliance checks that happen after execution, requiring reversal if violations are found See the [Glossary](/docs/executive-overview/glossary) for complete definitions. Operational visibility starts with a unified dashboard across tokenized asset activity. ## Unified infrastructure across the lifecycle [#unified-infrastructure-across-the-lifecycle] Institutional digital asset programs require more than token creation. Once an asset is live, operations teams need to control investor onboarding, compliance, custody-policy boundaries, approvals, transfers, settlement handling, servicing, reporting, and audit evidence. A viable platform consolidates these functions into one system with a single source of truth. ### One platform, one source of truth [#one-platform-one-source-of-truth] When all six functions operate on a shared registry, there is no nightly batch reconciliation, no data drift between systems, and no ambiguity about who owns what. Auditors see one definitive record. Compliance officers query one system. Operations teams monitor one dashboard. ### Fewer contracts, faster procurement [#fewer-contracts-faster-procurement] One vendor relationship means one security review, one legal contract, one SLA negotiation. Procurement cycles shrink from months to weeks. When something breaks at 2 a.m., there is one support line to call — not five vendors pointing at each other. ### Development velocity [#development-velocity] New asset classes, compliance rule changes, and feature additions happen inside one codebase. There are no cross-vendor API coordination delays, no waiting for third-party release cycles, no multi-system regression testing. ## Embedded compliance enforced before execution [#embedded-compliance-enforced-before-execution] Compliance visibility across identities, trusted issuers, and active verifications. ## Off-chain compliance creates legal risk [#off-chain-compliance-creates-legal-risk] Compliance checks must happen in the token's transfer path — before any state change occurs — not in off-chain middleware that runs after the fact. A typical off-chain compliance setup works like this: an investor buys tokens, the transfer executes on-chain instantly, and then — sometime later — a compliance officer checks whether that investor should have been eligible. If they weren't, unwinding a settled transaction creates evidence of non-compliance on an immutable ledger. ### Ex-ante controls prevent violations structurally [#ex-ante-controls-prevent-violations-structurally] With compliance embedded in the transfer path, non-compliant transactions revert immediately. The blockchain state never changes. There is no "undo" process, no cleanup operation, no regulator seeing evidence of a violation. Every transfer that succeeds is provably compliant. ### Consistent rules across all tokens [#consistent-rules-across-all-tokens] Different tokens may have different compliance rules — accreditation requirements, jurisdiction restrictions, holding limits. When those rules live in a unified policy engine rather than scattered external databases, there is no drift between what the compliance system thinks is enforced and what the token actually enforces. ### Real-time eligibility, not manual overrides [#real-time-eligibility-not-manual-overrides] Legitimate investors are not blocked by stale off-chain status checks. Identity verification happens in real time, credentials are portable across assets, and support tickets from incorrectly blocked investors drop significantly. ## Institutional custody with multi-signature controls [#institutional-custody-with-multi-signature-controls] Banks and asset managers require segregation of duties, dual control, maker-checker workflows, transaction limits, and formal approval processes. Single-key wallets do not meet these standards. ### Multi-signature governance [#multi-signature-governance] Treasury operations require M-of-N approval — multiple independent parties must authorize high-value actions. No single person can transfer all assets, inflate supply, or deploy malicious contract logic. Emergency pause capabilities protect against compromised accounts. ### Key loss recovery [#key-loss-recovery] Institutional custody requires formal key ceremony processes, geographic distribution of key shards, time-locked recovery mechanisms, and clear legal accountability. When a risk committee asks "what happens if your CTO is unavailable?" the answer must be a documented recovery procedure — not "we hope someone finds the password." ### HSM compatibility and custodian integration [#hsm-compatibility-and-custodian-integration] Private keys controlling substantial value belong in hardware security modules (HSMs) — tamper-resistant devices where keys never leave the secure enclave. Platforms should also integrate with configured custody providers, such as Fireblocks and DFNS, so institutions can route signing through approved custody arrangements instead of concentrating control in an application hot wallet. ## Atomic settlement with DvP guarantees [#atomic-settlement-with-dvp-guarantees] Blockchain promises instant settlement, and the token does move instantly on-chain. But the cash leg often settles on traditional banking rails operating on T+1 or T+2 cycles. ### Delivery versus payment eliminates counterparty risk [#delivery-versus-payment-eliminates-counterparty-risk] True atomic settlement — delivery versus payment (DvP) where both legs happen simultaneously or neither happens at all — requires both assets and cash to be on-chain. When the asset and cash legs execute together in a single transaction, there is no window for counterparty default, no reconciliation work, and no need for trusted intermediaries. ### On-chain cash integration [#on-chain-cash-integration] Atomic settlement requires tokenized cash: tokenized deposits, regulated stablecoins, or Central Bank Digital Currency when available. The platform must provide settlement infrastructure that coordinates these atomic exchanges, including multi-party settlement (XvP) where any number of participants exchange tokens atomically. ### Reconciliation eliminated [#reconciliation-eliminated] Operations teams that spend hours matching blockchain transactions against bank statements get that time back. When both legs settle atomically, the reconciliation problem disappears entirely. The promise of instant settlement becomes real. ## Automated corporate actions [#automated-corporate-actions] Quarterly coupons, dividend distributions, shareholder votes, and redemptions should execute programmatically — not through spreadsheets, manual wire transfers, and email chains. ### Scheduled yield management [#scheduled-yield-management] Configure payment schedules once during issuance. The platform calculates entitlements automatically on payment dates. Token holders claim their yields directly through smart contracts with cryptographic proof of entitlement. No spreadsheets, no reconciliation, no manual wire transfers to thousands of investors. ### On-chain voting [#on-chain-voting] Voting power derives automatically from token holdings at snapshot blocks. Votes are tallied on-chain. There is no export-to-Excel, no manual identity matching, no spreadsheet tallying, and no challenge to the results. ### Automated redemptions [#automated-redemptions] An investor submits a redemption request. The platform verifies their holdings, checks liquidity, executes the burn transaction, and processes the cash payment — as a coordinated workflow, not a five-person relay chain. Stock splits, dividend distributions, rights offerings, tender offers — every corporate action that traditionally requires custom projects should be handled by the platform. Operations teams shrink instead of growing. ## Enterprise deployment with SSO, RBAC, and audit trails [#enterprise-deployment-with-sso-rbac-and-audit-trails] Banks and asset managers operate with strict IT controls. A tokenization platform must integrate with these existing enterprise systems, not bypass them. ### Identity and access management [#identity-and-access-management] The platform connects to existing identity providers via SAML or OIDC for single sign-on (SSO). Multi-factor authentication (MFA) aligns with corporate security policies. Role-based access control (RBAC) or attribute-based access control (ABAC) maps to organizational hierarchies. Employee onboarding and offboarding flows through existing IAM systems — not a shadow IT system with separate credentials. ### Audit and compliance logging [#audit-and-compliance-logging] Enterprise audit requirements go beyond basic transaction logs. The platform must capture who accessed what data, who approved what transactions, who changed what settings, with precise timestamps and immutable evidence. Security Information and Event Management (SIEM) integration feeds all events into centralized monitoring. ### Data residency and deployment flexibility [#data-residency-and-deployment-flexibility] European customers need data stored in EU data centers. Asian customers need data in their local region. Banks with specific regulatory constraints need on-premises deployment. The platform must support on-premises, bring-your-own-cloud, and dedicated SaaS deployment models — not force multi-tenant SaaS on a public chain. ### Integration with existing systems [#integration-with-existing-systems] Core banking systems, fund administration platforms, ERPs, and CRMs must connect to the tokenization platform through well-documented APIs, SDKs, and transaction status patterns. Integrators need to know when a call returns immediate data and transaction hashes, and when a long-running blockchain operation returns a transaction ID, current status, and polling URL instead of requiring reverse-engineering. ## Multi-network deployment flexibility [#multi-network-deployment-flexibility] Regulated securities often require permissioned networks where only authorized participants can run validators, submit transactions, or read the ledger. ### Network choice [#network-choice] The platform should support public chains (Ethereum, Polygon), permissioned networks (Hyperledger Besu, Quorum), consortium deployments, and private networks — not assume one chain fits all use cases. ### Predictable costs [#predictable-costs] Gas fees on public networks create unpredictable operational costs. For thousands of small distributions, gas costs become prohibitive. The platform should support networks with predictable fee structures or batching mechanisms. ### Throughput and privacy [#throughput-and-privacy] Ethereum processes about 15 transactions per second. Distributing tokens to 10,000 holders requires batching support. Regulated securities also often require privacy controls — the platform should handle private or encrypted transactions where holder identities, amounts, or trade existence cannot be disclosed publicly. ## Evaluation criteria [#evaluation-criteria] When evaluating digital asset operations platforms, map each capability to specific technical implementations: * **Unified infrastructure** — Is there a single source of truth for ownership? If reconciling multiple systems is required, the platform does not meet this criterion. * **Embedded compliance** — Are rules enforced before or after transfers execute? If checks happen asynchronously after settlement, violations are possible. * **Institutional custody** — Does the platform support multi-signature controls, HSM integration, and formal recovery procedures? * **Atomic settlement** — Can the platform demonstrate DvP where both legs succeed together or revert together? * **Automated servicing** — Are corporate actions handled programmatically or through manual processes? * **Enterprise deployment** — Does the platform integrate with your IAM, support your deployment model, and meet your data residency requirements? ## Key takeaways [#key-takeaways] Understanding these institutional requirements helps you: * **Evaluate platforms concretely** — Ask vendors how they address each capability with specific technical implementations * **Budget accurately** — Unified platforms reduce integration, reconciliation, and operational overhead costs * **Set success criteria** — Define measurable outcomes (T+0 settlement %, compliance breach count, reconciliation hours saved) rather than feature checklists * **Scope your evaluation** — Include realistic workflows that exercise compliance, custody, settlement, and servicing capabilities ## Where to next [#where-to-next] * [DALP solution](/docs/executive-overview/dalp-solution) – How unified lifecycle platforms address these requirements * [DALP overview](/docs/executive-overview/dalp-overview) – SettleMint's implementation in practice * [Glossary](/docs/executive-overview/glossary) – Define technical terms # Market Data Infrastructure Source: https://docs.settlemint.com/docs/executive-overview/market-data-infrastructure How DALP's integrated market data infrastructure provides regulated institutions with reliable, auditable pricing and exchange rate data for digital asset operations. ## Pricing integrity as a regulatory requirement [#pricing-integrity-as-a-regulatory-requirement] For regulated financial institutions, reliable and auditable pricing data is not optional — it is a regulatory requirement. Asset valuations determine compliance thresholds, redemption amounts, yield calculations, and investor reporting. When pricing data is unreliable, every downstream operation is exposed. Traditional approaches to digital asset pricing rely on external price oracle services with opaque provenance, off-chain data feeds that cannot be independently audited, or manual price updates that introduce operational risk and lag. None of these approaches meet the standards that regulated institutions and their regulators expect. ## What DALP provides [#what-dalp-provides] DALP includes a purpose-built **market data infrastructure** — the Feeds system — that gives institutions direct control over the pricing and exchange rate data that governs their digital asset operations. The Feeds system provides: **A centralized, authoritative data registry** All price and exchange rate feeds are registered in a single directory. Any operation on the platform that requires pricing — compliance checks, yield calculations, redemption valuations — draws from this registry. There is one authoritative source of truth for all pricing operations. **Issuer-controlled pricing** Asset issuers can publish cryptographically signed pricing data directly from their own systems. Each price update carries the issuer's digital signature — independently verifiable proof that the issuer stands behind the published value. This is the institutional equivalent of an issuer's authorized valuation, published on-chain with full traceability. **External feed integration with address stability** Where market prices are sourced from external data providers, DALP's adapter layer maintains a stable integration point. Underlying data providers can be changed — to a more reliable source, a newer version, or an in-house feed — without disrupting any system that consumes the data. Integrations point to a stable address that always resolves to the current authoritative feed. **Full auditability** Every price update is recorded on-chain with a timestamp and, for issuer-signed feeds, a cryptographic signature. The complete pricing history is available for regulatory reporting, audit inquiries, and dispute resolution. Asset-scoped feed creation links pricing infrastructure directly to tokenized assets. ## Business impact [#business-impact] | Capability | Business impact | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | Issuer-signed pricing | Immutable, verifiable record of authorized valuations — no dispute over which price was used for a given operation | | Centralized feed registry | Consistent pricing across all assets and operations — no divergence between what compliance checks use and what reporting shows | | On-chain price history | Audit-ready pricing records without manual data extraction or reconciliation | | Stable feed addresses | Upgrade data providers without breaking existing integrations | | Drift protection | Automatic detection of anomalous price updates — safeguard against erroneous or manipulated data | Verification settings centralize the authoritative data feed registry. Create-feed workflows support new market data sources without breaking consumers. ## Who manages market data [#who-manages-market-data] The Feeds system operates under role-based access control. Only personnel assigned the **Feeds Manager** role can register, update, or remove feeds. This restriction ensures that critical financial data cannot be modified by unauthorized parties — a governance control that regulators and internal risk teams expect. ## Regulatory alignment [#regulatory-alignment] Reliable on-chain pricing supports compliance with: * **MiCA (EU)** — Valuation and redemption obligations for asset-referenced tokens and e-money tokens * **AIFMD / UCITS** — NAV calculation requirements for fund-like instruments * **IFRS 13** — Fair value measurement requirements for financial reporting * **MAS (Singapore)** — Pricing transparency requirements for digital capital market services ## Related resources [#related-resources] * [Architecture: Feeds System](/docs/architecture/components/infrastructure/feeds-system) — Registry, feed types, and trust model # Corporate bonds Source: https://docs.settlemint.com/docs/executive-overview/use-cases/corporate-bonds Corporate bond issuance through DALP reduces time-to-market from months to days while automating ongoing servicing. The platform handles primary distribution, coupon payments, and redemption with embedded compliance controls. **Who should read this:** Corporate treasurers, investment bankers, capital markets teams evaluating faster execution and reduced operational overhead. **Business value:** Cut issuance timelines by 80%, eliminate transfer agent fees, calculate quarterly coupon entitlements with on-demand claiming, and gain real-time cap table visibility. ## Business challenge [#business-challenge] MidCorp Industries needs to raise $50 million through a three-year bond offering. Traditional underwriting involves months of roadshows, weeks of settlement, and ongoing manual processes for coupon payments and investor communications. ### Traditional approach [#traditional-approach] Tokenized corporate bonds with real-time pricing and issuer information. ## How DALP solves it [#how-dalp-solves-it] **DALP's lifecycle management capabilities eliminate manual processes:** * **DvP settlement** for atomic primary distribution and secondary trading * **Vault custody** for secure treasury management with multi-signature controls * **Scheduled yield calculations** for quarterly coupon entitlements without manual spreadsheet work ### Configuration and deployment [#configuration-and-deployment] Bond configuration captures name, symbol, and jurisdiction from the outset. Bond setup continues with instrument selection and issuance-specific parameters. MidCorp's treasury team uses the Asset Designer to configure a bond token: $50 million principal, 5% annual coupon paid quarterly, three-year maturity, USD denomination. They select a Regulation D compliance template to limit the offering to accredited US investors. The platform generates the smart contract with compliance controls embedded. **Critically, DALP also deploys a fixed yield schedule contract that will calculate quarterly coupon entitlements for claim-based distribution** and connects the bond to the XvP settlement system for atomic trading. MidCorp uploads their offering memorandum, which gets cryptographically hashed and linked to the token metadata. Legal documentation remains permanently associated with the asset. Bond instrument details capture maturity and debt-specific metadata. ### Investor onboarding with embedded compliance [#investor-onboarding-with-embedded-compliance] Accredited investors connect through the Investor Portal and complete KYC verification via an integrated provider. Their OnchainID receives an accreditation claim. Only wallet addresses linked to verified, accredited identities can receive bond tokens. Non-accredited addresses cannot receive tokens; transfers revert automatically. ### Primary distribution with DvP settlement [#primary-distribution-with-dvp-settlement] MidCorp allocates tokens to a subscriber list. **Using DALP's XvP settlement system, investors exchange cash (stablecoins or tokenized deposits) for bond tokens atomically.** If an investor's payment fails, they don't receive tokens. If token transfer fails (e.g., compliance violation), payment doesn't leave their wallet. Both legs execute together or both revert: true delivery versus payment. The platform enforces that every recipient meets eligibility requirements, eliminating risk of non-compliant distribution. Settlement is instant with T+0 finality. No reconciliation required. Bond pricing and valuation connect settlement and servicing to issuer-approved economics. ### Scheduled coupon calculations via yield schedules [#scheduled-coupon-calculations-via-yield-schedules] **DALP's fixed yield schedule contract calculates quarterly entitlements without manual spreadsheet work.** Every quarter, the yield system calculates interest owed to each holder based on proportional ownership at the snapshot block. Token holders claim their coupons directly through the smart contract with cryptographic proof of entitlement. The platform supports claims through tokenized cash settlement: token holders claim their distributions when stablecoins or tokenized deposits fund the yield wallet. Jurisdictions that require human review can keep a manual approval step before disbursement. **This removes manual entitlement calculations for large holder lists.** The yield schedule is configured once during issuance; entitlements are calculated automatically on payment dates, and investors claim their coupons on-demand. ### Vault custody for treasury operations [#vault-custody-for-treasury-operations] **MidCorp's bond proceeds are held in DALP's multi-signature vault** with configurable approval requirements. Treasury operations require multiple signatures. No single person can move funds unilaterally. The vault provides: * **Maker-checker workflows** where one admin proposes and others approve * **Emergency pause** capabilities to freeze operations if accounts are compromised * **Full audit trails** showing every proposal, approval, and execution * **Role-based access control** separating operational roles This provides bank-grade custody without requiring integration with external custodians. ### Real-time cap table visibility [#real-time-cap-table-visibility] MidCorp's IR team sees current ownership in real time through the admin dashboard. No waiting for monthly reports from a transfer agent. When investors trade bonds on secondary markets (if permitted through DALP's XvP settlement system with atomic DvP), the cap table updates immediately. ### Maturity and redemption [#maturity-and-redemption] At three years, the platform handles redemption automatically: bond tokens are burned, principal plus final interest is distributed, and the issuance concludes with complete audit trails showing every payment, every holder, and every compliance check throughout the lifecycle. ## Key capabilities [#key-capabilities] | Capability | Traditional | With DALP | | -------------------------- | -------------------------------------- | ---------------------------------------------- | | **Issuance timeline** | 8-12 weeks | Days | | **Settlement** | T+2 to T+5 | Instant atomic finality | | **Coupon processing** | Manual calculation & wire instructions | Scheduled calculations with on-demand claiming | | **Cap table updates** | Monthly from transfer agent | Real-time on-chain | | **Compliance enforcement** | Post-trade surveillance | Pre-transfer validation | | **Transfer agent fees** | Ongoing quarterly fees | Eliminated | ## Measurable outcomes [#measurable-outcomes] **Time savings** – Illustrative deployments reduce issuance timelines from 8-12 weeks to under two weeks by eliminating roadshow logistics and settlement delays. **Cost reduction** – Transfer agent fees for ongoing servicing are eliminated. Legal review time decreases because compliance templates are pre-vetted. Operations overhead drops as corporate actions execute automatically. **Secondary market liquidity** – Instant settlement enables institutional investors to trade positions knowing finality arrives in minutes rather than days. **Compliance certainty** – Zero non-compliant transfers because only verified accredited investors can hold tokens. Transfer restrictions are enforced at the smart contract level. The diagram above shows the DALP bond lifecycle, from configuration through automated servicing to redemption. ## Compliance considerations [#compliance-considerations] Bond tokens operate under securities regulations requiring: * **Accredited investor verification** – Enforced via OnchainID claims before token transfer * **Transfer restrictions** – Lock-up periods, ROFR rights, and volume limits configured in compliance modules * **Jurisdiction controls** – Geographic restrictions via country allow/block lists * **Audit trails** – Every transaction, compliance check, and lifecycle event recorded on-chain For detailed compliance architecture, see [Compliance & Security](/docs/executive-overview/compliance-security). ## Implementation checklist [#implementation-checklist] 1. Define bond terms (principal, coupon rate, maturity, currency) 2. Select compliance template (Reg D, Reg S, or custom ruleset) 3. Integrate KYC/AML provider for investor verification 4. Fund the yield wallet with stablecoins or tokenized deposits for coupon distributions 5. Upload offering documents and link to token metadata 6. Deploy smart contract and conduct primary distribution 7. Configure coupon scheduler and test distribution workflow 8. Set up admin dashboard access for IR and operations teams ## Next steps [#next-steps] * Review [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) to understand embedded compliance * Explore [Developer Documentation](/docs/developer-guides) for technical integration details * Contact your SettleMint representative to discuss a bond tokenization pilot # Deposit certificates Source: https://docs.settlemint.com/docs/executive-overview/use-cases/deposits Time deposits meet blockchain in this modernization of certificates of deposit. DALP automates the entire lifecycle—from issuance and vault-secured reserves through yield accrual to redemption—while maintaining FDIC insurance and regulatory compliance. Real-time observability replaces quarterly statements, giving both customers and treasury teams continuous visibility into deposit positions and reserve health. **Who should read this:** Retail banking product managers exploring digital-first deposit products, treasury operations teams managing reserve ratios, and compliance officers ensuring FDIC coverage. **Business value:** Differentiate with tech-savvy customers who expect real-time visibility, eliminate manual maturity tracking and interest calculations, maintain full FDIC compliance, and gain operational dashboards that replace spreadsheet-based reserve monitoring. ## Customer flow: from legacy friction to digital clarity [#customer-flow-from-legacy-friction-to-digital-clarity] Meet Sarah, a millennial software engineer with $25,000 to park for six months. She's done the math: a 4% APY beats her savings account, and FDIC insurance gives her peace of mind. But the experience? Opening a traditional certificate of deposit means visiting a branch (or navigating a clunky web form), receiving a PDF "certificate" via email, and then... silence. She checks her banking app periodically, but there's no visibility into daily interest accrual. At maturity, she receives a letter weeks after the fact, informing her the CD rolled over automatically because she missed the tiny renewal window. Now imagine Sarah's experience with a digital deposit certificate on DALP. She verifies her identity once (leveraging the bank's existing KYC), deposits funds, and immediately receives a digital token in her wallet—a certificate she actually controls. Her dashboard shows the principal, today's accrued interest, the exact maturity date with a countdown, and her projected payout. At maturity, she initiates redemption with a single click. Funds hit her account within hours, not days. No surprise rollovers. No wondering if the bank "forgot" her deposit. This isn't a futuristic concept. It's how deposits work when custody, compliance, and customer experience converge on a blockchain platform built for regulated financial products. ## Traditional deposit operations: manual tracking at scale [#traditional-deposit-operations-manual-tracking-at-scale] Metropolitan Bank issues thousands of time deposits annually. Behind the scenes, deposit operations teams juggle: * Maturity date spreadsheets across multiple product types (3-month, 6-month, 12-month CDs) * Batch interest calculations running overnight, prone to calculation errors on leap years or partial periods * Quarterly statement generation requiring manual reconciliation against core banking records * Early withdrawal requests triggering manual penalty calculations (different formulas per product vintage) * Reserve ratio monitoring via end-of-day reports that are stale by morning * FDIC reporting requirements pulling data from disparate systems When a customer calls asking "How much interest have I earned so far this month?", the call center representative can't answer without placing them on hold to run a special query. Customer frustration grows. Operational costs mount. And the bank's reputation as "tech-forward" rings hollow when the deposit experience feels unchanged since the 1980s. *Traditional deposit lifecycle: separate disconnected systems, delayed visibility, and manual intervention at every stage.* Tokenized deposits with full regulatory compliance. ## How DALP transforms deposit operations [#how-dalp-transforms-deposit-operations] DALP doesn't just digitize certificates—it rebuilds the entire deposit lifecycle around automated custody, real-time yield management, and continuous observability. Here's how the platform handles Sarah's deposit from issuance to redemption. ### Product configuration and vault preparation [#product-configuration-and-vault-preparation] Metropolitan's treasury team configures the 6-month CD product in DALP: 4% APY, $1,000 minimum, $250,000 maximum (staying within FDIC single-account coverage). They designate a **vault contract** to custody the fiat reserves backing all deposits. This isn't a metaphor—the vault is an on-chain escrow holding the bank's reserves, with **multi-signature controls** requiring both treasury and compliance approval for any withdrawal. The observability stack immediately starts tracking vault metrics. Treasury can view the **reserve ratio dashboard** showing total deposit token supply against vault holdings. If the ratio drops below the configured threshold (e.g., 105% to maintain a safety buffer), automated alerts fire to Slack and email. No more waiting for end-of-day reports to discover reserve shortfalls. ### Customer onboarding with identity verification [#customer-onboarding-with-identity-verification] Sarah completes KYC through Metropolitan's existing vendor integration. Her verification status—including accredited investor determination if needed for other products—links to her **OnchainID**, a blockchain-based identity registry. This isn't a new identity system; it's a compliance bridge connecting traditional KYC outcomes to on-chain eligibility. The platform enforces per-customer aggregate limits across all deposit products. If Sarah already holds $200,000 in other Metropolitan deposit tokens, the system prevents her from purchasing more than $50,000 of this CD to stay within FDIC coverage. Compliance rules execute automatically at transaction time, not via manual review after the fact. The deposit tokenization workflow enables banks to bring customer deposits on-chain. ### Deposit issuance: atomic settlement with vault custody [#deposit-issuance-atomic-settlement-with-vault-custody] Sarah transfers $10,000 to Metropolitan's designated deposit account. Once the ACH clears (typically 1-2 business days), Metropolitan initiates token issuance. The platform executes an **atomic transaction** that: 1. Mints 10,000 deposit tokens to Sarah's wallet 2. Records the issuance timestamp, maturity date (180 days forward), and product terms on-chain 3. Escrows the $10,000 fiat equivalent into the vault contract 4. Updates the reserve ratio dashboard in real-time This is **Delivery vs Payment (DvP)** at work—one of DALP's core DALP lifecycle capabilities. Sarah receives her tokens only after the platform confirms vault custody of reserves. If reserves aren't sufficient, the transaction reverts. No partial states. No reconciliation gaps. The chain indexer immediately picks up the issuance event, making Sarah's deposit visible in the customer dashboard within seconds. The **transaction observability panel** shows the issuance transaction, gas costs, and confirmation time. Operations teams can drill into any deposit's on-chain history from the admin dashboard without touching a blockchain explorer. ### Time-lock enforcement with compliance context [#time-lock-enforcement-with-compliance-context] The smart contract enforces the 6-month maturity via a **time-lock compliance module**. This isn't a simple timestamp check—it's an ERC-3643 compliance rule integrated into the token's transfer restrictions. Sarah cannot transfer her tokens to another wallet, cannot redeem early, and cannot use them as collateral (unless Metropolitan configures those as permitted actions). Why this matters for compliance: FDIC insurance requires deposits to meet specific criteria, including holding period commitments for certain product types. Time-lock enforcement provides cryptographic proof that no deposit was redeemed early without penalty, satisfying audit requirements without manual log reviews. The **compliance dashboard** tracks time-lock status across all outstanding deposits. Treasury can see a maturity calendar showing redemption dates for the next 90 days, helping forecast liquidity needs. If a customer requests early withdrawal, the system calculates penalties according to the configured penalty module (e.g., forfeit 90 days of interest for a 6-month CD withdrawn after 3 months). Note: Early withdrawal penalty calculations require a configured penalty module or custom addon. The base time-lock implementation prevents transfer and redemption until maturity; sophisticated penalty logic (partial redemptions, tiered penalties based on hold period) is an optional configuration that can be tailored to your product requirements and regulatory environment. ### Yield accrual: real-time visibility into earnings [#yield-accrual-real-time-visibility-into-earnings] Sarah's dashboard shows her deposit growing daily. The platform tracks interest accrued based on the 4% APY using one of two approaches: **On-chain accrual (higher cost, maximum transparency):** A scheduled job calls a contract function daily to update each deposit's accrued interest. Every update is a blockchain transaction, creating an immutable audit trail. Gas costs are predictable (approximately 50,000 gas per update), making this viable for smaller deposit portfolios or premium products where customers pay for real-time on-chain updates. **Indexer-backed calculation (recommended for scale):** The platform calculates accrued interest off-chain from indexed issuance data, using the issuance timestamp, maturity date, and APY stored in the issuance event. The calculation is deterministic—anyone can verify the displayed interest by querying the same on-chain data. At redemption, the on-chain smart contract performs the final interest calculation to determine payout, ensuring the off-chain display was accurate. Most implementations use the chain indexer for cost efficiency while maintaining full transparency. The **yield monitoring dashboard** shows aggregate interest accrued across all deposits, helping treasury forecast interest payment obligations. If actual interest payments (tracked via vault withdrawals) diverge from projected accrual, automated variance alerts notify treasury teams. Sarah doesn't care about the implementation. She sees her balance grow from $10,000.00 to $10,054.79 after 30 days, then $10,110.41 after 60 days. No guessing. No waiting for quarterly statements. The transparency builds trust, and Sarah tells her friends about her "bank that actually shows you what's happening." ### Maturity and redemption: automated settlement [#maturity-and-redemption-automated-settlement] At 180 days, Sarah's tokens become redeemable. She receives an in-app notification (and email, if configured) alerting her that maturity has arrived. Unlike traditional CDs that may auto-renew by default, DALP makes redemption explicit—Sarah must initiate the transaction. She clicks "Redeem" in the dashboard. The platform: 1. Burns her 10,000 deposit tokens (removing them from circulation) 2. Calculates final interest: $10,000 × 4% × (180/365) = $197.26 3. Triggers a vault withdrawal of $10,197.26 to Metropolitan's operations account 4. Initiates an ACH transfer to Sarah's designated bank account via core banking integration The **DvP settlement dashboard** tracks redemption transactions in real-time. Treasury sees the vault withdrawal, the token burn, and the ACH initiation—all linked to Sarah's deposit. If the ACH fails (wrong account number, closed account), the system flags the failed settlement for manual intervention, but the vault withdrawal and token burn remain recorded on-chain, preventing double-redemption attempts. Sarah receives her $10,197.26 within one business day. The entire redemption flow—from her click to funds in her account—is fully automated. No operations staff involvement unless the ACH fails. ### FDIC compliance and audit readiness [#fdic-compliance-and-audit-readiness] The platform enforces per-customer deposit limits at transaction time via the compliance module. When Sarah attempts to purchase additional deposit tokens, the system queries her existing holdings across all Metropolitan deposit products. If the aggregate would exceed $250,000 (FDIC single-account coverage), the transaction reverts with a clear error message: "Purchase would exceed FDIC coverage limit." All deposit issuances, transfers (if permitted), and redemptions are recorded on-chain with timestamps and transaction hashes. The **compliance audit dashboard** provides filterable views of: * All deposits issued in a date range (for FDIC Call Report preparation) * Per-customer aggregate holdings at any historical point in time * Deposits redeemed early with penalties applied * Reserve ratio history, showing vault holdings vs. deposit supply over time When FDIC examiners request documentation, Metropolitan exports the relevant data from the dashboard—no manual log diving, no reconstructing spreadsheets from backup tapes. Every deposit's lifecycle is cryptographically verified and immutable. ### Reserve ratio monitoring and treasury operations [#reserve-ratio-monitoring-and-treasury-operations] The vault contract holds Metropolitan's reserves backing all deposit tokens. The **vault observability dashboard** provides treasury teams with: * **Real-time reserve ratio:** Vault balance ÷ Total deposit token supply. Target: ≥ 105% * **Maturity forecasting:** Expected redemptions over the next 30/60/90 days, helping treasury schedule fund movements * **Interest obligations:** Aggregate interest accrued across all deposits, updated daily * **Vault withdrawal history:** Every funds movement out of the vault, with approvals and transaction hashes If the reserve ratio drops below 105% (perhaps due to a large batch of redemptions), automated alerts fire. Treasury can proactively transfer additional funds into the vault to maintain the buffer, with all movements tracked via multi-signature approval workflows. The observability stack also monitors **settlement performance**: average time from redemption request to ACH initiation, failed ACH rates, and redemption abandonment (customers who don't redeem at maturity). These operational metrics help Metropolitan optimize the customer experience and identify bottlenecks in the integration with core banking systems. ## Key capabilities: before and after comparison [#key-capabilities-before-and-after-comparison] | Capability | Traditional | With DALP | | ------------------------- | --------------------------------------- | --------------------------------------------------------- | | **Certificate format** | Paper or PDF via email | Digital token in customer wallet | | **Maturity tracking** | Excel spreadsheets, manual calendars | Smart contract time-lock with compliance dashboard | | **Interest visibility** | Quarterly statements, call center | Real-time dashboard showing daily accrual | | **Reserve monitoring** | End-of-day reports, stale by morning | Live vault dashboard with automated alerts | | **Early withdrawal** | Manual penalty calculation, error-prone | Configured penalty module (optional), deterministic rules | | **Redemption processing** | Manual notification, wire initiation | Automated burn, vault withdrawal, ACH trigger | | **FDIC compliance** | Manual record-keeping, audit scramble | Automated per-customer limits, export-ready audit trail | | **Customer support** | "Let me check and call you back" | Customer sees same data as operations team | ## Measurable outcomes: beyond operational efficiency [#measurable-outcomes-beyond-operational-efficiency] **Customer acquisition and retention:** Metropolitan's tech-forward customers appreciate blockchain-based deposits not as a gimmick, but as a tangible improvement. Real-time interest visibility, instant redemption requests, and wallet-based certificates differentiate Metropolitan from competitors still mailing PDFs. Early pilots show 40% higher satisfaction scores among customers using digital deposit products compared to traditional CDs, with 25% of pilot participants referring friends (vs. 5% referral rate for traditional products). **Operational cost reduction:** Eliminating manual maturity tracking, interest calculations, and redemption processing reduces deposit operations costs by an estimated 60-70% at scale. One operations analyst can manage what previously required a team, with the observability dashboards replacing spreadsheet-based workflows. Gas costs for on-chain transactions (issuance, redemption) are predictable and low (under $1 per deposit at current blockchain gas prices), making the economics favorable even for small deposits. **Treasury efficiency:** Real-time reserve ratio monitoring replaces end-of-day reports, enabling proactive liquidity management. Treasury teams gain 12-24 hours of lead time for funding decisions, reducing reliance on expensive overnight borrowing. The maturity forecasting dashboard provides accurate 90-day liquidity projections, improving cash management and reducing idle reserves (improving net interest margin by 5-10 basis points based on pilot data). **Compliance confidence:** Automated FDIC limit enforcement prevents violations before they occur, eliminating the risk of discovering coverage gaps during audits. Examiners can verify deposit records on-chain, reducing the burden on compliance staff to reconstruct historical positions. The immutable audit trail satisfies regulators' increasing expectations for digital record-keeping, positioning Metropolitan ahead of future regulatory requirements. **Transparency and trust:** Customers see exactly what the bank sees. No hidden calculations, no "trust us" on interest accrual, no surprise fees. This transparency builds customer trust, especially among younger demographics skeptical of traditional banking opacity. When Sarah's friends ask "How do you know the bank isn't skimming your interest?", she can point to the on-chain transaction history. The blockchain isn't just a technology choice—it's a trust mechanism. *DALP deposit lifecycle: customer experience (purple), DALP platform capabilities (blue), and treasury/operations observability (pink) working in concert. DvP settlement ensures atomic issuance and custody, yield management provides real-time visibility, and vault monitoring enables proactive treasury management.* ## Compliance considerations: meeting regulatory expectations [#compliance-considerations-meeting-regulatory-expectations] Digital deposit certificates operate under existing banking regulations—DALP provides the infrastructure to comply more efficiently, not to circumvent requirements. ### FDIC insurance and coverage limits [#fdic-insurance-and-coverage-limits] The platform enforces per-customer aggregate limits via the compliance module, preventing any customer from holding more than $250,000 across all deposit products (or higher limits for joint accounts, retirement accounts, etc., depending on configuration). This enforcement happens at transaction time: if a purchase would exceed the limit, the transaction reverts with a clear error message. For FDIC reporting (Call Reports, examination requests), the platform provides export functionality that generates customer-level deposit holdings as of any historical date. The on-chain record is the source of truth, eliminating the risk of data loss or manipulation. ### Customer verification and KYC requirements [#customer-verification-and-kyc-requirements] OnchainID links traditional KYC outcomes to on-chain eligibility. Metropolitan continues using its existing KYC vendors (Jumio, Onfido, etc.)—the platform simply records the verification status on-chain to enable automated compliance checks. Customers don't experience a "blockchain KYC process"; they complete the same verification flow as traditional products, with the results enabling digital deposit eligibility. ### Interest rate disclosure and truth in savings [#interest-rate-disclosure-and-truth-in-savings] Deposit terms (APY, maturity date, penalty calculations) are recorded in the token's metadata and issuance events. Customers receive clear disclosures at purchase time, with terms accessible via the dashboard at any point during the hold period. This transparency satisfies Truth in Savings Act requirements for rate disclosure and account terms. ### Early withdrawal penalties [#early-withdrawal-penalties] If Metropolitan offers early redemption (many time deposits prohibit it entirely), the penalty module calculates forfeitures deterministically. Common penalty structures: * **Forfeit 90 days of interest** for 6-month CDs redeemed before maturity * **Forfeit 180 days of interest** for 12-month CDs * **Tiered penalties** based on hold period (smaller penalties if you've held most of the term) The penalty calculation is embedded in the smart contract, ensuring consistency and auditability. No customer receives a "one-time exception" that creates compliance risk—the code enforces the disclosed terms uniformly. ### Record retention and audit trails [#record-retention-and-audit-trails] All deposit transactions (issuance, interest accrual updates if on-chain, redemptions) are recorded on-chain with timestamps and transaction hashes. This immutable ledger satisfies record retention requirements (typically 5-7 years for deposit records) without requiring backup systems or manual log archival. The compliance dashboard provides filtered views and exports for audits, examinations, and regulatory filings. Examiners can verify records independently by querying the blockchain directly, though most will use the dashboard's user-friendly export functionality. For detailed compliance architecture and how OnchainID enables automated eligibility checks, see [Compliance & Security](/docs/executive-overview/compliance-security). ## Limitations and considerations: honest assessment [#limitations-and-considerations-honest-assessment] DALP provides significant operational improvements, but understanding the boundaries helps set realistic expectations. ### Early withdrawal penalties require configuration [#early-withdrawal-penalties-require-configuration] The base time-lock implementation prevents any redemption before maturity—the tokens are non-transferable and non-redeemable until the lock expires. If your product offers early withdrawal with penalties (as most retail CDs do), you must configure a penalty module or develop a custom addon. The penalty module supports common structures (forfeit X days of interest), but complex tiered penalties or partial redemptions may require custom development. Budget for this during implementation if early withdrawal is a product requirement. ### Interest accrual visibility requires indexing infrastructure [#interest-accrual-visibility-requires-indexing-infrastructure] Real-time interest visibility (showing Sarah her balance grow daily) requires either: 1. **On-chain accrual updates:** Daily transactions calling a contract function to update each deposit's accrued interest. Provides maximum transparency (every update is a blockchain transaction) but incurs gas costs. Viable for smaller portfolios or premium products where customers pay for real-time on-chain updates. 2. **Indexer-backed calculation (recommended):** Off-chain calculation based on issuance events and product terms. No per-deposit gas costs, scales to millions of deposits, and relies on the platform's PostgreSQL-backed chain indexer for dashboard visibility. Most implementations use the chain indexer. The calculation is deterministic (anyone can verify it matches on-chain terms), but you do depend on indexer uptime for dashboard visibility. ### FDIC aggregate limits across products require coordination [#fdic-aggregate-limits-across-products-require-coordination] The compliance module enforces per-customer limits within DALP, but if customers hold traditional (non-tokenized) deposits at your institution, you must sync customer holdings from your core banking system. The platform provides integration hooks, but ensuring real-time synchronization requires coordination between DALP and core banking APIs. If that integration isn't real-time, there's a window where a customer could exceed FDIC coverage by opening a traditional CD after maxing out their tokenized deposit holdings. Most implementations use daily sync jobs and accept the small risk window, with periodic reconciliation reports flagging any overages for manual resolution. ### Redemption timing depends on core banking integration [#redemption-timing-depends-on-core-banking-integration] DALP automates the on-chain settlement (token burn, vault withdrawal trigger) instantly, but the final funds transfer to customers' bank accounts depends on your core banking system's ACH processing. If your core banking system batches ACH initiations once daily, customers experience same-day redemption only if they redeem before the cutoff time. The platform can trigger immediate wire transfers instead of ACH for premium customers or large redemptions, but wire fees may apply. Setting customer expectations appropriately (e.g., "Redemption requests submitted before 2 PM ET process same business day") prevents satisfaction issues. ## Next steps: exploring deposit tokenization [#next-steps-exploring-deposit-tokenization] If Metropolitan Bank (or your institution) is considering digital deposit products, these resources provide deeper technical and architectural context: * Review [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) to understand how compliance modules enforce time-locks, FDIC limits, and transfer restrictions * Explore [DALP lifecycle capabilities](/docs/executive-overview/dalp-solution) to see how DvP settlement and vault custody apply across all asset types, not just deposits * Examine [Developer Documentation](/docs/developer-guides) for core banking integration patterns (ACH triggers, vault funding, customer data sync) * Check the [Observability stack guide](/docs/architecture/operability/observability) to understand dashboard capabilities, alerting configuration, and metrics available for treasury monitoring Ready to discuss a pilot program? Contact your SettleMint representative to design a phased rollout—starting with a single deposit product and select customers, proving operational efficiency and customer satisfaction before scaling to your full deposit portfolio. Digital deposits aren't about replacing FDIC insurance with blockchain hype. They're about giving customers transparency, operations teams automation, and treasury teams real-time visibility—all while maintaining the regulatory compliance and safety that make deposits trustworthy. That's the promise DALP delivers. # Equities Source: https://docs.settlemint.com/docs/executive-overview/use-cases/equities Equity tokenization through DALP enables companies to issue, manage, and service digital shares with embedded governance voting, real-time cap table management, and automated dividend distribution — replacing transfer agents, paper proxies, and manual record-keeping. **Who should read this:** Corporate secretaries, CFOs, investor relations teams, and legal counsel evaluating tokenized share structures. **Business value:** Eliminate transfer agent fees, enable real-time cap table visibility, automate dividend entitlement calculations with on-demand claiming, and give shareholders on-chain governance voting rights. ## Business challenge [#business-challenge] NovaTech is a growth-stage company with 2,000 shareholders across multiple share classes. Managing shareholder records, processing dividend payments, conducting proxy votes, and handling secondary transfers consumes significant resources and introduces delays at every step. ### Traditional approach [#traditional-approach] Tokenized equities with on-chain compliance. ## How DALP solves it [#how-dalp-solves-it] ### Share issuance with embedded rights [#share-issuance-with-embedded-rights] NovaTech tokenizes its equity structure as compliant digital securities. Each token represents one share and carries embedded ownership rights, dividend entitlements, and governance voting power. Share classes (common, preferred) are represented as separate token deployments with distinct compliance rules and economic terms. Equity token details with company information and voting rights parameters. ### On-chain governance voting [#on-chain-governance-voting] The platform gives shareholders on-chain voting rights proportional to their token holdings. Shareholders delegate voting power and cast votes on corporate resolutions directly through the platform. Historical balance snapshots ensure voting weight reflects ownership at the record date, not at the time of the vote — preventing vote manipulation through last-minute transfers. ### Shareholder onboarding [#shareholder-onboarding] Investors complete identity verification through the platform's identity system. Their verified credentials enable participation across multiple NovaTech share classes without re-verification. Only verified investors can receive equity tokens; transfers to unverified recipients are blocked automatically. ### Real-time cap table [#real-time-cap-table] Every share transfer updates the cap table instantly. NovaTech's IR team sees current ownership distribution, shareholder counts per class, and concentration metrics in real time. No waiting for monthly transfer agent reconciliation. Regulatory reporting pulls current data on demand. ### Dividend distribution [#dividend-distribution] When NovaTech declares a dividend, the yield engine calculates entitlements for every shareholder based on their balance at the record date snapshot. Shareholders claim their dividends on-demand through the platform. What previously required wire instructions to 2,000 bank accounts now settles through claim-based distribution with instant finality. The platform supports: * **Tokenized cash settlement** — shareholders claim distributions funded by stablecoins or tokenized deposits in the yield wallet * **Off-chain payment adapters** — integration with treasury systems for traditional wire/ACH settlement * **Per-class calculations** — different dividend rates for common vs preferred shares handled by separate token deployments ### Custodian controls [#custodian-controls] The custodian role enables corporate actions that traditional equity structures require: freezing shares during legal disputes, forced transfers for court orders, and account recovery for lost wallets. These operations execute through role-based access controls with full audit trails. ### Secondary trading [#secondary-trading] When shareholders want to sell, the platform facilitates compliant transfers through DALP's XvP settlement system. Compliance checks verify buyer eligibility before settlement. Transfer restrictions — right of first refusal, lock-up periods, maximum holder limits — are enforced programmatically. Settlement happens atomically via DvP. ## Key capabilities [#key-capabilities] | Capability | Traditional | With DALP | | ----------------------- | --------------------------------- | ------------------------------------------- | | **Cap table updates** | Monthly reconciliation | Real-time on-chain | | **Dividend processing** | Wire to 2,000 accounts, days | Calculated entitlements, on-demand claiming | | **Governance voting** | Paper proxies, solicitation firms | On-chain voting with balance snapshots | | **Secondary transfers** | Weeks of paperwork | Hours with embedded compliance | | **Transfer agent fees** | Ongoing monthly/quarterly fees | Eliminated | | **Shareholder reports** | Quarterly statements | Real-time dashboard access | ## Measurable outcomes [#measurable-outcomes] **Operational efficiency** — Transfer agent fees and manual record-keeping eliminated. Dividend distribution that consumed days of coordination now executes through automated entitlement calculation and self-service claiming. **Governance participation** — On-chain voting removes the friction of paper proxies and solicitation firms. Shareholders vote directly, increasing participation rates and reducing governance costs. **Cap table accuracy** — Real-time ownership records eliminate reconciliation errors. Regulatory reporting and investor communications reference a single source of truth that updates with every transaction. **Secondary liquidity** — Compliant share transfers that took weeks of legal paperwork now settle in hours with compliance enforced at the platform level. The diagram above shows the DALP equity lifecycle, from tokenization through automated governance and dividend distribution to compliant secondary trading. ## Compliance considerations [#compliance-considerations] Equity tokens operate under securities regulations requiring: * **Investor verification** — enforced via identity claims for KYC/AML * **Transfer restrictions** — lock-up periods, ROFR rights, maximum holder limits configured in compliance modules * **Jurisdiction controls** — geographic restrictions via country allow/block lists * **Shareholder caps** — investor count limits per share class for regulatory thresholds (e.g., Section 12(g) reporting triggers) * **Audit trails** — every transaction, vote, compliance check, and corporate action recorded on-chain For detailed compliance architecture, see [Compliance & Security](/docs/executive-overview/compliance-security). ## Implementation checklist [#implementation-checklist] 1. Define share structure (classes, par value, total authorized shares) 2. Select compliance template (Reg D, Reg S, or custom ruleset) 3. Integrate KYC/AML provider for investor verification 4. Configure dividend payment adapter (tokenized cash or off-chain settlement) 5. Upload corporate documents (articles of incorporation, shareholder agreement) and link to token metadata 6. Deploy asset with voting extensions enabled 7. Configure custodian roles and multi-signature controls 8. Test dividend distribution, voting workflow, and secondary transfer restrictions ## Limitations and considerations [#limitations-and-considerations] * **Voting complexity** — On-chain voting supports simple majority/quorum models; complex voting structures (cumulative voting, supermajority per class) may require custom addon development * **Dividend calculation** — Out-of-the-box implementation supports pro-rata distributions; preferential dividend waterfalls across share classes use separate token deployments with independent yield schedules * **Regulatory approval** — Legal counsel should review tokenized share structure for compatibility with corporate charter and applicable securities regulations ## Next steps [#next-steps] * Review [Compliance & Security](/docs/executive-overview/compliance-security) to understand embedded compliance controls * Explore [Developer Documentation](/docs/developer-guides) for governance and voting customization * Contact your SettleMint representative to discuss an equity tokenization pilot # Use cases Source: https://docs.settlemint.com/docs/executive-overview/use-cases Seven asset classes, one lifecycle engine. See how bonds, equities, funds, real estate, precious metals, stablecoins, and deposits share the same DvP settlement, vault custody, and yield automation—eliminating the integration chaos that fragments traditional tokenization stacks. **Who should read this:** CFOs, treasurers, product managers, and compliance officers evaluating tokenization for specific asset classes. **Business value:** Understand how unified lifecycle infrastructure reduces time-to-market, operational overhead, and compliance risk across diverse regulated assets. DALP's Asset Designer supports tokenization across all major asset classes. ## The fragmentation problem nobody talks about [#the-fragmentation-problem-nobody-talks-about] Here's the uncomfortable truth about most tokenization projects: they succeed at minting a token, then immediately hit a wall when quarterly coupons come due. Traditional approaches often split issuance, compliance, custody, and servicing across separate operating layers. Each integration adds engineering work and introduces reconciliation risk. When you need to pay bond coupons, distribute rental income, or process fund redemptions, you discover that the hard part is not minting the token. It is operating the asset after launch. The result? Spreadsheets. Manual transfers. Delayed distributions. Exactly the friction tokenization promised to eliminate. ## Why unified lifecycle infrastructure changes everything [#why-unified-lifecycle-infrastructure-changes-everything] DALP solves this with **Digital Asset Lifecycle Platform (DALP)** primitives that work across every asset type: **Delivery vs Payment (DvP)** ensures atomic settlement—the token and payment move together or not at all, eliminating counterparty risk. Bond issuance closes in seconds instead of days because settlement is cryptographically guaranteed. **Vault custody** separates operational control from economic ownership. Your legal team requires board approval for treasury transfers? Configure the vault with multi-signature rules. Investors earn yield while assets stay locked during holding periods. **Yield automation** handles scheduled distributions without manual intervention. Bond coupons, fund dividends, rental income, stablecoin interest—all calculated on-chain and distributed automatically. Token holders claim their entitlements when convenient, eliminating wire transfer coordination. These capabilities aren't bolted on after the fact. They're embedded in the asset layer, available from day one, consistent across every asset class. Asset insights show how one lifecycle engine spans multiple tokenized asset classes. ## Asset class coverage [#asset-class-coverage] The platform handles seven primary categories, each with distinct regulatory requirements yet sharing the same lifecycle engine: | Asset class | Primary use | Settlement mechanism | Typical compliance | | ------------------------------------------------------------------------- | --------------------------------- | -------------------- | ------------------------------------------- | | **[Corporate Bonds](/docs/executive-overview/use-cases/corporate-bonds)** | Capital raising, debt instruments | DvP + Yield | Accredited investor, transfer restrictions | | **[Equities](/docs/executive-overview/use-cases/equities)** | Company shares, dividends | DvP + Yield | Securities laws, shareholder rights | | **[Private Equity](/docs/executive-overview/use-cases/private-equity)** | Fund units, LP servicing | DvP + NAV tracking | Qualified purchaser, holding periods | | **[Real Estate](/docs/executive-overview/use-cases/real-estate)** | Fractionalized ownership | DvP + Yield | Securities laws, FIRPTA withholding | | **[Precious Metals](/docs/executive-overview/use-cases/precious-metals)** | Commodity-backed tokens | DvP + Custody | Commodity regulations, custody verification | | **[Stablecoins](/docs/executive-overview/use-cases/stablecoins)** | Payment rails, treasury | DvP + Reserves | AML/sanctions, reserve attestation | | **[Deposit Certificates](/docs/executive-overview/use-cases/deposits)** | Time deposits, retail banking | Vault + Interest | FDIC limits, customer verification | ## Lifecycle walkthroughs: from theory to production [#lifecycle-walkthroughs-from-theory-to-production] ### Corporate bonds: the 90-day coupon cycle [#corporate-bonds-the-90-day-coupon-cycle] Traditional bond servicing involves spreadsheets tracking who held what on the record date, calculating accrued interest, coordinating wire transfers, and reconciling payments across custodians. It takes days and introduces errors. **With DALP lifecycle automation:** 1. **Issuance via DvP** — Investor commits USDC payment; bond tokens transfer atomically upon receipt. No settlement risk, no escrow delays. 2. **Holding period with vault** — Bonds locked until seasoning requirements met; transfers blocked by compliance rules but economic ownership clear. 3. **Coupon calculation** — On-chain yield engine computes entitlements based on snapshot of holders at record date. No manual calculations. 4. **Claim distribution** — Token holders call `claimYield()` when convenient. Payment releases from yield vault instantly. No coordination needed. 5. **Redemption at maturity** — Principal plus final coupon automatically available; investors redeem bonds and receive payment atomically via DvP. Result: 90% reduction in operational overhead. Zero reconciliation errors. Instant settlement replaces three-day clearing cycles. ### Private equity: NAV updates and distribution waterfalls [#private-equity-nav-updates-and-distribution-waterfalls] Fund administrators spend weeks calculating NAV, determining distribution waterfalls, and processing capital calls. Limited partners wait for quarterly statements that are outdated the moment they arrive. **With DALP lifecycle automation:** 1. **Primary issuance via DvP** — LP commits capital; receives fund tokens upon payment confirmation. Subscription process completes in minutes. 2. **Vault custody with gates** — Tokens subject to lock-up periods and transfer restrictions. GP controls redemption gates through vault parameters. 3. **NAV tracking** — On-chain oracle or manual update publishes current NAV; token metadata reflects latest valuation instantly visible to all LPs. 4. **Distribution waterfall** — Yield engine applies preferred return and carried interest rules automatically. GP take calculated on-chain, LP distributions released to yield vault. 5. **Quarterly claims** — LPs claim their distributions via simple transaction; receive payment in stablecoins or redeem for fiat through integrated rails. Result: Real-time cap table visibility. Automated compliance with fund terms. LPs access their capital faster, GPs reduce administrative burden by 70%. ### Real estate: rental income without rent rolls [#real-estate-rental-income-without-rent-rolls] Property managers maintain complex rent rolls, track pro-rata ownership, calculate distributions, and process dozens of individual payments. Investors wait 30-45 days after quarter-end to see their income. **With DALP lifecycle automation:** 1. **Fractionalization via DvP** — Property tokenized into shares; investors purchase via atomic swap against stablecoins. Ownership recorded on-chain. 2. **Custody in yield vault** — Property management entity deposits rental income into vault monthly; funds accumulate against token holder entitlements. 3. **Pro-rata calculation** — Yield engine computes each investor's share based on token balance; accounts for mid-period transfers automatically. 4. **Self-service claims** — Investors call `claimYield()` to receive their rental income anytime. No waiting for property manager's payment run. 5. **Exit via DvP** — When property sells, proceeds deposited to vault; investors redeem tokens and receive their share atomically. Result: Monthly income available within days of rent collection. Zero manual rent roll maintenance. Property manager focuses on operations, not payment processing. ### Precious metals: custody-verified fractional ownership [#precious-metals-custody-verified-fractional-ownership] Traditional precious metals investment requires purchasing full bars, managing physical custody, and accepting limited liquidity. Verification depends on periodic audits, and transfers require physical delivery. **With DALP lifecycle automation:** 1. **Tokenization via DvP** — Custodian deposits verified metal holdings; tokens issued atomically representing fractional ownership of allocated bars. 2. **Custody attestation** — Vault operator provides on-chain attestations of metal holdings including bar numbers, weights, purity grades, and locations. 3. **Transparent verification** — Investors view custody status, purity certifications, and audit reports in real time via dashboard. 4. **Secondary trading** — Token holders trade fractional metal ownership without physical movement; compliance checks enforce investor eligibility. 5. **Redemption options** — Holders redeem for physical delivery (minimum quantities apply) or cash settlement at spot prices via DvP. Result: Fractional precious metals access from $200 instead of $60K+ for full bars. Real-time custody verification replaces annual audits. Secondary liquidity without physical logistics. ### Stablecoins: reserve management and interest distribution [#stablecoins-reserve-management-and-interest-distribution] Stablecoin issuers face constant pressure to prove reserves and distribute yield from treasury operations. Traditional systems require separate banking rails, manual reconciliation, and delayed transparency. **With DALP lifecycle automation:** 1. **Minting via DvP** — User deposits collateral (USDC, treasuries, etc.); receives stablecoins atomically. Reserve backing cryptographically verified. 2. **Vault custody of reserves** — Collateral held in multi-sig vault; reserve ratio enforced on-chain. Real-time attestation eliminates audit delays. 3. **Yield generation** — Treasury operations (lending, staking) generate returns; interest credited to yield vault proportional to holdings. 4. **Continuous distribution** — Interest accrues to token holders automatically; claimable anytime. No monthly distribution schedules needed. 5. **Redemption via DvP** — User burns stablecoins; receives collateral atomically from vault. No redemption queues or processing delays. Result: Transparent reserves build trust. Automated yield distribution reduces operational costs by 85%. Instant redemption eliminates bank run risk. ### Deposits: time-locked savings with guaranteed interest [#deposits-time-locked-savings-with-guaranteed-interest] Banks offering tokenized certificates of deposit face regulatory requirements for term enforcement, interest accrual accuracy, and early withdrawal penalties. Manual tracking creates compliance risk. **With DALP lifecycle automation:** 1. **Deposit via vault** — Customer deposits funds; receives time-locked CD token. Smart contract enforces maturity date; early withdrawal requires penalty calculation. 2. **Interest accrual** — Yield engine calculates daily interest based on deposit terms; entitlement grows continuously and transparently on-chain. 3. **Mid-term visibility** — Customer views accrued interest anytime via dashboard; knows exact maturity value. No statement delays. 4. **Automatic maturity** — At term end, vault unlocks; customer claims principal plus interest via single transaction. No branch visit needed. 5. **Early exit option** — If permitted, vault calculates penalty automatically; customer accepts and receives reduced amount via DvP. Result: Zero manual interest calculation errors. Regulatory compliance enforced by code. Customer self-service eliminates 90% of support tickets. ## Cross-cutting capabilities: the unified infrastructure advantage [#cross-cutting-capabilities-the-unified-infrastructure-advantage] Every asset type benefits from shared platform features that traditional point solutions force you to build separately: **Compliance enforcement at transfer time** — SMART Protocol validates investor eligibility, geographic restrictions, and holding period requirements before any token moves. Works identically whether transferring bonds, fund units, or real estate shares. **Real-time cap tables** — Ownership updates instantly as tokens transfer. Regulatory reporting pulls current data anytime. No month-end reconciliation across custodians, no stale spreadsheets. **Unified observability** — Single dashboard shows transaction latency, compliance rule evaluation statistics, yield distribution status, and vault custody metrics across all asset types. DevOps teams monitor one system instead of five. **Audit trails** — Every transaction, compliance check, corporate action, and lifecycle event recorded on-chain with cryptographic proofs. Regulators access complete history without requesting data exports. **Instant settlement** — Atomic finality eliminates counterparty risk and capital lockup for all asset types. T+2 and T+5 clearing cycles become historical curiosities. ## The cost of fragmentation vs. unified infrastructure [#the-cost-of-fragmentation-vs-unified-infrastructure] **Traditional multi-vendor approach:** * Integration engineering: 6-9 months per asset type * Ongoing reconciliation: 40+ hours monthly per asset * Compliance gaps: Manual verification introduces errors and delays * Operational overhead: Separate systems for custody, distributions, servicing * Scaling cost: Each new asset type requires new vendor relationships and integrations **DALP unified approach:** * Launch to production: 4-8 weeks for first asset, 2-4 weeks for additional types * Reconciliation: Zero—single source of truth on-chain * Compliance enforcement: Automated at protocol level, consistent across assets * Operational overhead: One platform, one monitoring stack, one support team * Scaling cost: Deploy new asset configurations, reuse existing infrastructure The difference compounds over time. With a unified platform, teams that launch three asset types can move straight to their fourth and fifth using the same small team — no additional engineering headcount, no separate operations staff per asset. ## Observability: seeing what matters in production [#observability-seeing-what-matters-in-production] Traditional tokenization platforms leave you blind. When a distribution fails or a transfer gets rejected, you're debugging logs and calling vendor support. DALP's observability stack gives you answers immediately: **Transaction latency dashboard** — Verify settlement times meet SLAs. See P50/P95/P99 latencies across asset types. Spot performance degradation before users complain. **Compliance metrics panel** — Real-time statistics on rule evaluation. Track rejection rates by rule type. Identify compliance bottlenecks proactively. **Yield distribution monitoring** — Track scheduled corporate actions, distribution calculations, and claim activity. Know exactly how much yield remains unclaimed and why. **Vault custody analytics** — Monitor multi-sig operations, threshold changes, and asset movements. Audit trail visualization for regulatory inquiries. **Cross-asset health view** — Single pane showing status across bonds, funds, real estate, precious metals, stablecoins, and deposits. Ops teams triage issues without context-switching between systems. This isn't just nice to have. It's the difference between confidently scaling to production and nervously hoping nothing breaks. ## Regulatory considerations [#regulatory-considerations] Each asset class navigates distinct frameworks, but the platform provides consistent compliance infrastructure: **Bonds** typically fall under securities laws requiring accredited investor verification and transfer restrictions. The SMART Protocol embeds these rules at the contract level—every bond transfer checks eligibility automatically. **Private equity** adds qualified purchaser thresholds and lock-up periods. Vault custody enforces holding requirements while yield automation handles distribution waterfalls per fund terms. **Real estate** introduces property transfer rules and tax withholding (like FIRPTA). Compliance modules configure jurisdiction-specific requirements without custom contract development. **Precious metals** navigate commodity trading regulations (CFTC) alongside securities laws when structured as investment products. Custody verification requirements ensure physical metal backing is provable on-chain. **Stablecoins** face banking regulations and reserve requirements. On-chain reserve proofs and real-time attestation eliminate periodic audit delays. **Deposits** require FDIC compliance and consumer protection. Time-lock enforcement and interest calculation accuracy are guaranteed by smart contract logic. See [Compliance & Security](/docs/executive-overview/compliance-security) for detailed architecture. ## Implementation path [#implementation-path] Deploying a use case follows a consistent pattern regardless of asset type: 1. **Asset configuration** — Define terms, compliance rules, and lifecycle schedules via Asset Designer. Reuse templates for common patterns. 2. **Investor onboarding** — Integrate KYC/AML providers to verify eligibility and issue OnchainID credentials. Process works identically across asset types. 3. **Primary distribution** — Allocate tokens to verified investors via DvP settlement. Configure vault parameters and yield schedules. 4. **Lifecycle automation** — Corporate actions execute automatically: distributions, redemptions, NAV updates. Token holders self-service their claims. 5. **Ongoing operations** — Monitor unified dashboard, handle secondary transfers, generate regulatory reports from on-chain data. For technical implementation details, see the [Developer Guides](/docs/developer-guides/). ## Next steps [#next-steps] * Explore detailed scenarios for your asset class using the links in the table above * Review [Platform Overview](/docs/architecture/overview) to understand the underlying infrastructure * Contact your SettleMint representative to discuss a proof of concept demonstrating DALP lifecycle features with your specific asset type # Precious metals Source: https://docs.settlemint.com/docs/executive-overview/use-cases/precious-metals Precious metals tokenization through DALP helps model gold, silver, platinum, and palladium assets with metal metadata, weight-based pricing, custody context, and compliance controls. **Who should read this:** Precious metals dealers, vault operators, custodians, and asset managers exploring tokenized commodity offerings. **Business value:** Represent precious metal assets with structured metal metadata, weight-based pricing, custody context, holder visibility, and compliance-aware transfer controls. ## Business challenge [#business-challenge] Precious metals programs need a clear operational link between the token that investors hold and the metal program it represents. Operators must track the metal type, unit of account, pricing basis, storage context, and the compliance rules that determine who can hold or transfer the token. ### Traditional approach [#traditional-approach] Precious metals tokenization with asset listings. Asset detail views surface precious metal metadata when available. ## How DALP models precious metals [#how-dalp-models-precious-metals] Asset Designer supports precious metal token creation. Operators can create a precious metal asset directly or use a precious metal asset template, then fill in the metal, weight, valuation, custody, and compliance fields required for the issuance program. ### Metal classification [#metal-classification] A precious metal asset records the metal type as gold, silver, platinum, or palladium. Operators can also record a purity grade when the product requires that level of classification. ### Weight-based token terms [#weight-based-token-terms] The asset can define the unit used for the metal program, such as grams, troy ounces, or kilograms. It also records how much metal each token represents and the price currency and spot price per unit used for valuation. ### Custody context [#custody-context] Operators can add storage context to the asset, including a vault location and a custodian or vault operator name. DALP surfaces this context on the asset detail view when it is present, so users can inspect the public-facing custody fields attached to the token. ### Compliance-aware transfers [#compliance-aware-transfers] Precious metal assets can be created with compliance modules. Those controls can limit who may receive or transfer the token according to the rules configured for the issuance program. ### Holder and operational views [#holder-and-operational-views] DALP shows token details and holder information through the asset workspace. Users can inspect the metal classification, purity, weight-per-token terms, storage location, custodian, supply, holders, and transfer activity where those fields and views are available for the asset. ## Key capabilities [#key-capabilities] | Capability | What DALP records or enforces | | -------------------- | -------------------------------------------------------------- | | Metal classification | Gold, silver, platinum, or palladium | | Purity metadata | Optional purity grade for the metal program | | Weight terms | Unit of account and weight per token | | Valuation input | Price currency and spot price per unit | | Custody context | Optional storage location and custodian or vault operator name | | Compliance controls | Configured transfer rules applied to token operations | | Holder visibility | Token holder and transfer views in the asset workspace | ## Example structure [#example-structure] A gold-backed product can be modeled with: 1. gold as the metal type 2. an optional purity grade, such as 999.9 3. a weight unit, such as troy ounces or grams 4. a weight-per-token value 5. a price currency and spot price per unit 6. optional vault location and custodian fields 7. compliance modules selected for the target issuance rules The same model also supports silver, platinum, and palladium programs when the operator configures the appropriate metadata and compliance rules. ## Compliance considerations [#compliance-considerations] Precious metals programs usually need legal, custody, investor-eligibility, and market-operations review before launch. DALP provides configurable asset and compliance controls, but the issuer remains responsible for selecting the rules, operating the off-chain custody process, and validating the legal treatment of the product in each jurisdiction. For detailed compliance architecture, see [Compliance & Security](/docs/executive-overview/compliance-security). ## Implementation checklist [#implementation-checklist] 1. Define the metal program and target jurisdictions 2. Choose the metal type and optional purity grade 3. Set the weight unit, weight per token, price currency, and spot price input 4. Decide which storage location and custodian fields should be visible 5. Select compliance modules for holder and transfer eligibility 6. Configure issuer, custodian, emergency, governance, and supply-management responsibilities as needed for the operating model 7. Create the token and review the detail, holder, and transfer views before making it available to users ## Limitations and considerations [#limitations-and-considerations] * **Custody operations:** DALP can show custody context fields, but off-chain vault operations, inventory reconciliation, insurance, and audit procedures remain issuer and custodian responsibilities. * **Pricing inputs:** Valuation depends on the price currency and spot price inputs configured for the asset. Operators should define how those values are maintained and reviewed. * **Physical delivery:** Any physical metal delivery or redemption workflow must be operated outside the token metadata unless a deployment adds a verified redemption process. * **Regulatory scope:** Commodity, securities, and AML/KYC requirements vary by jurisdiction. Issuers should confirm the applicable rules before launch. ## Next steps [#next-steps] * Review [Compliance & Security](/docs/executive-overview/compliance-security) to understand embedded compliance controls * Review [Asset contracts](/docs/architecture/components/asset-contracts) to understand how instrument profiles and configurable assets fit together * Explore [Developer Documentation](/docs/developer-guides) for integration and operations guidance # Private equity Source: https://docs.settlemint.com/docs/executive-overview/use-cases/private-equity Private equity fund tokenization through DALP supports LP servicing, NAV tracking, distribution processing, and compliant secondary-transfer workflows for fund units. **Who should read this:** General partners, fund administrators, and investor relations teams managing institutional LP relationships. **Business value:** Model fund units, track holdings and NAV, calculate distribution entitlements with on-demand claiming, and route secondary transfers through embedded compliance checks. ## Business challenge [#business-challenge] Apex Capital runs a $200 million venture fund with 80 institutional LPs. Traditional fund administration is expensive, slow, and opaque. Quarterly valuations take weeks to calculate and distribute. Capital calls and distributions require manual processing. Secondary transfers between LPs are nearly impossible due to paperwork complexity. ### Traditional approach [#traditional-approach] Fund tokens carry fund-specific metadata including investment category, fund class, and management fee parameters. ## How DALP solves it [#how-dalp-solves-it] ### Fund unit creation [#fund-unit-creation] Apex tokenizes their fund structure as equity tokens representing limited partner units. Each token can carry fund-specific metadata such as investment category, fund class, and management fee parameters. Fund documents can be uploaded and linked from the token metadata so investors and operators have a consistent reference. ### LP onboarding [#lp-onboarding] Institutional investors complete accredited investor verification and qualified purchaser checks through the platform's identity system. Reusing verification across funds depends on the identity provider, claim configuration, and fund rules. ### Subscription processing and capital tracking [#subscription-processing-and-capital-tracking] When LPs commit capital, Apex issues fund tokens representing their percentage ownership. The platform tracks committed versus called capital through on-chain records. Note: The DALP fund implementation supports proportional (pro-rata) distributions based on token holdings. Complex waterfall logic (preferred returns, catch-up provisions, carried interest tiers) requires custom addon development or off-chain calculation with on-chain distribution execution. ### NAV updates and performance tracking [#nav-updates-and-performance-tracking] Monthly or quarterly, Apex updates the net asset value through the admin portal. Holders can see the latest recorded fund-unit valuation in the platform. Performance metrics such as IRR, MOIC, or quartile ranking belong in the fund administrator's reporting model unless they are configured in a connected reporting workflow. ### Scheduled distributions [#scheduled-distributions] When portfolio companies exit or pay dividends, Apex calculates proportional entitlements for token holders. Token holders can claim tokenized distributions on-demand through the smart contract. Fiat payments and bank-wire processes remain dependent on the configured payment adapters and the fund's operating process. ### Management fee collection [#management-fee-collection] The AUM fee feature can calculate time-based management fees from total supply and mint the fee amount to the configured recipient. Fund administrators still need to align the configured fee rate, collection cadence, and accounting process with the fund terms. Equity details with on-chain governance parameters. ### Secondary trading among LPs [#secondary-trading-among-lps] When an LP wants to exit early, the platform supports a transfer to another qualified institutional investor. Compliance checks ensure the buyer meets the configured requirements before the transfer can proceed. Transfer restrictions in the fund documents can be represented through compliance modules, approval workflows, or operational review, depending on the fund terms. ## Key capabilities [#key-capabilities] | Capability | Traditional | With DALP | | --------------------------- | ----------------------------------- | ------------------------------------------- | | **Fund administration** | Quarterly fees to third-party admin | On-chain records reduce reconciliation work | | **NAV distribution** | Delayed LP statements | Current recorded valuation visible in DALP | | **Distribution processing** | Wire instructions and manual checks | Calculated entitlements, on-demand claiming | | **Fee collection** | Manual invoicing and payment | AUM fee collection to configured recipient | | **Secondary transfers** | Legal and operational coordination | Compliance-gated transfer workflow | | **LP reporting** | Periodic statements | Dashboard access to current holdings | ## Expected outcomes [#expected-outcomes] **Operational efficiency** – On-chain records, configured fee parameters, and claim-based distribution workflows reduce reconciliation and manual servicing work. The exact cost impact depends on the fund's operating model and connected payment systems. **LP experience** – Current holdings visibility and on-demand distribution claiming can reduce status inquiries when the fund uses the platform as its servicing interface. **Secondary liquidity** – DALP can route eligible LP transfers through configured identity and compliance checks. Legal approvals, ROFR handling, and settlement arrangements still need to match the fund documents. The diagram above shows the DALP fund lifecycle, from tokenization through configured servicing workflows to compliance-gated secondary-transfer support. ## Compliance considerations [#compliance-considerations] Private equity fund tokens operate under securities regulations requiring: * **Qualified purchaser verification** – Enforced via OnchainID claims * **Holding periods and lock-ups** – Configured via time-lock compliance modules * **Transfer restrictions** – ROFR rights, limited transfer windows, GP approval workflows * **Jurisdiction controls** – Investment restrictions by geography * **Audit trails** – Full transaction history for regulatory reporting For detailed compliance architecture, see [Compliance & Security](/docs/executive-overview/compliance-security). ## Implementation checklist [#implementation-checklist] 1. Define fund structure (equity tokens, percentage ownership model) 2. Select compliance template (qualified purchaser, accredited investor, or custom) 3. Integrate KYC/AML provider for institutional LP verification 4. Configure NAV update workflow and admin portal access 5. Configure tokenized distribution workflow; align fiat payment handling with the selected payment adapters and operating process 6. Configure AUM fee parameters and collection workflow 7. Upload fund documents (LPA, operating agreement) and link to token metadata 8. Deploy smart contract and conduct initial capital call 9. Test distribution workflow and secondary transfer restrictions ## Limitations and considerations [#limitations-and-considerations] * **Waterfall complexity** – Out-of-the-box implementation supports pro-rata distributions; custom waterfall logic (preferred returns, catch-up, carried interest tiers) requires addon development * **NAV data source** – NAV updates are manual entries via admin portal; integration with portfolio valuation systems requires custom API development * **Legal and regulatory review** – GP and LP legal counsel should review tokenized structure for compatibility with existing fund documents and regulatory frameworks ## Next steps [#next-steps] * Review [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) to understand embedded compliance * Explore [Developer Documentation](/docs/developer-guides) for custom addon development guidance * Contact your SettleMint representative to discuss a fund tokenization pilot # Real estate Source: https://docs.settlemint.com/docs/executive-overview/use-cases/real-estate Real estate tokenization through DALP democratizes access to institutional-quality properties while calculating rental income entitlements and maintaining compliance controls. The platform handles fractionalized ownership, expense tracking, and property governance. **Who should read this:** Real estate sponsors, property managers, and asset managers exploring fractional ownership models. **Business value:** Lower minimum investment thresholds, calculate monthly rental entitlements for on-demand claiming, enable secondary liquidity, and provide real-time performance visibility to investors. ## Business challenge [#business-challenge] Highland Properties owns a $25 million office building in a major market. They want to fractionalize ownership to enable smaller investors to participate while maintaining professional property management and compliance controls. ### Traditional approach [#traditional-approach] Real estate asset inventory gives sponsors a portfolio-level view of tokenized properties. ## How DALP solves it [#how-dalp-solves-it] ### Property tokenization as equity [#property-tokenization-as-equity] Highland creates 250,000 equity tokens, each representing $100 of building ownership. Token holders receive proportional rental income distributions and can participate in governance decisions on major property matters. Real estate listings show tokenized properties before drilling into asset-level operations. ### Investor eligibility and documentation [#investor-eligibility-and-documentation] The platform enforces that only verified investors with appropriate accreditation levels can purchase tokens. Property documents—purchase agreements, operating agreements, building inspections, insurance policies—are hashed and linked to the token metadata for permanent transparency. ### Fractional ownership [#fractional-ownership] Investors can purchase as few as 10 tokens ($1,000 investment) or as many as they want up to regulatory limits. The platform enforces maximum ownership percentages via compliance modules to maintain distributed ownership. ### Rental income distributions [#rental-income-distributions] Monthly rental income from tenants flows into Highland's treasury. After deducting property management fees and reserves, the platform distributes net income proportionally to all token holders. Distributions execute automatically on a preset schedule via configured payment adapters. ### Property expenses and reserves [#property-expenses-and-reserves] Major repairs or capital improvements require approval from token holders. Highland creates a governance proposal using the governance addon module. Token holders vote with their holdings weighted proportionally. Note: The current platform supports proposal creation and voting infrastructure. Property-specific UI workflows (proposal templates for repairs, capital improvements, special assessments) require custom dapp development or use of third-party governance tools integrated with the token contract. ### Valuation updates [#valuation-updates] Annual appraisals update the property's value through the admin portal, adjusting the implied token value. Holders see current valuations without requesting reports. Real estate token with property-specific metadata on-chain. ### Compliance with securities and real estate law [#compliance-with-securities-and-real-estate-law] The platform enforces both securities regulations (accredited investor requirements) and applicable real estate regulations (ownership transfer restrictions, FIRPTA withholding for foreign investors via compliance modules). ### Exit mechanisms [#exit-mechanisms] When Highland eventually sells the building, sale proceeds distribute automatically to all holders proportionally. Tokens are redeemed and the holding concludes with complete documentation of every income distribution, every expense, and every ownership change. ## Key capabilities [#key-capabilities] | Capability | Traditional | With DALP | | --------------------------- | ------------------------- | ----------------------------------- | | **Minimum investment** | $25M (full ownership) | $1,000 (10 tokens) | | **Distribution processing** | Days of manual wire work | Automated monthly schedule | | **Ownership visibility** | Quarterly reports | Real-time dashboard | | **Governance voting** | Email/phone campaigns | Token-weighted on-chain proposals | | **Secondary transfers** | Must sell entire property | Trade tokens with compliance checks | | **Expense tracking** | Spreadsheets | On-chain transaction records | ## Measurable outcomes [#measurable-outcomes] **Democratized access** – Real estate institutional-quality investment becomes available to smaller investors who cannot deploy $25 million for full ownership. **Operational streamlining** – Distribution entitlement calculations that used to take days of manual spreadsheet work now happen automatically each month, and token holders claim their rental income on-demand via the smart contract. **Transparency** – Investors see property performance, expenses, and income in real time rather than waiting for quarterly reports. **Secondary market** – Token holders can sell positions to other qualified investors, creating liquidity in a traditionally illiquid asset class. The diagram above shows the DALP real estate lifecycle, from tokenization through automated servicing to secondary liquidity. ## Compliance considerations [#compliance-considerations] Real estate operate under both securities and property regulations: * **Accredited investor verification** – Enforced via OnchainID claims * **Maximum ownership limits** – Configured via compliance modules to maintain distributed ownership * **Transfer restrictions** – Lock-up periods, holding requirements * **FIRPTA withholding** – For foreign investor transactions (requires custom tax module integration) * **State securities laws** – Multi-state registration compliance For detailed compliance architecture, see [Compliance & Security](/docs/executive-overview/compliance-security). ## Implementation checklist [#implementation-checklist] 1. Structure property ownership vehicle (LLC, Delaware Statutory Trust, etc.) 2. Define token economics (total supply, price per token, minimum/maximum holdings) 3. Capture property location metadata (City, District Code, Area ID) so dashboards, registries, and investors can search consistently—the asset designer now surfaces placeholders such as “New York”, “001”, and “NYC-001” to guide the expected format, and the reference Hardhat script issues the `TOPIC_ASSET_LOCATION` claim with those defaults for the Prime Commercial Property identity. 4. Select compliance template (Reg D, Reg A+, or custom ruleset) 5. Integrate KYC/AML provider for investor verification 6. Configure rental distribution schedule and payment adapters 7. Set up governance addon for proposal creation and voting 8. Upload property documents (appraisals, insurance, operating agreements) and link to metadata 9. Deploy smart contract and conduct primary token sale 10. Configure expense tracking and reserve management workflows ### Supply model in the reference deployment [#supply-model-in-the-reference-deployment] The reference Hardhat script (`kit/contracts/scripts/hardhat/assets/real-estate.ts`) mints the full Prime Commercial Property supply upfront, pairing the US ISIN with `Countries.US` so the jurisdictional metadata stays consistent. `DALPRealEstateImplementation` stores the premint recipient and amount during `initialize`, locks the SMART cap to that amount, and requires the factory to call `completePremint` exactly once to transfer tokens before pausing again. Additional `mint`/`batchMint` calls revert until governance raises the cap via `setCap`, ensuring the circulating supply remains fixed after deployment unless operations intentionally expand it. ## Limitations and considerations [#limitations-and-considerations] * **Governance UI** – Platform provides proposal registry and voting infrastructure; property-specific workflows (repair proposals, capital improvement templates) may require custom dapp development * **Rental data source** – Income and expense data entered manually via admin portal; integration with property management systems requires custom API development * **Regulatory complexity** – Multi-state securities registration and property-specific regulations require legal counsel review ## Next steps [#next-steps] * Review [SMART Protocol integration (ERC-3643)](/docs/architecture/components/asset-contracts/smart-protocol-integration) to understand embedded compliance * Explore [Developer Documentation](/docs/developer-guides) for governance addon customization * Contact your SettleMint representative to discuss a real estate tokenization pilot # Stablecoins Source: https://docs.settlemint.com/docs/executive-overview/use-cases/stablecoins Bank-issued stablecoins through DALP support controlled on-chain issuance, transfers, redemption burns, holder and supply visibility, collateral and reserve state, compliance controls, operational history, and API integration around the token lifecycle. **Who should read this:** Treasury teams, payment operations, and financial institutions evaluating bank-issued stablecoins as part of a controlled settlement and treasury operating model. **Business value:** Issue and operate stablecoins with on-chain supply and holder visibility, controlled mint and burn workflows, collateral and reserve state, compliance controls, and API integration into the surrounding banking and treasury estate. ## Business challenge [#business-challenge] Regional Bank wants to issue a USD-backed stablecoin for commercial clients to use in B2B settlement, trade finance, and treasury workflows. The bank needs the on-chain token lifecycle to reflect its operating controls: who can hold the asset, when supply can be minted or burned, what collateral or reserve state is recorded, and how operations are audited. The payment rail around that lifecycle is not a single universal system. Fiat movements, reserve custody, treasury and accounting systems, core-banking posting, client portals, and payment networks vary by deployment. DALP provides the on-chain stablecoin lifecycle and integration surfaces that those systems can use. ### Traditional operating model [#traditional-operating-model] Fully collateralised stablecoins maintaining the link between on-chain tokens and real-world reserves. ## DALP's role in the stablecoin operating model [#dalps-role-in-the-stablecoin-operating-model] DALP is the control and record layer for the on-chain stablecoin lifecycle. A stablecoin can be created from a template, configured with its currency and reserve model, minted to eligible holders, transferred under configured compliance rules, and burned during redemption or supply-reduction operations. The platform records the lifecycle events and exposes operational views for holder balances, total supply, collateral and reserve state, compliance status, documents, feeds, and activity history. This gives operations, treasury, and compliance teams a shared view of the token state that can be reconciled with the institution's off-chain systems. ### Issuance and reserve state [#issuance-and-reserve-state] Regional Bank creates a stablecoin token pegged 1:1 to USD. The bank or its appointed providers operate the off-chain reserve accounts and custody model. DALP records the on-chain asset, its reserve and collateral state, and the configured controls that must be satisfied before new supply is issued. When a client funds the reserve through the bank's chosen fiat process, the operations team can record the required state in DALP and mint the corresponding stablecoin supply to the client's eligible wallet. The on-chain supply then becomes visible in holder and asset views, while fiat balances remain governed by the bank's treasury and accounting controls. ### Transfers between eligible holders [#transfers-between-eligible-holders] Clients can transfer stablecoin balances between eligible wallets. Transfers move existing supply from one holder to another without changing total supply. Eligibility depends on the configured compliance controls for the asset, such as identity, jurisdiction, allow-list, or other provider-backed checks. DALP does not, by itself, prove execution on external payment networks or banking rails. It does not natively validate Circle CPN or CCTP messages, ISO 20022 payment messages, ACH or wire execution, core-banking posting, payment message translation, or universal network fee treatment. Those capabilities can be handled by deployment-specific integrations around DALP's on-chain lifecycle. ### Redemption and burns [#redemption-and-burns] When a holder redeems stablecoins for fiat, the off-chain reserve movement is handled by the institution's treasury, banking, custody, or payment providers. DALP records the on-chain burn of the corresponding stablecoin amount. The burn reduces both the holder balance and total supply, leaving the token record aligned with the current issued amount. ### Visibility and operational history [#visibility-and-operational-history] DALP exposes the operational history around stablecoin activity: creation, minting, transfers, burns, holder balances, total supply, compliance state, documents, feeds, and collateral or reserve state. This supports internal oversight, reconciliation, and audit preparation. External regulator or client-facing portals are deployment choices. A programme can expose DALP data through APIs or custom portals, but those channels depend on the bank's access model, tenancy requirements, and disclosure policy. Pricing configuration captures base price, total valuation, and fee structures. ## Compliance controls [#compliance-controls] Stablecoin programmes usually combine on-chain restrictions with provider and operations controls. DALP can enforce configured controls such as holder eligibility, jurisdiction rules, and transfer restrictions. AML screening, sanctions checks, transaction monitoring, case management, and alert handling depend on the compliance providers and workflows connected to the deployment. This distinction matters. DALP can make a transfer subject to configured compliance state, but it is not a universal AML or sanctions monitoring system unless those provider integrations and rules are part of the programme design. ## Integration boundary [#integration-boundary] DALP integrates with external systems around the on-chain lifecycle rather than replacing the whole payment and banking estate. | Area | DALP role | Deployment-specific systems | | ------------------------ | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | | **Stablecoin lifecycle** | Create assets, mint supply, transfer balances, burn supply, and expose holder and supply state | Asset programme policy and operating approvals | | **Reserve operations** | Record collateral and reserve state associated with issuance and redemption | Reserve custody, bank accounts, treasury workflows, and accounting systems | | **Compliance** | Enforce configured holder, jurisdiction, and transfer controls | KYC/KYB providers, AML and sanctions screening, transaction monitoring, and case management | | **Payment rails** | Provide the on-chain token movement and lifecycle data | ACH, wire, card, RTP, ISO 20022 messaging, Circle CPN/CCTP, correspondent banking, or other rail integrations | | **Client experience** | Expose APIs and operational data for stablecoin activity | Client portals, banking channels, statementing, and core-banking posting | ## Stablecoin lifecycle in DALP [#stablecoin-lifecycle-in-dalp] The diagram shows DALP's stablecoin scope: the on-chain asset lifecycle, operational state, and integration points that surround it. It does not imply that DALP executes every fiat movement, payment message, custody action, or banking ledger entry. ## Outcomes [#outcomes] **On-chain settlement visibility**: Stablecoin transfers settle on-chain and are reflected in holder balances, total supply, and activity history. **Controlled issuance and redemption**: Minting and burning can be tied to the programme's reserve, collateral, approval, and compliance controls. **Operational reconciliation**: Treasury and operations teams can compare off-chain reserve and accounting records with DALP's token lifecycle and supply data. **Integration flexibility**: Banks can connect DALP to the fiat rails, custody providers, compliance vendors, accounting tools, client portals, and core-banking systems required by their operating model. ## Considerations [#considerations] Bank-issued stablecoins require jurisdiction-specific legal, compliance, treasury, accounting, and operational review. DALP provides the stablecoin lifecycle and control surface, while the broader payment, custody, reserve, reporting, and client-channel architecture depends on the institution's programme design. For detailed compliance architecture, see [Compliance & Security](/docs/executive-overview/compliance-security). # Digital Asset Lifecycle Platform documentation Source: https://docs.settlemint.com/docs/ Complete documentation for the SettleMint Digital Asset Lifecycle Platform - a full-stack blockchain platform for tokenizing real-world assets. ## Welcome [#welcome] Welcome to the comprehensive documentation for the **Digital Asset Lifecycle Platform (DALP)**. DALP dashboard showing portfolio scale and operational visibility. Asset insights showing cross-asset coverage and platform breadth. This documentation is the working reference for understanding, deploying, operating, and extending the Digital Asset Lifecycle Platform. It is structured for four primary audiences: * **Business executives and decision makers** - Executive overview * **Technical leaders and architects** - Solution architecture and capabilities * **Platform operators and administrators** - User guides * **Developers and integrators** - Developer guides and reference ## Documentation structure [#documentation-structure] ### Executive overview [#executive-overview] High-level conceptual explanations for business stakeholders covering what the product is, why it's needed, and the problems it solves. ### Solution architecture and capabilities [#solution-architecture-and-capabilities] In-depth technical documentation for CTOs, compliance officers, and DevOps teams covering system architecture, security, compliance, and integration. ### User guides [#user-guides] Practical how-to guides for end-users and administrators covering asset tokenization workflows and administrative tasks. ### Developer guides and reference [#developer-guides-and-reference] Detailed tutorials and reference materials for developers integrating or extending the Digital Asset Lifecycle Platform. ### Appendices and resources [#appendices-and-resources] Supplementary information including glossary, FAQ, release notes, and roadmap. ## Getting started [#getting-started] * **New to asset tokenization?** Start with [Introduction](/docs/executive-overview/introduction) * **Evaluating the platform?** Review the [DALP overview](/docs/executive-overview/dalp-overview) and [Use cases](/docs/executive-overview/use-cases) * **Ready to deploy?** See the [DALP Execution Engine](/docs/architecture/components/infrastructure/execution-engine) * **Understanding the architecture?** Check the [Platform overview](/docs/architecture/overview) * **Building integrations?** Check the [Unified API](/docs/architecture/components/platform/unified-api) # Privacy Policy Source: https://docs.settlemint.com/docs/legal/privacy-policy How the SettleMint Digital Asset Lifecycle Platform collects, uses, stores, and protects personal data. **Effective date:** March 5, 2026 **Last updated:** March 5, 2026 SettleMint NV ("SettleMint", "we", "us", or "our"), a company incorporated under the laws of Belgium with company number 0661.674.810, having its registered office at Kempische Steenweg 311 bus 4.01, 3500 Hasselt, Belgium, is committed to protecting your privacy. This Privacy Policy explains how we collect, use, share, and protect personal data in connection with the SettleMint Digital Asset Lifecycle Platform ("DALP" or the "Platform") and our related websites, communications, and services. This Privacy Policy applies globally. Where specific regulations grant you additional rights, those are detailed in the [jurisdiction-specific sections](#11-jurisdiction-specific-provisions) below. ## 1. Data Controller [#1-data-controller] SettleMint NV is the data controller responsible for the processing of your personal data as described in this Privacy Policy. For questions or requests regarding your personal data, contact our Data Protection Officer: **Data Protection Officer** SettleMint NV Philipssite 5 bus 1 3001 Leuven, Belgium Email: [privacy@settlemint.com](mailto:privacy@settlemint.com) ## 2. Personal Data We Collect [#2-personal-data-we-collect] We collect the following categories of personal data depending on how you interact with us and the Platform: ### 2.1 Account and Identity Data [#21-account-and-identity-data] When you create an Account or are added as an Authorized User, we collect: * Full name * Email address * Organization name and role * Phone number (optional) * Account credentials (passwords are stored in hashed form only) * Multi-factor authentication identifiers ### 2.2 Compliance and Verification Data [#22-compliance-and-verification-data] When you or your end users undergo identity verification workflows on the Platform, the following data may be processed: * Government-issued identification documents (passport, national ID, driver's license) * Proof of address documentation * Corporate registration and beneficial ownership information * KYC/KYB verification status and results * Sanctions screening results **Important:** Compliance and verification data is processed by you (the Platform customer) as the data controller for your end users. SettleMint acts as a data processor for this data. Our processing is governed by the Data Processing Agreement between you and SettleMint. ### 2.3 Platform Usage Data [#23-platform-usage-data] When you use the Platform, we automatically collect: * Pages visited and features used * Actions performed (e.g., asset creation, transaction submissions) * Timestamps and session duration * Error logs and performance data * API usage and request metadata ### 2.4 Technical Data [#24-technical-data] We automatically collect certain technical information, including: * IP address * Browser type and version * Operating system * Device identifiers * Referring URL * Language preferences * Time zone setting ### 2.5 Transaction and Blockchain Data [#25-transaction-and-blockchain-data] When you create or manage Digital Assets through the Platform, we process: * Transaction metadata (timestamps, asset types, amounts) * Wallet addresses associated with your Account * Smart contract deployment records * On-chain transaction hashes **Note:** Data written to a public blockchain is immutable and publicly accessible. SettleMint cannot delete or modify on-chain data. You are responsible for ensuring that no personal data is recorded on-chain in violation of applicable law. For more information on blockchain-related risks and disclaimers, see Section 9.4 of our [Terms of Service](/docs/legal/terms-of-service). ### 2.6 Audit and Compliance Data [#26-audit-and-compliance-data] We maintain audit logs of compliance workflows, verification decisions, access events, and regulatory reports generated through the Platform for security, legal compliance, and accountability purposes. ### 2.7 Communication Data [#27-communication-data] When you contact us, we collect: * Email correspondence content * Support ticket details * Chat transcripts * Phone call records (if applicable) ### 2.7 Cookies and Tracking Technologies [#27-cookies-and-tracking-technologies] We use cookies and similar technologies to operate and improve the Platform. For details, see [Section 9](#9-cookies-and-tracking-technologies). ## 3. How We Use Your Personal Data [#3-how-we-use-your-personal-data] We process your personal data for the following purposes and legal bases: | Purpose | Legal Basis (GDPR) | Categories of Data | | ------------------------------------------------ | -------------------------------------------------------------------------- | -------------------------------------- | | Providing and operating the Platform | Performance of contract (Art. 6(1)(b)) | Account, Usage, Technical, Transaction | | Account creation and management | Performance of contract (Art. 6(1)(b)) | Account and Identity | | Processing compliance and verification workflows | Performance of contract (Art. 6(1)(b)); Legal obligation (Art. 6(1)(c)) | Compliance and Verification | | Customer support and communication | Performance of contract (Art. 6(1)(b)); Legitimate interest (Art. 6(1)(f)) | Account, Communication | | Platform security and fraud prevention | Legitimate interest (Art. 6(1)(f)) | Account, Usage, Technical | | Analytics and Platform improvement | Legitimate interest (Art. 6(1)(f)) | Usage, Technical | | Compliance with legal obligations | Legal obligation (Art. 6(1)(c)) | All categories as required | | Billing and invoicing | Performance of contract (Art. 6(1)(b)) | Account | | Marketing communications (with consent) | Consent (Art. 6(1)(a)) | Account (name, email) | Where we rely on legitimate interest as a legal basis, we have conducted a balancing test to ensure our interests do not override your fundamental rights and freedoms. You may request details of these assessments by contacting our Data Protection Officer. ## 4. Data Sharing [#4-data-sharing] We share your personal data only in the following circumstances: ### 4.1 Service Providers [#41-service-providers] We engage third-party service providers who process personal data on our behalf. These processors are contractually bound to process data only as instructed by us and to implement appropriate security measures. Categories of service providers include: * Cloud infrastructure providers (hosting and storage) * Identity verification and KYC/KYB providers * Analytics and monitoring providers * Customer support tools * Email and communication services * Payment processors ### 4.2 Professional Advisors [#42-professional-advisors] We may share personal data with our legal, financial, and insurance advisors where necessary for the management of our business. ### 4.3 Legal Requirements [#43-legal-requirements] We may disclose personal data where required by law, regulation, legal process, or governmental request, or where we believe disclosure is necessary to protect our rights, your safety, or the safety of others. ### 4.4 Business Transfers [#44-business-transfers] In connection with a merger, acquisition, reorganization, or sale of assets, your personal data may be transferred to the acquiring entity, subject to the same privacy protections described in this Policy. ### 4.5 With Your Consent [#45-with-your-consent] We may share your personal data with third parties where you have given your explicit consent. **We do not sell your personal data to any third party.** ## 5. International Data Transfers [#5-international-data-transfers] SettleMint operates globally, and your personal data may be transferred to and processed in countries outside your country of residence, including countries outside the European Economic Area (EEA). Where we transfer personal data outside the EEA, we ensure that appropriate safeguards are in place, including: * **Adequacy decisions:** Transfers to countries recognized by the European Commission as providing an adequate level of data protection * **Standard Contractual Clauses (SCCs):** We use the European Commission's standard contractual clauses (June 2021 version) for transfers to countries without an adequacy decision * **Supplementary measures:** Where necessary, we implement additional technical and organizational safeguards based on transfer impact assessments You may request a copy of the applicable transfer safeguards by contacting our Data Protection Officer. ## 6. Data Retention [#6-data-retention] We retain personal data only as long as necessary to fulfill the purposes for which it was collected, or as required by law. Our retention periods are as follows: | Data Category | Retention Period | Basis | | -------------------------------- | --------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | | Account and Identity Data | Duration of your subscription + 12 months | Contract performance; legitimate interest for account recovery | | Compliance and Verification Data | As required by applicable anti-money laundering law (typically 5–10 years after the end of the business relationship) | Legal obligation | | Platform Usage Data | 24 months from collection | Legitimate interest (analytics and improvement) | | Technical Data | 12 months from collection | Legitimate interest (security and troubleshooting) | | Transaction and Blockchain Data | Duration of your subscription + 7 years | Legal obligation (financial records retention) | | Communication Data | 36 months from last interaction | Legitimate interest (customer support continuity) | | Marketing consent records | Duration of consent + 3 years | Legal obligation (proof of consent) | On-chain data is immutable and cannot be deleted by SettleMint. Off-chain data is deleted or anonymized at the end of the applicable retention period. ## 7. Data Security [#7-data-security] We implement appropriate technical and organizational measures to protect your personal data against unauthorized access, alteration, disclosure, or destruction. These measures include: * Encryption of data in transit (TLS 1.2+) and at rest (AES-256) * Multi-factor authentication for Account access * Role-based access controls with least-privilege principles * Regular security assessments and penetration testing * Intrusion detection and monitoring systems * Employee security training and confidentiality obligations * Incident response procedures with documented breach notification protocols No method of electronic storage or transmission is 100% secure. While we strive to protect your personal data, we cannot guarantee its absolute security. ## 8. Your Rights [#8-your-rights] ### 8.1 Rights Under GDPR (EEA, UK, and Switzerland) [#81-rights-under-gdpr-eea-uk-and-switzerland] If you are located in the EEA, the UK, or Switzerland, you have the following rights under applicable data protection law: * **Right of access:** Request a copy of the personal data we hold about you * **Right to rectification:** Request correction of inaccurate or incomplete personal data * **Right to erasure:** Request deletion of your personal data where there is no compelling reason for continued processing * **Right to restriction:** Request restriction of processing in certain circumstances * **Right to data portability:** Receive your personal data in a structured, commonly used, machine-readable format * **Right to object:** Object to processing based on legitimate interest, including profiling * **Right to withdraw consent:** Where processing is based on consent, withdraw your consent at any time without affecting the lawfulness of prior processing * **Right to lodge a complaint:** File a complaint with your local data protection supervisory authority We will respond to your request within thirty (30) days. This period may be extended by sixty (60) days for complex requests, with prior notification. ### 8.2 Rights Under CCPA / CPRA (California Residents) [#82-rights-under-ccpa--cpra-california-residents] If you are a California resident, you have additional rights under the California Consumer Privacy Act and the California Privacy Rights Act. See [Section 11.2](#112-california-ccpa--cpra) for details. ### 8.3 Exercising Your Rights [#83-exercising-your-rights] To exercise any of your rights, contact our Data Protection Officer at [privacy@settlemint.com](mailto:privacy@settlemint.com). We may request verification of your identity before processing your request. We will not discriminate against you for exercising any of your privacy rights. ## 9. Cookies and Tracking Technologies [#9-cookies-and-tracking-technologies] ### 9.1 What We Use [#91-what-we-use] We use the following categories of cookies and tracking technologies: **Strictly Necessary Cookies:** Required for the Platform to function. These cannot be disabled. They include cookies for authentication, session management, and security. **Functional Cookies:** Enable enhanced functionality and personalization, such as language preferences and user interface settings. **Analytics Cookies:** Help us understand how the Platform is used, including page views, feature usage, and error reporting. We use these to improve the Platform's performance and user experience. **Marketing Cookies:** Used to deliver relevant communications and measure the effectiveness of our marketing campaigns. These are only set with your explicit consent. ### 9.2 Cookie Management [#92-cookie-management] When you first visit the Platform, you will be presented with a cookie consent banner allowing you to accept or reject non-essential cookies. You can update your cookie preferences at any time through the Platform's settings. You can also manage cookies through your browser settings. Note that disabling certain cookies may affect the functionality of the Platform. ### 9.3 Do Not Track [#93-do-not-track] The Platform does not currently respond to "Do Not Track" browser signals. However, you can manage your tracking preferences through the cookie consent mechanism described above. ## 10. Children's Privacy [#10-childrens-privacy] The Platform is not directed at individuals under the age of 18. We do not knowingly collect personal data from children. If we become aware that we have collected personal data from a child without appropriate consent, we will take steps to delete such data promptly. ## 11. Jurisdiction-Specific Provisions [#11-jurisdiction-specific-provisions] ### 11.1 European Economic Area, United Kingdom, and Switzerland [#111-european-economic-area-united-kingdom-and-switzerland] If you are in the EEA, UK, or Switzerland, the following additional provisions apply: * **Data Protection Officer:** You may contact our DPO at [privacy@settlemint.com](mailto:privacy@settlemint.com) * **Supervisory authority:** You have the right to lodge a complaint with the Belgian Data Protection Authority (Gegevensbeschermingsautoriteit) at [www.gegevensbeschermingsautoriteit.be](https://www.gegevensbeschermingsautoriteit.be), or your local supervisory authority * **Legal bases:** All processing activities have a documented legal basis as described in [Section 3](#3-how-we-use-your-personal-data) * **Automated decision-making:** We do not make decisions based solely on automated processing, including profiling, that produce legal effects or similarly significantly affect you, unless required for contract performance or with your explicit consent ### 11.2 California (CCPA / CPRA) [#112-california-ccpa--cpra] If you are a California resident, the following additional provisions apply under the California Consumer Privacy Act (as amended by the California Privacy Rights Act): **Categories of Personal Information Collected:** In the preceding twelve (12) months, we have collected the categories of personal information described in [Section 2](#2-personal-data-we-collect), which correspond to the following CCPA categories: identifiers; commercial information; internet or electronic network activity; geolocation data; and professional or employment-related information. **Your California Rights:** * **Right to know:** Request disclosure of the categories and specific pieces of personal information we have collected, the sources of collection, the business purposes, and the categories of third parties with whom we share it * **Right to delete:** Request deletion of your personal information, subject to certain exceptions * **Right to correct:** Request correction of inaccurate personal information * **Right to opt-out of sale/sharing:** We do not sell your personal information. We do not share your personal information for cross-context behavioral advertising * **Right to limit use of sensitive personal information:** Request that we limit our use of sensitive personal information to purposes necessary to provide the Services * **Right to non-discrimination:** We will not discriminate against you for exercising your CCPA rights **Submitting Requests:** To exercise your California rights, contact us at [privacy@settlemint.com](mailto:privacy@settlemint.com). We will verify your identity before processing your request. We will respond within forty-five (45) calendar days, which may be extended by an additional forty-five (45) days with notice. **Authorized Agents:** You may designate an authorized agent to submit requests on your behalf. The agent must provide written authorization signed by you. ### 11.3 Brazil (LGPD) [#113-brazil-lgpd] If you are located in Brazil, you have rights under the Lei Geral de Proteção de Dados (LGPD), including the right to access, correct, anonymize, block, or delete personal data. To exercise these rights, contact [privacy@settlemint.com](mailto:privacy@settlemint.com). ### 11.4 Other Jurisdictions [#114-other-jurisdictions] If you are located in a jurisdiction with data protection laws granting you additional rights not covered above, we will comply with those requirements. Contact our Data Protection Officer for jurisdiction-specific information. ## 12. Data Processing on Your Behalf [#12-data-processing-on-your-behalf] ### 12.1 Customer as Controller [#121-customer-as-controller] When you use the Platform to process personal data of your end users (for example, through KYC/KYB verification workflows or asset holder management), you act as the data controller and SettleMint acts as the data processor. ### 12.2 Data Processing Agreement [#122-data-processing-agreement] Our processing of your end users' personal data is governed by a Data Processing Agreement that complies with Article 28 of the GDPR. This agreement covers: * The scope and purpose of processing * The types of personal data processed * The obligations and rights of both parties * Sub-processor management and notification * Data breach notification procedures * Audit rights * Data deletion and return upon termination ### 12.3 Sub-Processors [#123-sub-processors] We use sub-processors to assist in providing the Services. A list of our current sub-processors is available upon request from [privacy@settlemint.com](mailto:privacy@settlemint.com). We will notify you of any changes to our sub-processors, and you may object to a new sub-processor in accordance with the Data Processing Agreement. ## 13. Changes to This Privacy Policy [#13-changes-to-this-privacy-policy] We may update this Privacy Policy from time to time to reflect changes in our practices, the Platform, or applicable law. Material changes will be communicated to you at least thirty (30) days in advance via email or through the Platform. The "Last updated" date at the top of this Policy indicates when the latest revision was made. Your continued use of the Platform after the effective date of an updated Privacy Policy constitutes your acceptance of the changes. If you do not agree with the changes, you should discontinue use of the Platform. ## 14. Contact Us [#14-contact-us] If you have any questions about this Privacy Policy, wish to exercise your rights, or have a complaint about our handling of your personal data, please contact: **Data Protection Officer** SettleMint NV Philipssite 5 bus 1 3001 Leuven, Belgium Email: [privacy@settlemint.com](mailto:privacy@settlemint.com) For general inquiries about the Platform: Email: [support@settlemint.com](mailto:support@settlemint.com) We aim to resolve all complaints internally. If you are not satisfied with our response, you have the right to lodge a complaint with the relevant data protection supervisory authority. # Terms of Service Source: https://docs.settlemint.com/docs/legal/terms-of-service Terms and conditions governing the use of the SettleMint Digital Asset Lifecycle Platform. **Effective date:** March 5, 2026 **Last updated:** March 5, 2026 These Terms of Service ("Terms") constitute a legally binding agreement between you ("User", "you", or "your") and SettleMint NV, a company incorporated under the laws of Belgium with company number 0661.674.810, having its registered office at Kempische Steenweg 311 bus 4.01, 3500 Hasselt, Belgium ("SettleMint", "we", "us", or "our"). These Terms govern your access to and use of the SettleMint Digital Asset Lifecycle Platform ("DALP" or the "Platform"). By accessing or using the Platform, you acknowledge that you have read, understood, and agree to be bound by these Terms. If you are using the Platform on behalf of a legal entity, you represent and warrant that you have the authority to bind that entity to these Terms, and "you" and "your" will refer to that entity. ## 1. Definitions [#1-definitions] **"Account"** means the user account created by or for you to access and use the Platform. **"Authorized User"** means any individual who is authorized by you to access and use the Platform under your Account, including your employees, contractors, and agents. **"Confidential Information"** means any non-public information disclosed by one party to the other in connection with these Terms, including technical, business, financial, and operational information. **"Content"** means any data, information, files, documents, configurations, smart contract code, or other materials that you upload, submit, or transmit through the Platform. **"Digital Assets"** means tokens, securities, bonds, funds, stablecoins, or other digitized financial instruments created, managed, or serviced through the Platform. **"Intellectual Property Rights"** means all patents, copyrights, trademarks, trade secrets, database rights, and other intellectual property rights, whether registered or unregistered. **"Order Form"** means any ordering document, subscription confirmation, or online order flow referencing these Terms and specifying the services, fees, and subscription term applicable to your use of the Platform. **"Personal Data"** has the meaning given to it under applicable data protection legislation, including the EU General Data Protection Regulation (Regulation 2016/679) ("GDPR"). **"Platform"** means the SettleMint Digital Asset Lifecycle Platform, including all associated software, APIs, documentation, blockchain infrastructure, smart contract tooling, and related services. **"Services"** means the services provided through or in connection with the Platform as described in the applicable Order Form. **"Subscription Term"** means the period during which you are authorized to access and use the Platform, as specified in the applicable Order Form. ## 2. Platform Description [#2-platform-description] DALP is a full-stack solution for digital asset tokenization, lifecycle management, and servicing. The Platform enables organizations to create, issue, manage, and service tokenized assets on blockchain networks. Platform capabilities include, but are not limited to: * Creation and deployment of tokenized financial instruments (bonds, equities, funds, stablecoins, and other digital assets) * Compliance and regulatory workflow management, including KYC/KYB verification and transfer restrictions * Smart contract deployment and management * Asset servicing operations such as distributions, corporate actions, and redemptions * User and role-based access management * API access for programmatic integration with third-party systems * Blockchain network connectivity and infrastructure management The specific features and services available to you depend on your subscription tier and applicable Order Form. ## 3. Account Registration and Security [#3-account-registration-and-security] ### 3.1 Account Creation [#31-account-creation] To use the Platform, you must create an Account by providing accurate, current, and complete registration information. You agree to maintain and promptly update your Account information to keep it accurate and complete. ### 3.2 Account Security [#32-account-security] You are responsible for maintaining the confidentiality of your Account credentials, including your password and any multi-factor authentication methods. You agree to immediately notify SettleMint at [support@settlemint.com](mailto:support@settlemint.com) of any unauthorized use of your Account or any other breach of security. ### 3.3 Account Responsibility [#33-account-responsibility] You are solely responsible for all activities that occur under your Account, including actions taken by Authorized Users. SettleMint is not liable for any loss or damage arising from unauthorized use of your Account. ### 3.4 Authorized Users [#34-authorized-users] You may authorize individuals to access the Platform under your Account. You are responsible for ensuring that all Authorized Users comply with these Terms. Any breach of these Terms by an Authorized User will be deemed a breach by you. ## 4. License and Access [#4-license-and-access] ### 4.1 License Grant [#41-license-grant] Subject to your compliance with these Terms and payment of applicable fees, SettleMint grants you a limited, non-exclusive, non-transferable, non-sublicensable, revocable license to access and use the Platform during the Subscription Term solely for your internal business purposes. ### 4.2 Restrictions [#42-restrictions] You agree not to, and will not permit any Authorized User or third party to: * Copy, modify, adapt, translate, or create derivative works of the Platform or any part thereof * Reverse engineer, disassemble, decompile, or otherwise attempt to derive the source code of the Platform, except to the extent expressly permitted by applicable law * Sublicense, sell, lease, rent, lend, assign, distribute, or otherwise transfer rights to the Platform to any third party * Remove, obscure, or alter any proprietary notices, labels, or marks on the Platform * Use the Platform to develop a competing product or service * Use the Platform for any unlawful purpose or in violation of any applicable law or regulation * Interfere with or disrupt the integrity or performance of the Platform or any data contained therein * Attempt to gain unauthorized access to the Platform, related systems, or networks * Use automated means (including bots, scrapers, or crawlers) to access or collect data from the Platform, except through published APIs in accordance with applicable rate limits * Use the Platform to process, store, or transmit any material that infringes or misappropriates the rights of any third party * Use the Platform to transmit malicious code, viruses, or other harmful content ### 4.3 Suspension [#43-suspension] SettleMint reserves the right to suspend your access to the Platform, in whole or in part, immediately upon notice if: (a) you breach these Terms; (b) your use of the Platform poses a security risk to the Platform or any third party; (c) your use of the Platform may subject SettleMint or any third party to liability; or (d) your Account is overdue for payment. SettleMint will use commercially reasonable efforts to provide advance notice of any suspension and to restore access promptly once the grounds for suspension have been resolved. ## 5. Fees and Payment [#5-fees-and-payment] ### 5.1 Fees [#51-fees] You agree to pay all fees specified in the applicable Order Form. Unless otherwise stated in the Order Form, all fees are quoted in euros, are non-refundable, and are exclusive of applicable taxes. ### 5.2 Payment Terms [#52-payment-terms] Invoices are payable within thirty (30) days of the invoice date, unless otherwise specified in the Order Form. Late payments will accrue interest at the rate of 1.5% per month or the maximum rate permitted by applicable law, whichever is lower. ### 5.3 Taxes [#53-taxes] You are responsible for all taxes, duties, and levies imposed by applicable governmental authorities on the transactions contemplated by these Terms, excluding taxes based on SettleMint's net income. ### 5.4 Fee Changes [#54-fee-changes] SettleMint may modify fees upon at least sixty (60) days' written notice prior to the start of a renewal Subscription Term. If you do not agree to the modified fees, you may terminate your subscription by providing written notice before the renewal date. ## 6. Intellectual Property [#6-intellectual-property] ### 6.1 Platform Ownership [#61-platform-ownership] The Platform, including all associated software, documentation, designs, algorithms, and technology, is and remains the exclusive property of SettleMint and its licensors. Nothing in these Terms transfers any Intellectual Property Rights in the Platform to you. ### 6.2 Your Content [#62-your-content] You retain all rights in your Content. By uploading Content to the Platform, you grant SettleMint a limited, non-exclusive, worldwide license to use, process, store, and display your Content solely as necessary to provide the Services. This license terminates when your Content is deleted from the Platform. ### 6.3 Smart Contracts [#63-smart-contracts] Smart contracts that you develop and deploy using the Platform are your property. SettleMint retains ownership of any templates, libraries, modules, or tooling provided as part of the Platform that are incorporated into your smart contracts. You receive a perpetual, non-exclusive license to use such components within the smart contracts you deploy, subject to any open-source license terms that may apply. ### 6.4 Feedback [#64-feedback] If you provide SettleMint with suggestions, enhancement requests, recommendations, or other feedback regarding the Platform ("Feedback"), you grant SettleMint an unrestricted, irrevocable, perpetual, royalty-free license to use, modify, and incorporate such Feedback into the Platform without any obligation to you. ## 7. Data Protection [#7-data-protection] ### 7.1 Personal Data [#71-personal-data] To the extent that your use of the Platform involves the processing of Personal Data, the parties agree to comply with applicable data protection legislation. SettleMint's processing of Personal Data in connection with the Platform is described in our [Privacy Policy](/docs/legal/privacy-policy). ### 7.2 Data Processing Agreement [#72-data-processing-agreement] Where SettleMint processes Personal Data on your behalf as a data processor, the parties will enter into a Data Processing Agreement that complies with Article 28 of the GDPR. The Data Processing Agreement forms part of these Terms. ### 7.3 Your Obligations [#73-your-obligations] You are responsible for ensuring that your use of the Platform complies with all applicable data protection laws, including obtaining any necessary consents from data subjects and providing required notices. ### 7.4 Blockchain Data [#74-blockchain-data] You acknowledge that data written to a blockchain network may be immutable and publicly accessible depending on the network type. You are solely responsible for determining what data is committed to a blockchain and ensuring that no Personal Data is recorded on-chain in violation of applicable data protection laws. ## 8. Confidentiality [#8-confidentiality] ### 8.1 Obligations [#81-obligations] Each party agrees to: (a) hold the other party's Confidential Information in strict confidence; (b) not disclose Confidential Information to any third party except as expressly permitted; and (c) use Confidential Information only for the purposes of exercising rights or fulfilling obligations under these Terms. ### 8.2 Permitted Disclosures [#82-permitted-disclosures] A party may disclose Confidential Information to its employees, contractors, and advisors who have a need to know and are bound by confidentiality obligations at least as protective as those in these Terms. Confidential Information may also be disclosed to the extent required by law or court order, provided that the disclosing party gives the other party prompt written notice (to the extent legally permitted) and cooperates in any effort to obtain protective treatment. ### 8.3 Exclusions [#83-exclusions] Confidential Information does not include information that: (a) is or becomes publicly available through no fault of the receiving party; (b) was lawfully known to the receiving party prior to disclosure; (c) is lawfully obtained from a third party without restriction; or (d) is independently developed without reference to the disclosing party's Confidential Information. ## 9. Warranties and Disclaimers [#9-warranties-and-disclaimers] ### 9.1 Mutual Warranties [#91-mutual-warranties] Each party represents and warrants that: (a) it has the legal power and authority to enter into these Terms; and (b) it will comply with all applicable laws in connection with its performance under these Terms. ### 9.2 Platform Warranty [#92-platform-warranty] SettleMint warrants that during the Subscription Term, the Platform will perform materially in accordance with the applicable documentation. Your sole and exclusive remedy for a breach of this warranty is, at SettleMint's option, repair or replacement of the non-conforming feature, or a pro-rata refund of prepaid fees for the affected period. ### 9.3 Disclaimers [#93-disclaimers] EXCEPT AS EXPRESSLY SET FORTH IN THESE TERMS, THE PLATFORM IS PROVIDED "AS IS" AND "AS AVAILABLE." SETTLEMINT DISCLAIMS ALL OTHER WARRANTIES, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, INCLUDING WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND NON-INFRINGEMENT. SettleMint does not warrant that the Platform will be uninterrupted, error-free, or free of harmful components, or that any Content will be secure or not otherwise lost or damaged. ### 9.4 Blockchain Disclaimer [#94-blockchain-disclaimer] SettleMint does not control the underlying blockchain networks on which Digital Assets may be deployed. SettleMint makes no warranties regarding the operation, availability, security, or finality of any blockchain network. You acknowledge that blockchain transactions may be subject to network congestion, protocol changes, forks, or other events beyond SettleMint's control. ### 9.5 Regulatory Disclaimer [#95-regulatory-disclaimer] SettleMint does not provide legal, regulatory, tax, or financial advice. The Platform's compliance features (including KYC/KYB workflows, transfer restrictions, and regulatory reporting tools) are tools to assist your compliance efforts. You are solely responsible for ensuring that your use of the Platform and any Digital Assets created through the Platform comply with all applicable laws and regulations in all relevant jurisdictions. ## 10. Limitation of Liability [#10-limitation-of-liability] ### 10.1 Exclusion of Consequential Damages [#101-exclusion-of-consequential-damages] TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, NEITHER PARTY WILL BE LIABLE TO THE OTHER PARTY FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, OR PUNITIVE DAMAGES, OR ANY LOSS OF PROFITS, REVENUE, DATA, GOODWILL, OR BUSINESS OPPORTUNITY ARISING OUT OF OR IN CONNECTION WITH THESE TERMS, REGARDLESS OF THE THEORY OF LIABILITY AND WHETHER OR NOT THE PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ### 10.2 Liability Cap [#102-liability-cap] TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, EACH PARTY'S TOTAL AGGREGATE LIABILITY ARISING OUT OF OR IN CONNECTION WITH THESE TERMS WILL NOT EXCEED THE TOTAL FEES PAID OR PAYABLE BY YOU TO SETTLEMINT DURING THE TWELVE (12) MONTHS IMMEDIATELY PRECEDING THE EVENT GIVING RISE TO THE LIABILITY. ### 10.3 Exceptions [#103-exceptions] The limitations in Sections 10.1 and 10.2 do not apply to: (a) either party's indemnification obligations; (b) either party's breach of confidentiality obligations; (c) your breach of the license restrictions in Section 4.2; (d) your payment obligations; or (e) liability that cannot be limited under applicable law. ## 11. Indemnification [#11-indemnification] ### 11.1 By SettleMint [#111-by-settlemint] SettleMint will defend, indemnify, and hold you harmless from any third-party claim that the Platform infringes or misappropriates that third party's Intellectual Property Rights, and will pay any damages finally awarded or settlement amounts agreed to, provided that you promptly notify SettleMint of the claim, give SettleMint sole control of the defense and settlement, and provide reasonable cooperation. ### 11.2 By You [#112-by-you] You will defend, indemnify, and hold SettleMint harmless from any third-party claim arising from: (a) your Content; (b) your use of the Platform in breach of these Terms; (c) your violation of applicable law; or (d) any Digital Assets created, issued, or managed through the Platform, and will pay any damages finally awarded or settlement amounts agreed to. ## 12. Term and Termination [#12-term-and-termination] ### 12.1 Term [#121-term] These Terms commence on the date you first access the Platform and continue until terminated. The Subscription Term is as specified in the applicable Order Form and will automatically renew for successive periods of equal length, unless either party provides written notice of non-renewal at least sixty (60) days before the end of the then-current term. ### 12.2 Termination for Cause [#122-termination-for-cause] Either party may terminate these Terms immediately upon written notice if the other party: (a) commits a material breach that remains uncured for thirty (30) days after written notice; or (b) becomes the subject of insolvency, bankruptcy, receivership, or similar proceedings. ### 12.3 Effects of Termination [#123-effects-of-termination] Upon termination or expiration: (a) all licenses granted under these Terms will immediately terminate; (b) you will cease all use of the Platform; (c) each party will return or destroy the other party's Confidential Information; and (d) SettleMint will make your Content available for export for a period of thirty (30) days following termination, after which SettleMint may delete your Content. ### 12.4 Survival [#124-survival] Sections 1, 6, 7.4, 8, 9.3, 9.4, 9.5, 10, 11, 12.3, 12.4, 13, and 14 will survive any termination or expiration of these Terms. ## 13. Governing Law and Dispute Resolution [#13-governing-law-and-dispute-resolution] ### 13.1 Governing Law [#131-governing-law] These Terms are governed by and construed in accordance with the laws of Belgium, without regard to its conflict of laws principles. The United Nations Convention on Contracts for the International Sale of Goods does not apply. ### 13.2 Dispute Resolution [#132-dispute-resolution] Any dispute arising out of or in connection with these Terms that cannot be resolved amicably within thirty (30) days will be submitted to the exclusive jurisdiction of the courts of Leuven, Belgium, except that either party may seek injunctive or other equitable relief in any court of competent jurisdiction to protect its Intellectual Property Rights or Confidential Information. ## 14. General Provisions [#14-general-provisions] ### 14.1 Entire Agreement [#141-entire-agreement] These Terms, together with any applicable Order Form and Data Processing Agreement, constitute the entire agreement between the parties with respect to the subject matter hereof and supersede all prior or contemporaneous agreements, representations, or understandings. ### 14.2 Amendments [#142-amendments] SettleMint may update these Terms from time to time. Material changes will be communicated to you at least thirty (30) days in advance via email or through the Platform. Your continued use of the Platform after the effective date of the updated Terms constitutes your acceptance. If you do not agree with the changes, you may terminate your subscription before the changes take effect. ### 14.3 Assignment [#143-assignment] You may not assign or transfer these Terms or any rights hereunder without SettleMint's prior written consent. SettleMint may assign these Terms in connection with a merger, acquisition, or sale of all or substantially all of its assets. ### 14.4 Severability [#144-severability] If any provision of these Terms is held to be invalid or unenforceable, the remaining provisions will continue in full force and effect. ### 14.5 Waiver [#145-waiver] No failure or delay by either party in exercising any right under these Terms constitutes a waiver of that right. ### 14.6 Force Majeure [#146-force-majeure] Neither party will be liable for any delay or failure to perform its obligations (other than payment obligations) due to causes beyond its reasonable control, including natural disasters, pandemics, acts of government, wars, terrorism, labor disputes, network or infrastructure failures, or blockchain network outages or protocol changes. ### 14.7 Notices [#147-notices] All notices under these Terms must be in writing and sent to the addresses specified in the applicable Order Form or, for SettleMint, to: SettleMint NV Philipssite 5 bus 1 3001 Leuven, Belgium Email: [support@settlemint.com](mailto:support@settlemint.com) ### 14.8 Third-Party Services [#148-third-party-services] The Platform may integrate with or enable access to third-party services, including blockchain networks, identity verification providers, and financial data services. SettleMint is not responsible for the availability, accuracy, or content of any third-party services and does not endorse any third-party services. Your use of third-party services is subject to the applicable third party's terms and conditions. ### 14.9 Export Compliance [#149-export-compliance] You agree to comply with all applicable export and import control laws and regulations in connection with your use of the Platform. You represent that you are not located in any country that is subject to a comprehensive trade embargo and that you are not on any restricted party list. # Runbooks Source: https://docs.settlemint.com/docs/runbooks Operator runbooks for rotating provider EOAs and performing high-control lifecycle operations. ## Purpose [#purpose] Runbooks document operational procedures that require coordinated system, chain, and provider actions. ## Available runbooks [#available-runbooks] | Runbook | Description | | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | [Rotate provider issuer EOA](/docs/runbooks/rotate-provider-issuer-eoa) | Remove a compromised or scheduled-rotation provider EOA from the tenant TIR, provision a replacement, and verify trust resolution | # Rotate provider issuer EOA Source: https://docs.settlemint.com/docs/runbooks/rotate-provider-issuer-eoa Operational runbook for rotating the DALP-custodied issuer key used by a tenant compliance provider integration. ## When to rotate [#when-to-rotate] Rotate a provider issuer EOA when: * The provider's signing key is suspected to be compromised. * A scheduled key-rotation policy requires replacement. * A provider integration is being decommissioned and the old EOA must stop attesting. * The tenant is moving provider operations to a new integration record. The provider issuer EOA is single-tenant and single-purpose: never share an EOA across tenants or providers. Remove the EOA from the trusted issuer registry **before** decommissioning the underlying key, and keep enough non-bootstrapper management-key holders to recover the registry if anything fails mid-rotation. ## Procedure [#procedure] ### Identify the integration [#identify-the-integration] In the dapp, go to **Platform Settings → Compliance providers** and open the integration's detail page. Note: * The provider name and topic. * The current **provider issuer address** displayed at the top of the detail page. * The webhook URL (you'll keep using the same URL after rotation). ### Remove the old issuer from the trusted issuer registry [#remove-the-old-issuer-from-the-trusted-issuer-registry] Submit a trusted-issuer removal in the dapp's **Identity** surface (or via your platform's trusted-issuer admin tooling) for the current provider issuer address and the integration's claim topic. This stops the old EOA from being trusted before the new one takes over. Do not delete or disable the underlying key until this removal transaction is final. ### Provision the replacement issuer [#provision-the-replacement-issuer] On the integration detail page, click **Retry provisioning** (the same action used to recover from a failed initial provisioning). DALP will: 1. Reuse the stored provider credentials and webhook signing secret. 2. Provision a new tenant-scoped, custodied EOA dedicated to this integration. 3. Register the replacement EOA in the trusted issuer registry for the integration's topic. The action is idempotent: it picks up where the previous attempt stopped and only runs the steps that didn't yet land. ### Confirm the new issuer is active [#confirm-the-new-issuer-is-active] Reload the integration detail page and confirm: * The status is **Active**. * The provider issuer address has changed to the replacement EOA. * The "Last rotated" timestamp reflects this rotation window. ## Verification [#verification] After the integration shows **Active** with the new issuer address, deliver a small synthetic test event from the provider dashboard (a no-op state-change webhook is fine). Confirm in the integration's monitoring or activity surface that the event was processed under the new issuer and produced the expected on-chain effect (or no effect, for monitoring providers that did not cross the configured severity threshold). If the test event surfaces under the **old** issuer address, the trusted-issuer registry still trusts the old key — restart provisioning and verify the registry transactions completed. ## Rollback [#rollback] If replacement provisioning fails **before** the old key is decommissioned: * Re-add the old EOA to the trusted issuer registry for the integration's topic. * Pause the integration until a maintenance window allows a clean retry. If the old key is suspected to be compromised, do **not** re-add it. Keep the integration paused; downstream transfers gated on the integration's topic will block until a replacement EOA is trusted, which is the correct fail-closed behavior under suspected compromise. ## Related [#related] * [Compliance provider onboarding](/docs/developer-guides/compliance/onboarding-a-provider) — full integration setup * [Compliance providers architecture](/docs/architecture/integrations/compliance-providers) — provider-as-issuer trust model # AI assistants Source: https://docs.settlemint.com/docs/user-guides/ai-agents/overview Use AI assistants to manage your digital assets through natural language. Connect Claude, Codex, or OpenClaw to the DALP platform for conversational asset management. AI assistants can connect directly to the DALP platform, allowing you to manage tokens, users, compliance, and operations through natural language conversations instead of navigating the web interface. Looking to integrate AI agents into development workflows? See the [developer guide for AI agent integration](/docs/developer-guides/cli/ai-agents). ## What can AI assistants do? [#what-can-ai-assistants-do] Once connected, you can ask your AI assistant to perform any platform operation: * **"Create a new equity token called Acme Shares with symbol ACME"** – deploys a token with the right configuration * **"Mint 10,000 units of the bond token to the treasury wallet"** – executes the mint operation * **"Show me all users who haven't completed KYC"** – queries and filters user data * **"Pause trading on the BOND-2025 token"** – pauses the token immediately * **"What's the current collateral ratio on our stablecoin?"** – fetches real-time stats * **"Onboard three new investors with these email addresses"** – creates users, registers identities, and starts KYC The assistant handles the technical details – API calls, parameter formatting, transaction signing – while you describe what you want in plain language. *** ## Supported assistants [#supported-assistants] | Assistant | Best for | Setup guide | | ------------------------------------------------ | ------------------------------------------------------------- | ------------------------------- | | [Claude Desktop](https://claude.ai/download) | Conversational asset management, complex multi-step workflows | [Setup Claude](#claude-desktop) | | [Codex Desktop](https://openai.com/index/codex/) | Task automation, batch operations | [Setup Codex](#codex-desktop) | | [OpenClaw](https://openclaw.ai) | Open-source alternative, self-hosted | [Setup OpenClaw](#openclaw) | All assistants connect to DALP through the **Model Context Protocol (MCP)**, an open standard for connecting AI models to external tools. *** ## Claude Desktop [#claude-desktop] ### Step 1: Install the DALP CLI [#step-1-install-the-dalp-cli] Open Terminal and run: ```bash npm install -g @settlemint/dalp-cli ``` ### Step 2: Authenticate [#step-2-authenticate] ```bash dalp login --url https://your-platform.example.com ``` Your browser opens for secure authentication. Once approved, the CLI stores your credentials securely in the system keychain. ### Step 3: Register with Claude Desktop [#step-3-register-with-claude-desktop] The CLI registers itself automatically: ```bash dalp mcp add ``` This detects Claude Desktop and writes the correct MCP configuration. No manual JSON editing needed. ### Step 4: Start using it [#step-4-start-using-it] Restart Claude Desktop. You should see DALP tools available in the tool picker. Start a conversation: > "Show me all tokens on the platform and their current status." Claude will use the DALP tools to query your platform and present the results. *** ## Codex Desktop [#codex-desktop] ### Step 1: Install and authenticate [#step-1-install-and-authenticate] ```bash npm install -g @settlemint/dalp-cli dalp login --url https://your-platform.example.com ``` ### Step 2: Register with Codex [#step-2-register-with-codex] ```bash dalp mcp add ``` This detects Codex and writes the correct MCP configuration automatically. ### Step 3: Start using it [#step-3-start-using-it] Open Codex and ask it to interact with the platform: > "List all equity tokens and show me the total supply of each." *** ## OpenClaw [#openclaw] ### Step 1: Install and authenticate [#step-1-install-and-authenticate-1] ```bash npm install -g @settlemint/dalp-cli dalp login --url https://your-platform.example.com ``` ### Step 2: Register with OpenClaw [#step-2-register-with-openclaw] ```bash dalp mcp add ``` This detects OpenClaw and writes the correct MCP configuration automatically. ### Step 3: Start using it [#step-3-start-using-it-1] OpenClaw will discover DALP tools automatically. Ask it to perform operations: > "What users are pending KYC approval? Approve the ones that have all required documents." *** ## Example conversations [#example-conversations] ### Token operations [#token-operations] > **You:** Create a new deposit token called "Term Deposit Q1" with symbol TDQ1, 6 decimals, and a maturity date of March 31, 2026. > > **Assistant:** I'll create that deposit token for you. \[Creates token, returns address and confirmation] > > **You:** Now mint 500,000 units to the treasury wallet. > > **Assistant:** Done. Minted 500,000 TDQ1 to 0xTREASURY. Transaction hash: 0x... ### Compliance management [#compliance-management] > **You:** Which tokens don't have a country restriction module? Add one to all of them that allows only US and EU investors. > > **Assistant:** I found 3 tokens without country restrictions. Let me add the module to each... \[Configures compliance on each token] ### User management [#user-management] > **You:** How many users signed up this month? Show me the ones without a verified identity. > > **Assistant:** 47 users signed up this month. 12 don't have a verified identity yet. Here's the list: \[Shows users with status] ### Reporting [#reporting] > **You:** Give me a summary of all bond tokens: name, maturity date, total supply, and coupon rate. > > **Assistant:** \[Queries each bond token and presents a formatted table] *** ## Security and permissions [#security-and-permissions] * **Your permissions apply**: The AI assistant operates with your account permissions. It cannot access anything you cannot access yourself. * **Organization scope**: Operations are scoped to your active organization. Switch organizations with `dalp auth org-switch` if needed. * **Confirmation for mutations**: AI assistants will describe what they plan to do before executing state-changing operations. You can review and approve each action. * **Audit trail**: All operations performed through AI assistants are logged with full traceability. * **Credential security**: Your credentials are stored in the system keychain (macOS) or an encrypted file, never in the AI assistant's memory or conversation history. *** ## Troubleshooting [#troubleshooting] ### "DALP tools not available" [#dalp-tools-not-available] 1. Verify the CLI is installed: `dalp --version` 2. Verify authentication: `dalp whoami` 3. Check the MCP configuration file path and JSON syntax 4. Restart the AI assistant after configuration changes ### "Authentication required" errors [#authentication-required-errors] Your session may have expired. Re-authenticate: ```bash dalp login --url https://your-platform.example.com ``` Then restart the AI assistant. ### "Permission denied" errors [#permission-denied-errors] Your account may lack the required role. Ask your platform administrator to grant the necessary permissions, or check your current roles: ```bash dalp system access-manager roles-list --format json ``` *** ## Next steps [#next-steps] * **[Platform overview](/docs/user-guides/platform-setup/platform-overview)** – Understand the platform before using AI assistants * **[User onboarding](/docs/user-guides/user-management/user-onboarding)** – Learn the user creation workflow * **[Compliance overview](/docs/user-guides/compliance/overview)** – Understand compliance modules before configuring via AI # Create asset Source: https://docs.settlemint.com/docs/user-guides/asset-creation/create-asset Deploy a new tokenized asset using the Asset Designer wizard. Creating an asset deploys a new tokenized security on the blockchain. The Asset Designer wizard guides you through selecting an asset class, choosing a published instrument template, configuring any template fields, and enabling compliance modules. **Common use cases:** * **Bond issuance** - Create fixed-income securities with maturity dates and coupon payments * **Equity tokenization** - Issue shares with voting rights and dividend distribution * **Fund launch** - Create investment fund units with NAV tracking * **Stablecoin creation** - Deploy collateral-backed stable value instruments * **Deposit certificates** - Tokenize bank deposits or cash equivalents * **Precious metal tokenization** - Configure metal-backed assets with weight, purity, storage, and valuation fields * **Real estate fractionalization** - Divide property ownership into tradeable fractions ## Prerequisites [#prerequisites] * **Asset manager role** - You must have the Asset manager role assigned on the system access manager * **Completed onboarding** - Your account must be fully onboarded with a verified identity * **Wallet setup** - Your wallet must be configured with PIN or OTP verification * **Instrument template available** - A published instrument template for your desired asset class must be available and backed by a usable token factory ## Asset types [#asset-types] The platform supports seven asset types organized into four classes: | Asset class | Asset types | Description | | ---------------- | --------------------------- | ------------------------------------------------------- | | Fixed Income | Bond | Debt securities with maturity dates and fixed payments | | Flexible Income | Equity, Fund | Variable return assets like shares and investment funds | | Cash Equivalent | Stablecoin, Deposit | Stable value assets pegged to fiat currencies | | Real World Asset | Precious Metal, Real Estate | Tokenized physical assets and property ownership | ## Steps [#steps] ### Open the Asset Designer [#open-the-asset-designer] Click **Asset designer** in the sidebar under **Asset management** to open the Asset Designer wizard. The wizard runs as a routed flow under `/asset-designer`. Each step has its own URL, so browser Back and Forward move between Asset Designer steps instead of leaving the flow. If you refresh the page while creating an asset, the wizard restores your in-progress values and returns you to the current step in the same browser session. If you use DALP navigation to leave the `/asset-designer` wizard after manually choosing an asset class, selecting a template, entering an asset name or symbol, choosing a compliance template, or setting initial permissions, DALP asks you to confirm before discarding the wizard state. After a successful deployment, confirmed in-app leave, or sign-out, the saved wizard state is cleared. Asset designer button in sidebar ### Select asset class [#select-asset-class] Choose the asset class that matches your security type. DALP shows the published instrument templates available for that class. Asset class selection ### Select instrument template [#select-instrument-template] Choose the published instrument template for the asset you want to create. The wizard shows the required features and compliance options attached to each available template. Instrument template selection ### Review template-required features [#review-template-required-features] Instrument templates can include **Required features**. When a template has required features, the Asset Designer shows them as badges during template selection. The deployed asset inherits those token-level behaviors from the selected template, then asks for additional inputs only for the badges that need configuration. #### Template-specific fields and defaults [#template-specific-fields-and-defaults] Selecting an instrument template can prefill feature settings and metadata values in the wizard. The Asset Designer clears feature-specific values from the previous selection before applying the new template, so switching templates does not carry old maturity, yield, fee, or conversion inputs into the next asset. Templates can also add metadata fields to the follow-up details step. These fields can be text, number, date, enum, ISIN, or address inputs. Required metadata fields must be filled before the wizard continues. Template defaults are applied when you select the template. For required-feature badges, fields that are fixed by the template can remain hidden while still being submitted with the asset configuration. Template feature settings that are not part of the template's required features can seed the wizard, but they are not submitted as standalone feature configurations. #### Self-contained required features [#self-contained-required-features] These badges do not add a follow-up configuration step from the required-feature badge itself. Use the [token feature catalog](/docs/architecture/components/token-features) for the canonical behavior reference. Current self-contained badges include Historical Balances, Voting Power, Permit, Transaction Fee, Transaction Fee Accounting, Fixed Treasury Yield, and Conversion Minter. Transaction-fee badges only create non-zero fee behavior when the template supplies fee settings; Fixed Treasury Yield only creates a configured yield schedule when the template supplies yield settings. #### Configurable required features [#configurable-required-features] These badges show follow-up inputs in the Asset Designer after template selection. Use the [token feature catalog](/docs/architecture/components/token-features) for canonical feature behavior; this table only lists the extra guided inputs. | Feature | Asset Designer asks for | | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | | [Maturity Redemption](/docs/architecture/components/token-features/maturity-redemption) | Maturity date, denomination asset, face value, and redemption treasury. | | [AUM Fee](/docs/architecture/components/token-features/aum-fee) | Management-fee basis points and recipient settings. | | [External Transaction Fee](/docs/architecture/components/token-features/external-transaction-fee) | External fee token, absolute mint, burn, or transfer fees, and recipient settings. | | [Conversion](/docs/architecture/components/token-features/conversion) | Target token, denomination asset, discount, conversion window, debt handling, and related rules. | Required features define what the token can do. Self-contained badges are inherited directly from the template; transaction-fee badges only create non-zero fee behavior when the template supplies fee settings. Configurable badges add feature-specific inputs to the guided setup. Compliance modules define who may hold, transfer, mint, or redeem under your policy. ### Configure asset basics [#configure-asset-basics] Enter the core asset properties. The Asset Designer shows live character counters below text inputs and highlights when you approach limits. * **Name** - Full name of the asset (e.g., "Acme Corporate Bond 2025") * **Symbol** - Trading symbol (3-12 characters, e.g., "ACME25") * **Decimals** - Decimal precision (0-18, typically 18 for fungible assets or 0 for whole units) * **Jurisdiction** - Country code for regulatory jurisdiction Asset basics form ### Configure asset details [#configure-asset-details] Complete the template-specific fields shown by the wizard. DALP inserts the details step only when the selected template exposes configurable required-feature inputs or metadata fields. Templates without input fields skip directly to pricing. ### Enable compliance modules [#enable-compliance-modules] Select which compliance rules to enforce on your asset. These modules are enforced automatically on-chain for every transfer. | Module | Description | | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Identity verification** | Requires both sender and recipient wallets to have a verified OnchainID with valid compliance verifications (e.g., KYC, accreditation). Wallets without verified identity cannot send or receive the asset. | | **Country allow list** | Only wallets with an OnchainID issued in specified countries can hold the asset. Use for securities restricted to certain jurisdictions. | | **Country block list** | Wallets with an OnchainID from blocked countries cannot hold the asset. Use for sanctions compliance or regulatory restrictions. | | **Address block list** | Explicitly block specific wallet addresses from holding or transferring the asset, regardless of their identity status. | | **Investor count limit** | Caps the maximum number of unique wallet addresses that can hold the asset. Useful for private placements with regulatory investor limits (e.g., Reg D 99 investor cap). | | **Time lock** | Enforces a minimum holding period before the asset can be transferred. Investors must hold for the specified duration after receiving units. | | **Transfer approval** | Requires an authorized approver to manually approve each transfer before it executes. Use for securities requiring transfer agent oversight. | | **[Collateral requirement](/docs/user-guides/compliance/collateral)** | Requires sufficient collateral backing before minting new units. Validates only minting operations, not transfers. | If you select a compliance template, the asset must submit every control declared by that template. DALP rejects the deployment when the template controls are missing from the final configuration or when a country allow list has no countries selected, because an empty allow list would block every transfer. For more details on compliance modules, see the [compliance overview](/docs/user-guides/compliance/overview). Compliance modules selection ### Review and deploy [#review-and-deploy] Review all configuration details on the summary page. When ready, click **Deploy** and authenticate with your PIN or OTP to submit the transaction. Summary and deploy ## After deployment [#after-deployment] Once the transaction confirms: * Your new asset appears under **Asset management** in the sidebar, listed under its asset class (e.g., Fixed income > Bonds) * You are automatically granted the `governance` role on the asset * Pricing and classification verifications are issued to the asset's identity * **The asset is paused by default** - transfers and issuance are disabled until activated ### Activating the asset [#activating-the-asset] New assets are created in a paused state to allow for review and final approvals before going live. This gives compliance teams a review point before any units are issued. To activate the asset: 1. Navigate to the asset detail page under **Asset management** in the sidebar 2. Click **Manage Asset** and select **Unpause** Manage Asset menu with Unpause option Unpause form 3. Authenticate with your PIN or OTP to confirm Only users with the `EMERGENCY` for this asset can unpause it. Contact your administrator if you don't have the required permissions. Once unpaused, authorized users can begin [issuing the asset](/docs/user-guides/asset-servicing/mint-assets) to investor accounts. ## Troubleshooting [#troubleshooting] | Issue | Solution | | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Create Asset button disabled | Verify you have the `tokenManager` role assigned | | Instrument template not available | Use [Select instrument template](#select-instrument-template) to choose a published template for the chosen asset class, or ask an administrator to publish one backed by a usable token factory. | | Name/symbol already exists | Choose a unique name and symbol combination | | Deployment fails | Ensure wallet has sufficient gas and wallet verification is configured | | Missing template feature inputs | Confirm the selected published instrument template is linked to a usable token factory and includes the required feature configuration. | | Missing compliance options | Use [Enable compliance modules](#enable-compliance-modules) to confirm the needed compliance rules are selected and configured for the asset. If you selected a compliance template, every required control must appear in the final configuration. | | Empty country allow list | Add at least one allowed country, remove the country allow-list control, or choose a different compliance template. An empty allow list blocks every transfer, so DALP rejects it before deployment. | # Instrument templates Source: https://docs.settlemint.com/docs/user-guides/asset-creation/instrument-templates Create, reuse, and publish instrument templates for the Asset Designer. Instrument templates help asset teams reuse the same issuance setup across assets. A template groups the asset class, deployable asset type, required token features, feature defaults, and metadata fields that the Asset Designer should collect during issuance. Use instrument templates when you want repeatable setup for a family of assets, such as bonds with a maturity feature, stable-value instruments with required metadata, or organization-specific asset classes. ## Template types [#template-types] DALP uses templates at different points in asset setup: | Template type | What it configures | When to use it | | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | | Instrument template | Asset class, deployable asset type, required token features, feature defaults, and metadata fields for the Asset Designer. | Reuse a complete issuance pattern for assets that share the same business and operational shape. | | Token template settings | Token-level behaviors that are attached during issuance, such as maturity redemption, yield, fees, conversion, or permit support. | Make a behavior mandatory for every asset created from the instrument template. | | Metadata schema | Fields the Asset Designer must collect, including field key, label, type, mutability, required status, and constraints. | Collect repeatable product, regulatory, or reporting data during asset creation. | | Compliance template | Compliance modules, jurisdiction scope, required controls, and whether selected controls remain configurable in the designer. | Reuse policy controls for assets that follow the same regulatory or organization-specific ruleset. | These templates compose rather than replace each other. An instrument template handles the asset setup, token template settings handle token behavior, metadata fields handle asset-specific information, and a compliance template can be selected in the compliance step when the asset needs reusable policy controls. ## Where templates fit [#where-templates-fit] An instrument template does not issue an asset by itself. It defines reusable defaults and requirements that the Asset Designer applies when an operator creates an asset. ## Template sources and statuses [#template-sources-and-statuses] The instrument template list separates templates by source and status: | Field | Meaning | | ----------------- | -------------------------------------------------------------------------------------------------------- | | Source | DALP library templates are system-provided. Organisation templates are created by your organization. | | Status | Draft templates can still be prepared before use. Published templates are ready for operators to select. | | Asset class | The business category used to group and filter templates. | | Required features | Token features that the template attaches to assets created from it. | The list page supports search, source filtering, status filtering, and asset-class filtering. Template cards show the template name, description, source, status, and any required feature badges. ## Create a template [#create-a-template] Open **Platform settings → Templates → Instrument templates**, then choose one of the creation modes: * **From existing** copies an existing template's name, description, asset class, required token features, metadata schema, feature configuration, and base asset type into a new draft. * **From scratch** starts a new draft where you select the asset class and configure the template manually. When creating or editing a template draft, configure: * **Name and description** so operators can pick the right template later. * **Asset class** to connect the template to a managed asset category. * **Required features** to attach token-level behavior such as maturity, yield, fees, conversion, or other installed token features. After the template draft exists, use the template detail page to add metadata fields and adjust feature-specific settings before publishing it. Template names must be unique within the organization. If a duplicate name is entered, DALP keeps the form open and shows an inline validation message. ## Work with required features [#work-with-required-features] Required features are shown as badges on template cards and as active features on the template detail page when the matching feature is available in the environment. On the template detail page, users with template management permissions can: * add available features, * remove active features, * adjust feature configuration, * choose which configured feature settings remain editable in the Asset Designer. Configurable feature settings are controlled per setting. For example, a template can keep a fee recipient fixed while allowing an operator to enter the fee value during asset creation. Settings that are not configurable stay on the template as defaults and do not appear as editable inputs in the Asset Designer. During asset creation, configurable settings from the selected template are grouped by token feature. Each feature card has its own heading and can be expanded or collapsed independently, so operators can complete maturity, yield, fee, conversion, and metadata fields without mixing the controls together. If a copied template references a feature that is not deployed in the current environment, DALP warns the operator while preparing the new draft instead of silently treating that draft as fully ready. ## Define metadata fields [#define-metadata-fields] The **Metadata** tab appears when a template has metadata fields or when the current user can manage the template. Each metadata field has a field key, type, mutability, label, and optional constraints. Supported metadata field types include text, number, date, enum, ISIN, and address fields. Required fields must be completed during asset creation. Fields marked immutable are locked on the issued asset after deployment; fields that are not immutable can still be managed through the supported metadata update flow, subject to permissions and governance controls. ## Publish, duplicate, edit, and delete [#publish-duplicate-edit-and-delete] Template actions are available from the template detail page. | Action | When to use it | | --------- | ---------------------------------------------------------------------------------------------------- | | Edit | Update an organization-owned template before or after publication. System templates are read-only. | | Duplicate | Start a new draft from an existing template while preserving the original. | | Publish | Move an organization-owned draft template to the published state so it can be selected for issuance. | | Delete | Remove an organization-owned template after explicit confirmation. | DALP library templates are read-only. They can be duplicated when your organization needs a similar template with its own asset class, feature choices, or metadata fields. ## How templates affect asset creation [#how-templates-affect-asset-creation] When an operator selects a template in the Asset Designer: * required feature badges explain which token behaviors are included, * template-specific metadata fields are added to the details step, * required metadata fields must be completed before the wizard continues, * template defaults are applied for feature-specific settings, * feature-specific fields are grouped into separate cards when the selected template includes more than one configurable feature, * the selected template determines which asset class and base asset behavior the deployed asset uses. Some required token features also need another token before the operator can select the template. Templates that use maturity redemption, fixed treasury yield, or external transaction fee behaviour need an ERC-20 token in the tenant. Templates that use conversion need an equity-class target token. Templates can also have feature dependencies: * Include `conversion` when a template includes `conversion-minter`. * The minter must be paired with the conversion behaviour it serves. If a required token is missing, the Asset Designer disables the affected template and shows the missing token type. The operator can create or register the required token, then return to the wizard and select the template after the token is available. Feature dependencies belong to the template configuration itself. If a template feature dependency is missing, update the template's feature selection before publishing or updating the template. ## Examples [#examples] Use these examples as patterns for how template parts affect issuance: * A bond-style instrument template can require maturity redemption and fixed treasury yield features. During asset creation, the operator sees those token behaviors as required feature badges and completes any configurable settings before deployment. * A real-world asset template can require metadata such as an ISIN, valuation date, address, or reporting category. Required metadata fields block wizard progress until completed. Immutable metadata fields are locked on the issued asset, while restricted-mutable fields can still follow the supported metadata update flow. * A compliance template can group reusable controls such as country lists, identity checks, holding limits, and transfer approvals. In the compliance step, the Asset Designer filters published templates by the asset context and carries selected controls into the asset's initial compliance setup. * Operators can also skip a compliance template and configure compliance modules manually when the asset needs one-off policy setup instead of a reusable template. For the end-to-end issuance flow, see [Create asset](/docs/user-guides/asset-creation/create-asset). For the conceptual model behind templates, see [Asset model](/docs/architecture/start-here/asset-model). # Asset detail workspace Source: https://docs.settlemint.com/docs/user-guides/asset-servicing/asset-detail-workspace Review an issued asset, monitor token tabs, and find the actions available for the current token. The asset detail workspace is the operating view for an issued asset. Open the workspace from **Asset management**. Select an asset class, select the asset type or template, and then select the asset. The page header shows the asset name, status, breadcrumb, and **Manage Asset** menu. The menu is context-sensitive: some actions are hidden when the asset or system configuration does not support them, while actions that are visible but currently unavailable appear disabled with a tooltip that explains the specific permission, system, or asset-state check blocking the action. ## What the workspace shows [#what-the-workspace-shows] The default tab summarizes the asset before you move into a specific workflow. Use it to review: * token identity, symbol, supply, decimals, factory, and status information * asset-specific details such as bond, deposit, equity, fund, precious metal, real estate, or stablecoin fields when those details are configured * capability tiles for configured token features such as maturity and redemption, yield, fees, conversion, historical balances, permit, governance, and metadata * supply, holder distribution, transfer volume, collateral, and bond-status charts where the matching data is available * related operations and identity claims for the asset Feature tiles link to their detail pages when that feature has its own workflow. For example, maturity, yield, conversion, AUM fee, events, verifications, and metadata open as full detail pages instead of staying inside the tab frame. ## Token tabs [#token-tabs] The tab bar is generated from the asset configuration. Some tabs are always available, and some only appear when the asset has the matching extension, compliance module, or system capability. | Tab | When it appears | Use it for | | ------------------ | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | | Token information | Always | Review the asset summary, configured features, identity claims, metrics, and related operations. | | Holders | Always | Review token holders, balances, available balances, and frozen amounts. | | Actions | Always | Review pending, upcoming, and completed actions filtered to this asset. | | Compliance | Always | Review the compliance modules configured for the asset. | | Documents | Always | Review and manage documents associated with the asset on the token documents route. | | Metadata route | From the metadata capability tile on Token information | Review on-chain metadata on the dedicated metadata detail page rather than inside the tab frame. | | Feeds | When the feeds directory is installed | Review feed-related data for the asset. | | Permissions | When the asset is access-managed | Review token-level permissions for the asset. | | Denomination asset | For bond-style assets that include a denomination asset | Review the settlement or denomination asset attached to the token. | ## Token documents and metadata routes [#token-documents-and-metadata-routes] The documents route is `/token/{assetClassSlug}/{templateSlug}/{tokenAddress}/documents`. Use it from the **Documents** tab to review and manage files associated with the asset. The metadata detail route is `/token/{assetClassSlug}/{templateSlug}/{tokenAddress}/metadata`. Metadata opens from the metadata capability tile and uses a full detail page rather than the tab frame. Allowlist and blocklist behavior is represented through the compliance configuration rather than separate public tabs in the current workspace. ## Manage asset actions [#manage-asset-actions] Use **Manage Asset** for actions that change the token or start an operator workflow. The menu is permission-gated and context-sensitive. Depending on the asset and system configuration, the menu can include minting, transfers, pause or unpause, forced transfer workflows, verification actions, transfer approvals, price updates, collateral updates, and token-sale creation. If an asset uses an external transaction fee, mint, burn, and transfer confirmation screens check the connected wallet before you submit when that wallet is the fee payer. The screen shows whether the wallet has enough fee-token balance and enough allowance for the external fee feature to collect the fee. The readiness check applies in these connected-wallet cases: * minting to the connected wallet * burning from the connected wallet * transferring from the connected wallet When the allowance is too low, use **Approve fee allowance** from the confirmation screen. The approval flow selects the required spender for you, then the readiness check updates so you can submit the original action. When an operator action charges a different wallet, such as minting to another recipient or burning another holder, that third-party wallet is not preflighted in the confirmation screen. Confirm the fee-token balance and allowance with that holder before submitting. If an expected action is missing or disabled, check: * your token-level role assignments on the **Permissions** tab * whether the asset is paused * whether the required compliance module or token feature is configured * whether your wallet has enough balance for holder actions * whether your wallet has enough fee-token balance and allowance when the asset uses an external transaction fee * whether the system-level feature and permission for the workflow are enabled * for verification actions, whether the asset has a token identity * for collateral actions, whether the collateral module is configured and the token identity address is available * for token-sale creation, whether token sales are enabled, your user can create token sales, the asset has a registered token identity, and a token-sale addon factory is available ## Related guides [#related-guides] * [Create asset](/docs/user-guides/asset-creation/create-asset) * [Mint assets](/docs/user-guides/asset-servicing/mint-assets) * [Pause or unpause an asset](/docs/user-guides/asset-servicing/pause-unpause-asset) * [Change asset admin roles](/docs/user-guides/asset-servicing/change-asset-admin-roles) * [Token feature catalog](/docs/architecture/components/token-features) # Burn assets Source: https://docs.settlemint.com/docs/user-guides/asset-servicing/burn-assets Permanently remove units of a security from circulation. Burning permanently removes units from circulation and reduces total supply. This is the on-chain equivalent of share cancellation or bond redemption. **Common use cases:** * **Redemptions** - Cancelling units when investors exit a fund or redeem bonds at maturity * **Buybacks** - Retiring shares after a repurchase program * **Regulatory enforcement** - Removing assets from non-compliant holders * **Error correction** - Reversing erroneous issuances (with proper authorization) Burning is permanent and cannot be undone. ## Prerequisites [#prerequisites] * `SUPPLY_MANAGEMENT_ROLE` for the asset (check **Permissions** tab) * Target addresses must hold sufficient unfrozen balance ## Steps [#steps] ### Open burn dialog [#open-burn-dialog] Navigate to the asset page and open the **Holders** tab. Click the **...** (actions) dropdown on the holder row you want to burn from, then select **Burn**. Holders tab with burn action ### Enter details [#enter-details] Select the holder address and enter amount. For multiple addresses, click **Add address**. Burn tokens form \### Confirm Review the supply change and authenticate with PIN/OTP. ## Verify totals [#verify-totals] Return to the **Asset details** tab to confirm **Total burned** reflects the cumulative amount burned for the asset. The totals update from indexed supply history, so allow a short refresh if needed. ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------ | ------------------------------------------- | | Burn button disabled | Verify `SUPPLY_MANAGEMENT_ROLE` assignment | | Insufficient balance | Check holder's available (unfrozen) balance | | Amount exceeds available | Some assets may be frozen by custodian | ## Related [#related] * [Asset servicing guides](/docs/user-guides/introduction) * [Mint assets](/docs/user-guides/asset-servicing/mint-assets) # Change asset admin roles Source: https://docs.settlemint.com/docs/user-guides/asset-servicing/change-asset-admin-roles Modify administrator roles and permissions for specific assets. This guide explains how to change administrator roles for specific assets, allowing you to assign or modify permissions for asset management functions. ## Prerequisites [#prerequisites] * **Permission manager** role on the asset (permission management) * Access to the asset you want to modify * Understanding of role responsibilities ## About asset admin roles [#about-asset-admin-roles] Each asset has its own set of administrators with specific roles: | Role | Description | Key Permissions | | ---------------------- | ---------------------- | ----------------------------------------------------------------------------------------------- | | **Permission manager** | Permission management | Manage other administrators' roles and permissions | | **Custodian** | Compliance enforcement | Freeze addresses and force transfers for legal orders, sanctions compliance, dispute resolution | | **Emergency** | Emergency operations | Pause/unpause asset operations | | **Supply Management** | Asset supply control | Mint and burn tokens | | **Governance** | Asset configuration | Update asset metadata and settings | ## Changing asset admin roles [#changing-asset-admin-roles] ### Access asset management [#access-asset-management] Navigate to **Asset Management** > **\[Asset Type]** (e.g., Equity, Bond, Fund) and click on the asset you want to modify. Asset details ### Access permissions [#access-permissions] Click on the **Permissions** tab to view asset administrators. This displays administrator addresses, current role assignments, and an actions menu. Permissions tab ### Initiate role change [#initiate-role-change] Find the administrator whose roles you want to modify and click the **three dots (...)** in the Actions column. Select **Change roles** from the dropdown to open the role management sheet. To add a new administrator, use the **Add admin** button in the toolbar above the permissions table. ### Configure roles [#configure-roles] Toggle on/off the roles needed for this administrator based on their responsibilities: Permission manager, Custodian, Emergency, Supply Management, or Governance. ### Review and authenticate [#review-and-authenticate] Click **Continue** to review the role change summary, then click **Save**. Enter your PIN when prompted to authenticate the blockchain transaction. The administrator's roles are updated immediately after confirmation. ## Best practices [#best-practices] ### Role assignment [#role-assignment] * Follow principle of least privilege * Assign only necessary roles for specific functions * Document role assignment decisions * Regular review of role assignments ### Security considerations [#security-considerations] * Limit Permission manager role to trusted individuals * Separate operational roles from governance roles * Maintain backup administrators for critical roles ### Operational efficiency [#operational-efficiency] * Assign Supply Management to token operators * Give Emergency role to operations team * Separate Custodian role for transfer management * Reserve Governance for strategic decisions ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | | Cannot see Permissions tab | • Verify you have Permission manager role on asset
• Check you're viewing correct asset
• Ensure asset is fully deployed | | Change roles option missing | • Confirm you have Permission manager permissions
• Verify administrator is part of asset
• Check your own role permissions | | Role changes not taking effect | • Wait for blockchain confirmation
• Refresh the page
• Check transaction was successful | | Cannot assign specific role | • Verify role compatibility
• Check platform role requirements
• Ensure administrator has necessary platform roles | ## Related guides [#related-guides] * [Add Administrators](/docs/user-guides/platform-setup/add-admins) - Adding new asset administrators * [Platform Setup Overview](/docs/user-guides/platform-setup/platform-overview) - Understanding platform vs asset roles * [Create Asset](/docs/user-guides/asset-creation/create-asset) - Initial asset setup with administrators # Forced transfer Source: https://docs.settlemint.com/docs/user-guides/asset-servicing/forced-transfer Execute custodial transfers without holder consent for regulatory compliance. A forced transfer moves assets between accounts without the holder's consent or signature. This replicates the authority that traditional custodians and transfer agents have to move securities under specific legal circumstances. Forced transfers are day-two operations. Limit them to governed exception cases, execute them with the correct asset role, and link them to supporting evidence for audit review. **Common use cases:** * **Court-ordered seizures** - Complying with judgments for fraud, divorce settlements, or creditor claims * **Estate transfers** - Moving assets from deceased holders to beneficiaries per probate orders * **Sanctions compliance** - Freezing or transferring assets of sanctioned entities to blocked accounts * **Lost access recovery** - Restoring assets to verified owners who lost wallet access * **Bankruptcy proceedings** - Transferring assets to court-appointed trustees Forced transfers bypass holder authorization, normal transfer initiation, and configured compliance-module transfer checks. They do not bypass the registered-recipient boundary. Use them only when legally required, run them through an operator with the Custodian role, send assets only to recipients you have verified, and keep detailed records for audit purposes. ## Prerequisites [#prerequisites] * `CUSTODIAN_ROLE` for the asset (check **Permissions** tab) * Source address must hold sufficient balance * Legal documentation justifying the transfer (court order, death certificate, etc.) ## Steps [#steps] ### Open forced transfer dialog [#open-forced-transfer-dialog] Navigate to **Asset Management**, select the asset, click **Manage Asset** > **Forced transfer**. Manage Asset menu with Forced transfer ### Enter details [#enter-details] * **From address** - Account losing assets * **To address** - Recipient (trustee, beneficiary, blocked account) * **Amount** - Units to transfer Forced transfer form \### Confirm Review details and authenticate with PIN/OTP. ### Document the evidence [#document-the-evidence] Record the transaction hash, the approving operator, the reason for the exception, and the supporting legal documentation. ## Troubleshooting [#troubleshooting] | Issue | Solution | | -------------------- | ------------------------------------------- | | Option not visible | Verify `CUSTODIAN_ROLE` assignment | | Insufficient balance | Check source address balance on Holders tab | | Transaction reverts | Verify contract not paused, sufficient gas | ## Related operations [#related-operations] The `CUSTODIAN_ROLE` also enables freeze/unfreeze and wallet recovery operations. See [Per-Asset RBAC](/docs/architecture/components/asset-contracts/rbac) for the full permission matrix. # Mint assets Source: https://docs.settlemint.com/docs/user-guides/asset-servicing/mint-assets Issue new units of a security to investor accounts. Minting creates new units of a security and credits them to investor accounts. This is the on-chain equivalent of issuing new shares, bonds, or fund units. **Common use cases:** * **Primary issuance** - Initial distribution after a new offering closes * **Subscription settlement** - Crediting investors after payment confirmation * **Stock dividends** - Issuing additional shares in lieu of cash dividends * **Capital calls** - Issuing fund units when investors fund commitments ## Prerequisites [#prerequisites] * `SUPPLY_MANAGEMENT_ROLE` for the asset (check **Permissions** tab) * Recipients must have verified OnchainID with required compliance verifications * For capped assets: sufficient headroom under supply cap * For collateralized assets: sufficient [collateral backing](/docs/user-guides/compliance/collateral) For collateralized assets, minting remains unavailable until a collateral verification is issued by a trusted issuer. If you do not have collateral permissions, ask a claim policy manager to add you as a trusted issuer in **Claim Topics & Issuers**. The mint form's protocol limit reflects remaining collateral after the current supply is backed, so it may be lower than the total collateral amount shown on the asset. ## Steps [#steps] ### Open mint dialog [#open-mint-dialog] Navigate to **Asset Management**, select the asset, click **Manage Asset** > **Mint tokens**. Manage Asset menu with Mint tokens ### Enter details [#enter-details] Select recipient address and enter amount. For multiple recipients, click **Add recipient**. Mint tokens form \### Confirm Review the supply change and authenticate with PIN/OTP. ## Troubleshooting [#troubleshooting] | Issue | Solution | | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | Mint button disabled | Verify `SUPPLY_MANAGEMENT_ROLE` assignment and confirm collateral backing for collateralized assets | | Recipient not eligible | Check OnchainID and compliance verifications | | Exceeds supply cap | Reduce amount or request cap increase | | Insufficient collateral | Issue a new [collateral verification](/docs/user-guides/compliance/collateral#issue-a-collateral-verification) with higher amount | # Pause and unpause assets Source: https://docs.settlemint.com/docs/user-guides/asset-servicing/pause-unpause-asset Control asset operations by pausing or unpausing to manage transfers and activities. Pausing and unpausing controls whether an asset is active for transfers and operations. New assets are created in a paused state for safety and must be explicitly activated when ready for use. ## Prerequisites [#prerequisites] * **Emergency** role on the specific asset * Understanding of current asset status (paused or active) * All required configurations should be completed (permissions, compliance modules, etc.) ## When to pause/unpause assets [#when-to-pauseunpause-assets] ### Newly created assets [#newly-created-assets] * All new assets are paused by default * Unpause after completing configuration and verification * Ensures safety before activation ### After maintenance [#after-maintenance] * Assets may be paused for updates or maintenance * Unpause when operations should resume * Coordinate with stakeholders before reactivation ### Emergency situations resolved [#emergency-situations-resolved] * Assets paused due to security concerns * Unpause when issues are resolved * Verify all systems are functioning correctly ## Managing asset status [#managing-asset-status] ### Access asset management [#access-asset-management] Navigate to **Asset Management** > **\[Asset Type]** (e.g., Equity, Bond, Fund) and click on the asset you want to manage. ### Open Manage Asset dropdown [#open-manage-asset-dropdown] On the asset details page, find the **Manage Asset** dropdown. This dropdown is available on all tabs and shows operations based on your permissions and the asset's current state. Manage Asset dropdown ### Select pause/unpause action [#select-pauseunpause-action] From the **Manage Asset** dropdown, choose **Unpause Asset** (if currently paused) or **Pause Asset** (if currently active). The available option depends on your Emergency role and the asset's current status. ### Review and authenticate [#review-and-authenticate] Review the summary showing the asset name, current status, and proposed action. Click **Pause Asset** or **Unpause Asset** to proceed, then enter your PIN to sign the blockchain transaction. ### Verify status change [#verify-status-change] After the transaction confirms, the asset status updates ("Paused" ↔ "Active") and transfer capabilities change accordingly. Asset showing Active status ## Effects of pause/unpause [#effects-of-pauseunpause] ### When unpaused (Active) [#when-unpaused-active] * **Transfers enabled** - Users can send/receive assets * **Operations allowed** - Minting, burning, and other operations work * **Compliance active** - All compliance rules are enforced * **Status updated** - Asset shows as "Active" in the UI ### When paused [#when-paused] * **Transfers disabled** - All transfer operations are blocked * **Operations suspended** - Minting, burning stopped (except by admins) * **Status updated** - Asset shows as "Paused" in the UI * **Emergency access** - Only Emergency role holders can change status ### Continued restrictions [#continued-restrictions] * **Role requirements** - Users still need appropriate permissions * **Compliance checks** - Verification requirements remain in effect * **Module restrictions** - Compliance modules continue to enforce rules ## Security considerations [#security-considerations] ### Emergency role responsibility [#emergency-role-responsibility] * Only trusted operators should have Emergency role * Use unpause capability responsibly * Consider business hours for activation * Have pause capability ready if issues arise ### Operational readiness [#operational-readiness] * Ensure support team is available * Have monitoring systems active * Prepare for increased transaction volume * Review emergency procedures ## Troubleshooting [#troubleshooting] | Issue | Solution | | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Pause/Unpause option not available | • Verify you have the Emergency role for this specific asset
• Check the asset's current status
• Ensure you're viewing the correct asset | | Transaction fails | • Verify wallet has sufficient gas
• Check your PIN is correct
• Ensure network connectivity
• Try the operation again | | Status not updating | • Wait for blockchain confirmation
• Refresh the page
• Check transaction was successful
• Verify transaction on block explorer | | Users cannot transfer after unpause | • Check compliance requirements are met
• Verify users have required verifications
• Confirm asset permissions are correct
• Review compliance module settings | ## Related operations [#related-operations] * [Change Asset Admin Roles](/docs/user-guides/asset-servicing/change-asset-admin-roles) - Configure Emergency role permissions * [Asset Compliance](/docs/user-guides/compliance/overview) - Set up verification requirements * [Forced Transfer](/docs/user-guides/asset-servicing/forced-transfer) - Emergency transfer operations ## Emergency use cases [#emergency-use-cases] ### When to pause [#when-to-pause] * Security incident or vulnerability discovered * Regulatory compliance issue identified * Technical maintenance required * Legal order to halt operations ### When to unpause [#when-to-unpause] * New asset ready for launch * Issues resolved after emergency pause * Maintenance completed * Regulatory approval received The Emergency role should be used responsibly. Pausing/unpausing affects all token holders and should only be done when necessary. # Collateral requirement Source: https://docs.settlemint.com/docs/user-guides/compliance/collateral The collateral compliance module ensures assets are backed by verifiable reserves. Learn how to configure collateral ratios and issue collateral verifications. The collateral compliance module ensures your tokenized assets are backed by verifiable reserves. When enabled, the blockchain validates that sufficient collateral exists before allowing new units to be minted. This module only applies to **minting operations**. Regular transfers between wallets are not affected by collateral requirements. ## How collateral validation works [#how-collateral-validation-works] When you attempt to mint new units of an asset with the collateral module enabled, the system performs the following checks: The module checks if the asset's identity has a valid collateral verification. The verification must be issued by a trusted issuer and not expired. The collateral amount must meet or exceed the configured ratio based on the post-mint total supply. ## Roles and responsibilities [#roles-and-responsibilities] Two roles are typically involved in managing collateral compliance: | Role | Responsibilities | | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | **Asset issuer** (governance role) | Configures the collateral module during asset creation. Sets the collateral ratio and trusted issuers. | | **Trusted issuer** (verification issuer) | Issues and manages collateral verifications. Must be registered in the [trusted issuers registry](/docs/architecture/security/identity-compliance). | The asset issuer configures *how* collateral is validated. A separate trusted issuer (often a compliance officer or auditor) issues the actual collateral verifications based on off-chain proof of reserves. ## Configuration parameters [#configuration-parameters] When configuring the collateral module during asset creation, you set three parameters: | Parameter | Description | Valid values | Example | | ------------------------------ | --------------------------------------------------------------------------------------------------------------------- | ------------------------ | ----------------------------------- | | **Proof topic ID** | Identifies the verification type to check. The system provides a default collateral topic. | System-provided topic ID | `collateral (ID: 565916...0528)` | | **Collateral ratio** | Required backing ratio in basis points. 10000 = 100% backing. | 0 to 20000 | 10000 (100%), 15000 (150%) | | **Additional trusted issuers** | Issuers not in the platform registry. Only needed if your issuer is not globally registered for the collateral topic. | List of wallet addresses | Leave empty to use registry issuers | The collateral ratio is specified in basis points (1/100th of a percent): - **0** = Collateral enforcement disabled (not recommended) - **10000** = 100% backing (1,000 minted units require 1,000 collateral) - **15000** = 150% over-collateralized (1,000 minted units require 1,500 collateral) - **20000** = 200% maximum ratio (1,000 minted units require 2,000 collateral) ## Configure collateral during asset creation [#configure-collateral-during-asset-creation] ### Open the Asset Designer [#open-the-asset-designer] Navigate to **Asset management > Asset designer** in the sidebar. ### Complete asset setup [#complete-asset-setup] Follow the wizard through asset class, type, and basic configuration. See [Create asset](/docs/user-guides/asset-creation/create-asset) for details. ### Enable collateral requirement [#enable-collateral-requirement] On the compliance modules step, select **Collateral requirement** from the available modules. This module requires collateral proof before token transfers. Compliance module selection with Collateral requirement ### Configure collateral settings [#configure-collateral-settings] Set your collateral parameters: 1. **Proof topic ID** - Select the collateral topic from the dropdown (system default is pre-configured) 2. **Collateral ratio** - Enter the required backing ratio in basis points (10000 = 100%, 20000 = 200%) 3. **Additional trusted issuers** - Add any issuer addresses not in the platform registry (optional) Collateral requirement configuration form ### Complete deployment [#complete-deployment] Review your configuration and deploy the asset. The collateral module is now active. ## Issue a collateral verification [#issue-a-collateral-verification] After the asset is deployed, a trusted issuer must add a collateral verification to the asset's identity before minting can occur. ### Prerequisites [#prerequisites] You must be either: * A **trusted issuer** for the collateral topic (see [Trusted issuers registry](/docs/architecture/security/identity-compliance)) * OR have the **governance role** on the asset ### Verification fields [#verification-fields] | Field | Description | Format | | ---------- | ----------------------------------------------------- | ---------------------------------------------------------------------- | | **Amount** | The collateral value backing the asset | Number (in asset units, e.g., 1000 for 1000 units worth of collateral) | | **Expiry** | When the verification expires (must be in the future) | Date and time | ### Navigate to Entities [#navigate-to-entities] Go to **Participants > Entities** in the sidebar. This section shows all entities (including assets) that have an on-chain identity. Entity addresses use the Web3 display format, so you can see avatars and any saved display names alongside the address. Entities page showing assets with on-chain identities ### Select your asset [#select-your-asset] Find and click on the asset you created that you want to issue the collateral verification for. Assets appear in the entities list because they have their own on-chain identity. ### Open verification management [#open-verification-management] On the asset's entity detail page, locate the **Identity and verifications** tile. Click **Manage verifications** to view all verifications issued on this asset's identity. Entity detail page with Identity and verifications tile ### View existing verifications [#view-existing-verifications] You'll see a list of all verifications currently issued on this asset. This may include pricing, classification, or other verifications depending on your asset configuration. Click the **Add verification** button in the top right corner to open the verification creation form. Verifications list with Add verification button ### Enter collateral details [#enter-collateral-details] In the verification form: 1. **Verification topic** - Select **collateral** from the dropdown 2. **Amount** - Enter the total collateral amount (e.g., 10000 for 10,000 units) 3. **Expiry Timestamp** - Select a future date and time when this verification should expire Collateral verification form with topic, amount, and expiry fields ### Submit the verification [#submit-the-verification] Review the verification details in the confirmation screen, then click **Issue verification** to submit the transaction. Confirmation before issuing verification Collateral verifications have an expiry date. Plan to renew verifications before they expire to avoid blocking future minting operations. ## Example scenario [#example-scenario] A company wants to issue equity shares with 150% collateral backing to provide extra investor protection. ### Configuration [#configuration] * **Collateral ratio:** 150% * **Proof topic:** System default (collateral) * **Trusted issuers:** Empty (using global registry) ### Collateral verification [#collateral-verification] * **Amount:** 15,000 * **Expiry:** December 31, 2025 ### Minting simulation [#minting-simulation] | Operation | Current supply | Mint amount | Post-mint supply | Required collateral (150%) | Available collateral | Result | | ------------ | -------------- | ----------- | ---------------- | -------------------------- | -------------------- | -------------------------------- | | Initial mint | 0 | 5,000 | 5,000 | 7,500 | 15,000 | ✅ **Allowed** - 15,000 ≥ 7,500 | | Second mint | 5,000 | 3,000 | 8,000 | 12,000 | 15,000 | ✅ **Allowed** - 15,000 ≥ 12,000 | | Third mint | 8,000 | 5,000 | 13,000 | 19,500 | 15,000 | ❌ **Blocked** - 15,000 \< 19,500 | ### Why the third mint fails [#why-the-third-mint-fails] The third mint fails because: * Post-mint supply would be 13,000 units * At 150% ratio, required collateral = 13,000 × 1.5 = 19,500 * Current collateral verification only covers 15,000 * A new collateral verification with higher amount is needed ### To unblock [#to-unblock] 1. Have the trusted issuer issue a new collateral verification with amount ≥ 19,500 2. Retry the minting operation ## Troubleshooting [#troubleshooting] | Issue | Cause | Solution | | ------------------------------------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------- | | Minting blocked with "Insufficient collateral" | Collateral verification amount is less than required | Issue new verification with higher amount | | Minting blocked with "No collateral verification" | No valid verification exists on asset identity | Have trusted issuer issue a collateral verification | | Minting blocked with "Verification expired" | The collateral verification has passed its expiry date | Issue a new verification with future expiry | | Cannot issue verification | User is not a trusted issuer or doesn't have governance role | Request trusted issuer role or governance role from administrator | ## Next steps [#next-steps] * **[Create asset](/docs/user-guides/asset-creation/create-asset)** - Deploy a new asset with compliance modules * **[Mint assets](/docs/user-guides/asset-servicing/mint-assets)** - Issue new units after configuring collateral * **[Configure equity collateral (API)](/docs/developer-guides/runbooks/configure-equity-collateral)** - Programmatic configuration for developers * **[Trusted issuers registry](/docs/architecture/security/identity-compliance)** - Become a trusted issuer # Configure trusted issuers Source: https://docs.settlemint.com/docs/user-guides/compliance/configure-trusted-issuers Configure trusted entities who can issue verifications for compliance. ## Prerequisites [#prerequisites] * **Verification Policy Manager** role or equivalent permissions * Understanding of your compliance requirements * Identified entities who will issue verifications ## About trusted issuers [#about-trusted-issuers] Trusted issuers are entities authorized by your organization to issue verifications that are automatically recognized for compliance purposes. For background on how the verification system works, see [Compliance Overview](/docs/user-guides/compliance/overview). Trust relationships are specific to your organization. An issuer trusted by Organization A may not be trusted by Organization B for the same verification topics. ## Configuring trusted issuers [#configuring-trusted-issuers] ### Access verification settings [#access-verification-settings] Navigate to **Platform Settings** > **Verification Topics & Issuers**. The page shows current verification topics and existing trusted issuers with their identity address, assigned topics, and an actions menu. Verification Topics and Issuers page ### Add new trusted issuer [#add-new-trusted-issuer] Click **Add trusted issuer** to start the configuration process. ### Select issuer [#select-issuer] Choose who will be the trusted issuer by selecting an existing user from the dropdown or entering a valid identity address manually. The user must have completed onboarding and have an on-chain identity. Trusted issuers are registered based on their identity address, not their EOA (wallet address). The issuer must have a deployed on-chain identity contract. If the user is already listed as a trusted issuer, use **Edit topics** from the trusted issuers table to update their topic assignments instead of adding them again. ### Choose verification topics [#choose-verification-topics] Select which verification topics this issuer will be trusted for using the multi-select dropdown. Consider the issuer's expertise and authority when assigning topics. Multiple issuers can be trusted for the same topic, providing redundancy and flexibility in your verification process. Multi-select topics dropdown ### Review and add issuer [#review-and-add-issuer] Click **Continue** to see the summary, then click **Add issuer**. Enter your PIN when prompted to authenticate the on-chain transaction that registers the trust relationship. ### Verify trusted issuer [#verify-trusted-issuer] After the transaction confirms, the new issuer appears in the trusted issuers list and can immediately begin issuing verifications for the assigned topics. ## Managing trusted issuers [#managing-trusted-issuers] ### Update issuer topics [#update-issuer-topics] To modify which topics an issuer is trusted for: 1. Find the issuer in the trusted issuers list 2. Click the three dots (...) in the **Actions** column 3. Select **"Edit topics"** from the dropdown 4. Modify topic selections using the multi-select dropdown 5. Save changes with PIN authentication ### Remove trusted issuer [#remove-trusted-issuer] To revoke an issuer's trusted status: 1. Find the issuer in the trusted issuers list 2. Click the three dots (...) in the **Actions** column 3. Select **"Remove issuer"** from the dropdown 4. Confirm removal with PIN authentication Removing an issuer removes that issuer's trusted issuer configuration. Existing verification records remain available for review, but claims from that issuer no longer count as trusted for eligibility checks once the issuer is removed. ## Best practices [#best-practices] ### Issuer selection criteria [#issuer-selection-criteria] Choose trusted issuers based on: * **Authority** - Legal or professional standing * **Expertise** - Knowledge of verification area * **Independence** - Avoid conflicts of interest * **Reliability** - Consistent and accurate ### Topic assignment principles [#topic-assignment-principles] * **Segregation** - Separate issuer types by domain * **Redundancy** - Multiple issuers for critical topics * **Specialization** - Match expertise to topics * **Compliance** - Follow regulatory requirements ### Operational considerations [#operational-considerations] 1. **Regular audits** - Review issuer activities 2. **Rotation** - Periodically update issuers 3. **Training** - Ensure issuers understand responsibilities 4. **Documentation** - Record issuer selection rationale ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | Cannot add issuer | • Verify issuer has on-chain identity
• Check your Verification Policy Manager role
• Ensure issuer has Verification Issuer role | | Topics not appearing | • Confirm transaction completed
• Refresh the page
• Check contract deployment | | Verifications not trusted | • Ensure issuer is registered for topic
• Verify verification hasn't expired
• Check verification data integrity | ## Integration with compliance [#integration-with-compliance] Trusted issuers work with: * [Verify KYC](/docs/user-guides/compliance/verify-kyc) - Issue identity verifications * Token compliance modules - Enforce verification requirements * Transfer restrictions - Gate transactions on verifications ## Related guides [#related-guides] * [Compliance Overview](/docs/user-guides/compliance/overview) - Complete compliance reference * [Verify KYC](/docs/user-guides/compliance/verify-kyc) - Using verifications * [Add Administrators](/docs/user-guides/platform-setup/add-admins) - Grant Verification Issuer role * [Create Asset](/docs/user-guides/asset-creation/create-asset) - Configure compliance modules # Manage KYC data Source: https://docs.settlemint.com/docs/user-guides/compliance/manage-kyc-data Review, approve, reject, or request updates to user KYC submissions as an Identity Manager. This guide explains how Identity Managers review and manage KYC (Know Your Customer) submissions from users. Managing KYC data is a prerequisite step before issuing on-chain KYC verifications. ## Prerequisites [#prerequisites] * **Identity Manager** role or equivalent permissions * Understanding of your organization's KYC requirements * Access to the Participants section ## About KYC management [#about-kyc-management] Users complete their KYC profiles with personal information and supporting documents, then submit for review. As an Identity Manager, you review these submissions and take one of three actions: | Action | When to use | Result | | ------------------ | --------------------------------------------- | ------------------------------------------------ | | **Approve** | All information is correct and complete | User profile is approved, ready for verification | | **Reject** | Submission has fundamental issues | User must correct and resubmit | | **Request update** | Minor issues or additional information needed | New draft created with your feedback for user | ## Reviewing KYC submissions [#reviewing-kyc-submissions] ### Access user KYC data [#access-user-kyc-data] Navigate to **Participants** > **Users** and select a user. On the user detail page, find the **KYC** section in the sidebar or click the KYC status tile. ### View submission details [#view-submission-details] The KYC detail page shows all version history for the user. Click any row to open the version details sheet showing: * Personal information (name, date of birth, country, residency status, national ID) * Uploaded documents with download links * Submission timestamp and status * Previous review notes or rejection reasons KYC version detail sheet ### Review documents [#review-documents] Download and examine each uploaded document. Verify that: * Documents are legible and complete * Information matches the form fields * Documents are current (not expired) * Document types match requirements (government ID, proof of address) ### Take action [#take-action] Based on your review, choose one of the three actions available in the version detail sheet. ## Approving submissions [#approving-submissions] When a submission meets all requirements: ### Open approval dialog [#open-approval-dialog] Click **Approve** in the version detail sheet. The approval dialog opens. ### Add optional notes [#add-optional-notes] Enter any internal notes about the approval. These notes are stored for audit purposes. **Note:** Depending on your organization's configuration, these notes may be visible to the user, so avoid including sensitive internal information. ### Confirm approval [#confirm-approval] Click **Approve** to confirm. The submission status changes to **Approved** and the user can proceed to receive on-chain verifications. After approval, follow [Verify KYC](/docs/user-guides/compliance/verify-kyc) to issue the on-chain KYC verification. ## Rejecting submissions [#rejecting-submissions] When a submission has fundamental issues that require complete correction: ### Open rejection dialog [#open-rejection-dialog] Click **Reject** in the version detail sheet. The rejection dialog opens. ### Provide rejection reason [#provide-rejection-reason] Enter a clear explanation of why the submission is rejected. This message is shown to the user, so be specific about what needs to change. Minimum 10 characters required. **Example reasons:** * "Document is expired. Please upload a current passport or national ID." * "Name on document does not match the name entered in the form." * "Proof of address document is illegible. Please provide a clearer scan." ### Confirm rejection [#confirm-rejection] Click **Reject** to confirm. The submission status changes to **Rejected** and the user sees your feedback on their profile page. ## Requesting updates [#requesting-updates] When minor corrections or additional information are needed without fully rejecting the submission: ### Open update request dialog [#open-update-request-dialog] Click **Request update** in the version detail sheet. The update request dialog opens. ### Describe required changes [#describe-required-changes] Enter a clear message explaining what the user needs to update or provide. Minimum 10 characters required. **Example requests:** * "Please add a proof of address document (utility bill or bank statement from the last 3 months)." * "Date of birth appears incorrect. Please verify and update." * "Please upload the second page of your passport showing the signature." ### Submit request [#submit-request] Click **Request update** to confirm. A new draft version is created for the user with your feedback displayed. The user can edit and resubmit. ## Version history [#version-history] The KYC detail page shows the complete version history for each user: | Column | Description | | --------- | ------------------------------------------- | | Version | Sequential version number | | Status | Current status (draft, submitted, approved) | | Submitted | When the version was submitted for review | | Reviewed | When and by whom the version was reviewed | Click any row to view full details including documents and review notes. ## Integration with verification workflow [#integration-with-verification-workflow] KYC data management is part of the broader user onboarding workflow: 1. **User completes onboarding** - Creates wallet and on-chain identity 2. **User provides KYC data** - Fills profile and uploads documents 3. **Identity Manager reviews** - Approves, rejects, or requests updates (this guide) 4. **Identity Manager registers user** - Adds user to identity registry 5. **Identity Manager verifies KYC** - Issues on-chain verification See [Onboarding runbook](/docs/user-guides/runbooks/onboard-user-im) for the complete workflow. ## Best practices [#best-practices] ### Review consistency [#review-consistency] * Apply the same standards to all submissions * Document your organization's specific requirements * Consult with compliance team for edge cases ### Clear communication [#clear-communication] * Provide specific, actionable feedback * Reference exact documents or fields that need attention * Explain why information is insufficient and what must change ### Timely processing [#timely-processing] * Review submissions promptly to avoid delays * Use request update for minor issues rather than full rejection * Batch similar reviews when possible for efficiency ### Audit trail [#audit-trail] * Add notes to approvals explaining verification steps taken * Keep records of document verification methods * Maintain consistent documentation standards ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------------- | ------------------------------------------------------------------------------------------------------------ | | Cannot see KYC section | Verify you have Identity Manager role with KYC read permissions | | Cannot download documents | Check browser permissions for downloads. Try a different browser if issues persist. | | Approve/Reject buttons disabled | Only submitted or under review versions can be actioned. Draft versions must be submitted by the user first. | | User not receiving feedback | Confirm the action completed successfully. Check user can access their profile page. | | Cannot find user submission | Use the search function in Participants. Verify the user submitted the profile instead of saving a draft. | ## Related guides [#related-guides] * [Provide KYC Data](/docs/user-guides/user-management/provide-kyc-data) - How users complete their KYC profile * [Register User](/docs/user-guides/user-management/register-user) - Register users in the identity registry * [Verify KYC](/docs/user-guides/compliance/verify-kyc) - Issue on-chain KYC verifications after approval * [Onboarding Runbook](/docs/user-guides/runbooks/onboard-user-im) - Complete user onboarding workflow * [Configure Trusted Issuers](/docs/user-guides/compliance/configure-trusted-issuers) - Set up verification permissions # Compliance overview Source: https://docs.settlemint.com/docs/user-guides/compliance/overview Compliance modules enforce on-chain rules for your tokenized assets. Learn about available modules and how to configure them during asset creation. Compliance modules are on-chain rules that automatically enforce regulatory and business requirements for your tokenized assets. The verification system enables trusted entities to issue attestations that prove compliance with these requirements. # Compliance Modules [#compliance-modules] Compliance modules are smart contracts that validate every transaction against your business rules. ## When to configure compliance modules [#when-to-configure-compliance-modules] You select compliance modules during [asset creation](/docs/user-guides/asset-creation/create-asset) using the Asset Designer wizard. Some modules can be added or modified after deployment if you have the **governance role** on the asset. However, it's best to configure compliance requirements upfront to avoid disrupting existing holders. Compliance management integrated into the operator dashboard ## Available compliance modules [#available-compliance-modules] | Module | Description | Validates | | ----------------------------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------- | | Identity verification | Requires verified OnchainID with valid verifications (KYC, accreditation) | Both sender and recipient | | Country allow list | Only wallets from specified countries can hold the asset | Recipient | | Country block list | Wallets from blocked countries cannot hold the asset | Recipient | | Address block list | Explicitly block specific wallet addresses | Both | | Investor count limit | Caps maximum unique holders (e.g., Reg D 99 investor limit) | Transfers increasing holder count | | Time lock | Enforces minimum holding period before transfers | Sender | | Transfer approval | Requires manual approval before each transfer executes | Each transfer | | [Collateral requirement](/docs/user-guides/compliance/collateral) | Requires sufficient collateral backing before minting new units | Minting operations only | When configuring address-based modules (identity allow/block lists, address block lists, and trusted issuers), you can select from existing identities or enter custom addresses manually. ## How modules work together [#how-modules-work-together] When multiple compliance modules are enabled, **all** modules must pass for a transaction to succeed. If any module rejects the transaction, it fails with an error indicating which compliance check was not met. For example, if you enable both identity verification and country allow list: * The recipient must have a verified OnchainID * The recipient's identity must be from an allowed country * Both conditions must be true for the transfer to proceed # Verification System [#verification-system] ## Understanding verifications [#understanding-verifications] ### What are verifications? [#what-are-verifications] Verifications are on-chain attestations that confirm specific information about: * **Investors** - Identity, accreditation, eligibility * **Issuers** - Regulatory compliance, licensing, jurisdiction * **Assets** - Collateral backing, pricing, classification * **Contracts** - Smart contract identity verification ### Verification components [#verification-components] Each verification contains: * **Topic** - What is being verified (e.g., KYC, Accreditation) * **Scheme** - The signature scheme used (e.g., ECDSA or CONTRACT) * **Issuer** - Who verified the information * **Signature** - The cryptographic signature of the verification * **Data** - Verification details (often hashed for privacy) * **URI** - Optional URI reference (e.g., IPFS hash for additional data) **Important:** Verifications do not have expiration dates by default. Instead, issuers can manually revoke verifications when they are no longer valid. ## How trusted issuers work [#how-trusted-issuers-work] ### Anyone can issue verifications [#anyone-can-issue-verifications] Any identity with an on-chain presence can technically issue verifications. However, for the platform to recognize and enforce these verifications for compliance purposes, the issuer must be designated as **trusted** by your organization. ### Organization-specific trust [#organization-specific-trust] Trust relationships are organization-specific: * **Issuer X** may be trusted by **Organization A** for KYC verifications * **Issuer X** may NOT be trusted by **Organization B** for the same topic * Each organization maintains its own list of trusted issuers per verification topic ### Two types of verification issuers [#two-types-of-verification-issuers] **Trusted issuers:** * Can add verifications directly to user identities on-chain * Their verifications are automatically recognized by compliance modules * Require explicit configuration by platform administrators **Regular issuers:** * Can create verifications but cannot add them directly to identities * Must provide verifications off-chain to users * Users decide whether to add these verifications to their own identities * Verifications are NOT automatically recognized for compliance Verification topics define available compliance checks ## Available verification topics [#available-verification-topics] ### Investor-level verification topics [#investor-level-verification-topics] | Topic | Purpose | Common Issuers | Regulatory Context | | ------------------------------------------ | --------------------------------------------------------------------------- | ---------------------------------------- | ---------------------------------------------------- | | **Know Your Customer (KYC)** | Identity verification (passport, government ID, residency) | Compliance officers, KYC providers | Required for AML/CTF compliance and basic onboarding | | **Anti-Money Laundering (AML)** | AML/CTF screening (sanctions lists, PEP screening, adverse media) | Compliance officers, banks | Usually paired with KYC verification | | **Qualified Institutional Investor (QII)** | QII status under securities regulations | Banks, insurers, pension funds | US Investment Company Act, Japanese FIEA | | **Professional Investor** | Professional/qualified investor criteria | Regional regulators, professional bodies | EU MiFID II, Hong Kong SFO, Singapore SFA | | **Accredited Investor** | Accredited investor qualification (income, net worth, institutional status) | Lawyers, accountants, banks | US SEC Regulation D, Rule 501 | | **Accredited Investor (Verified)** | Document-verified accredited status with supporting documentation | Licensed professionals | US Regulation D 506(c) offerings | | **Regulation S** | Non-US person status as defined in Regulation S | Compliance officers | Required for US Securities Act Reg S exemptions | ### Asset-level verification topics [#asset-level-verification-topics] | Topic | Purpose | Common Issuers | Examples | | --------------------- | ------------------------------------- | ----------------------------- | ------------------------------------------------------- | | **Collateral** | Asset backing verification | Custodians, collateral agents | Physical assets, cash reserves, securities | | **Unique Identifier** | Official asset identification | Numbering agencies, operators | ISIN for securities, Real Estate Number (REN) | | **Classification** | Asset class and category | Operators, regulators | COMMON\_EQUITY, GROWTH\_EQUITY, RESIDENTIAL, MIXED\_USE | | **Base Price** | Asset valuation | Price oracles, valuers | Market price, appraisal value | | **Issuer** | Legal issuer of the asset | Legal counsel | Corresponds to on-chain TokenIssuer identity | | **Location** | Asset location data (for real estate) | Asset operators, registrars | City, district code, area ID (privacy-preserving) | ### Issuer-level verification topics [#issuer-level-verification-topics] | Topic | Purpose | Common Issuers | Regulatory Context | | ----------------------- | ------------------------------------------- | ------------------------------------- | --------------------------------------------------- | | **Prospectus Filed** | Valid prospectus filed or published | Regulatory authorities | Required under securities/markets law unless exempt | | **Prospectus Exempt** | Exemption from prospectus requirements | Legal counsel, regulators | Small offers, private placements, MiCA de-minimis | | **Licensed** | Regulatory license or authorization held | Licensing authorities | MiCA CASP license, SEC registration, EU AIFM | | **Reporting Compliant** | Ongoing disclosure and reporting compliance | Auditors, regulatory authorities | Periodic financial reports, annual filings | | **Jurisdiction** | Issuer's legal jurisdiction | Legal counsel, regulatory authorities | Legal domicile and regulatory oversight | ### General verification topics [#general-verification-topics] | Topic | Purpose | Common Issuers | | --------------------- | ------------------------------------ | --------------------- | | **Contract Identity** | Smart contract identity verification | System administrators | # Compliance Strategies [#compliance-strategies] ## For regulated securities [#for-regulated-securities] * Government-licensed issuers * Multiple verification layers * Strict expiration periods * Regular re-verification ## For private placements [#for-private-placements] * Internal compliance team * Lawyer attestations * Longer validity periods * Risk-based approach ## For institutional markets [#for-institutional-markets] * Exchange verifications * Mutual recognition * Automated verification * High-volume capable # Setup Guides [#setup-guides] ## Verification framework [#verification-framework] * **[Configure Trusted Issuers](/docs/user-guides/compliance/configure-trusted-issuers)** - Configure entities who can issue verifications * **[Register User](/docs/user-guides/user-management/register-user)** - Register users in identity registry * **[Verify KYC](/docs/user-guides/compliance/verify-kyc)** - Issue KYC verifications for users ## Asset compliance [#asset-compliance] * **[Collateral requirement](/docs/user-guides/compliance/collateral)** - Configure collateral backing for your assets * **[Create asset](/docs/user-guides/asset-creation/create-asset)** - Deploy a new asset with compliance modules ## Developer guides [#developer-guides] * **[Configure equity collateral (API)](/docs/developer-guides/runbooks/configure-equity-collateral)** - Programmatic configuration # Compliance templates Source: https://docs.settlemint.com/docs/user-guides/compliance/templates Create, publish, and reuse compliance templates that group DALP compliance modules for asset creation. Compliance templates group compliance modules for reuse during asset creation. Operators can keep a template in draft, publish the template when ready, and then select the published template in the Asset Designer. ## What are compliance templates? [#what-are-compliance-templates] A compliance template is a collection of compliance modules with predefined configurations. When you apply a template to an asset, DALP copies the template modules into the asset's compliance configuration. ### How template types fit together [#how-template-types-fit-together] DALP separates reusable asset setup into three template areas: | Template type | Defines | Usage | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | | Compliance template | Compliance modules, jurisdictions, required controls, and draft or published status. | Selected in the Asset Designer compliance step when an asset should reuse a policy pattern. | | Token template | Token-level behaviors the issued asset should include, such as maturity, fees, yield, conversion, or permit support. | Managed through instrument templates as required token features and feature configuration. | | Metadata template | Asset data fields the Asset Designer should collect, including field key, label, type, required status, mutability, and constraints. | Managed through instrument templates and completed during asset creation. | These template types compose with each other. An [instrument template](/docs/user-guides/asset-creation/instrument-templates) can require token behaviors and metadata fields, while a compliance template supplies the policy controls selected during the compliance step. ### Why use templates? [#why-use-templates] * **Reusability:** Apply the same compliance module pattern to multiple assets * **Consistency:** Keep assets in the same category aligned to the same configured controls * **Efficiency:** Configure compliance modules once and reuse them across your portfolio * **DALP library templates:** Start with pre-built templates designed for common compliance patterns ## Template detail page [#template-detail-page] When you open a compliance template, you see the template detail page with all the key information at a glance. ### Inline metadata bar [#inline-metadata-bar] The top of the template detail page displays an inline metadata bar showing: * **Status:** Whether the template is published or in draft mode * **Jurisdiction:** The regulatory jurisdiction the template is designed for (or "Global" for multi-jurisdictional templates) * **Version:** The current version number of the template * **Last updated:** When the template was last modified This information helps you quickly identify the right template for your needs without opening each one. ### Template content [#template-content] Below the metadata bar, the page displays all compliance modules included in the template, organized by category: * **Eligibility:** Modules that control who can hold your assets (identity verification, allow lists, block lists) * **Restrictions:** Geographic and address-based restrictions * **Transfer controls:** Rules governing asset transfers * **Issuance and supply:** Limits on token supply and investor count * **Time-based rules:** Holding periods and time locks * **Settlement and collateral:** Collateral requirements for asset issuance ## Module classification tags [#module-classification-tags] Each compliance module in a template displays classification tags that help you understand its purpose: ### Tag categories [#tag-categories] | Tag | Meaning | | ---------------- | ---------------------------------------------------------------------- | | **Eligibility** | Controls who can hold or receive assets based on identity verification | | **Identity** | Validates investor identity through verified credentials | | **Restrictions** | Limits asset ownership based on geography or specific addresses | | **Security** | Enforces security measures like transfer approvals | | **Limits** | Caps the number of holders or total supply | | **Transfer** | Controls how and when transfers can occur | ### Viewing enforced rules [#viewing-enforced-rules] When you expand a module's information, you can see exactly what rules it enforces. This helps you understand the full impact of each compliance module before enabling it. ## Filtering templates [#filtering-templates] The templates index page offers multiple filters to help you find the right template quickly: ### Search [#search] Type in the search box to find templates by name or description. ### Jurisdiction filter [#jurisdiction-filter] Filter templates by regulatory jurisdiction. Select a specific country or "Global" for templates that work across jurisdictions. ### Source filter [#source-filter] Filter by template source: * **DALP library:** Pre-built templates provided by DALP for common compliance patterns * **Organisation:** Custom templates created by your organisation ### Status filter [#status-filter] Filter by template status: * **Published:** Active templates ready to apply to assets * **Draft:** Templates still being configured ## Creating a new template [#creating-a-new-template] To create a new compliance template: 1. Navigate to **Platform Settings** → **Compliance** → **Templates**. 2. Use the page-level **Version** filter to choose the generation you want to work with. **Current** lists current-generation templates, **Legacy** lists legacy templates, and **All** lists both. 3. Click **Create template** and select: * **From scratch** to build a new policy from the ground up. * **From existing** to copy an existing template. 4. When you create from an existing template, choose a **Source Template**. The source list follows the page-level Version filter. In **All** mode, legacy source rows are marked so you can tell same-named templates apart. 5. Choose the module generation for the template. From-scratch templates start on the current generation by default. Templates copied from an existing template start with the source template's generation, but you can still change the generation before saving. If copied modules or required controls are not available in the generation you select, DALP asks you to confirm and removes the incompatible items from the new template. 6. Configure the compliance modules and required controls you need. 7. Set the template status to **Published** when ready. ### Required controls [#required-controls] Required controls let you define which compliance controls must be present when the template is applied to an asset. Use them when a template should enforce that a control is included, even if the detailed settings are configured later during asset creation or governance. When you select required controls, the selector shows a generation marker next to each control name. Use the marker to distinguish controls with similar names and to select the generation that matches the template you are creating. The marker is display guidance only: the saved template stores the selected raw control IDs, not the badge text. The visible generation marker comes from compliance control metadata. Use the marker in the selector to choose the control generation that matches the template. ## Applying templates to assets [#applying-templates-to-assets] When creating a new asset using the Asset Designer, select a compliance template on the compliance step. All modules from the template are automatically applied to your asset. You can also change the compliance template for existing assets if you have the governance role, though this should be done carefully to avoid disrupting existing holders. ## Managing templates [#managing-templates] ### Editing templates [#editing-templates] Open a template and click **Edit** to modify its configuration. Changes to the template do not automatically affect assets that already use the template. Each asset maintains its own copy of the compliance configuration. ### Publishing and drafts [#publishing-and-drafts] Templates can be in **Draft** or **Published** status: * **Draft** templates are hidden from the template selector during asset creation * **Published** templates are available to apply to new assets This lets you prepare templates in advance without making them available until ready. ## Related [#related] * [Compliance user guides](/docs/user-guides/compliance/overview) * [Configure trusted issuers](/docs/user-guides/compliance/configure-trusted-issuers) # Verify KYC Source: https://docs.settlemint.com/docs/user-guides/compliance/verify-kyc Issue KYC verifications for users to enable asset transactions and compliance. This guide explains how to issue KYC (Know Your Customer) verifications for enrolled users, enabling them to receive assets and participate in compliant transactions. ## Prerequisites [#prerequisites] * **Verification Issuer** role * Must be configured as trusted issuer for KYC topics (see [Configure Trusted Issuers](/docs/user-guides/compliance/configure-trusted-issuers)) * User must be enrolled in identity registry * Completed KYC documentation review ## About KYC verifications [#about-kyc-verifications] KYC verifications are on-chain attestations that confirm: * User identity has been verified * Documentation meets compliance standards * User is eligible for asset transactions * Verification is issued by a trusted entity ### When KYC is required [#when-kyc-is-required] KYC verification is **not automatically required** for all users. It becomes necessary when: **Global requirement:** The platform's Identity verification compliance module is configured to require KYC verification for all asset transactions. This is recommended as a default security measure. **Per-asset requirement:** Individual assets can enable the Identity verification compliance module with KYC as a required verification topic, even if not required globally. **No requirement:** Some use cases may not require KYC verification at all, depending on the asset type and regulatory context. For complete information about verification topics and framework, see [Compliance Overview](/docs/user-guides/compliance/overview). ## Issuing KYC verifications [#issuing-kyc-verifications] ### Access user verifications [#access-user-verifications] Navigate to **Participants** > **Users** and select a user you want to verify. On the user detail page, find the **"Identity and verifications"** tile and click **Manage verifications**. Manage verifications view ### Issue verification [#issue-verification] Click **Issue verification** and select the verification topic you want to issue (KYC, Accredited Investor, AML, or other topics based on your trusted issuer status). ### Complete verification process [#complete-verification-process] Click **Continue** to proceed, review the verification summary, then click **Issue verification**. Enter your PIN when prompted and wait for transaction confirmation. ### Verify completion [#verify-completion] After transaction confirmation, the verification appears in the user's verification list with a **green shield** indicating you were a trusted issuer for this topic. The user can now receive assets requiring this verification. Verified user with KYC verification ## Managing verifications [#managing-verifications] ### View existing verifications [#view-existing-verifications] To see all verifications for a user: 1. Access user profile in Compliance > Users 2. View verification summary on profile 3. Click **Manage verifications** for details ## Best practices [#best-practices] ### Verification standards [#verification-standards] * Follow your written KYC policy consistently * Maintain evidence of verification decisions * Document verification rationale for audit * Use consistent verification data formats ### Data privacy [#data-privacy] * Store minimal personal data on-chain * Use hashes for sensitive information references * Maintain secure off-chain document storage * Follow applicable data protection regulations ### Verification quality [#verification-quality] * Verify document authenticity * Cross-check information sources * Monitor for suspicious patterns * Maintain verification standards ## Integration with compliance [#integration-with-compliance] * Identity verification module validates verifications * Transfer restrictions enforce verification requirements * Compliance reporting tracks verification status ## Troubleshooting [#troubleshooting] | Issue | Solution | | ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | Cannot see Add verification button | • Verify you have Verification Issuer role
• Check you're configured as trusted issuer for KYC topics
• Ensure user is enrolled in registry | | Verification not showing as trusted | • Confirm you're a trusted issuer for the topic
• Verify transaction completed successfully
• Check verification data format | | User still cannot receive assets | • Ensure correct verification topic was issued
• Check verification hasn't expired
• Verify token's specific compliance requirements | | Transaction fails when issuing | • Check wallet has sufficient gas
• Verify your trusted issuer status
• Ensure valid verification data format | ## Related guides [#related-guides] * [Provide KYC Data](/docs/user-guides/user-management/provide-kyc-data) - How users complete their KYC profile * [Manage KYC Data](/docs/user-guides/compliance/manage-kyc-data) - Review and approve KYC submissions before verification * [Register User](/docs/user-guides/user-management/register-user) - Register users before verification * [Configure Trusted Issuers](/docs/user-guides/compliance/configure-trusted-issuers) - Set up verification permissions * [Compliance Overview](/docs/user-guides/compliance/overview) - Complete compliance reference * [Create Asset](/docs/user-guides/asset-creation/create-asset) - Set Asset compliance requirements # Create a feed Source: https://docs.settlemint.com/docs/user-guides/data-feeds/create-feed Register a global or asset-scoped issuer-signed scalar data feed. Once you have a scalar feed topic and a trusted issuer configured, you can register a data feed. Feeds can be **global** (market-wide data) or **asset-scoped** (data tied to a specific token). ## Prerequisites [#prerequisites] * **Feeds manager** role (`feedsManager`) assigned on the System Access Manager * A scalar feed **verification topic** already created (see [Create a topic](/docs/user-guides/data-feeds/create-topic)) * A **trusted issuer** configured for the topic (see [Configure trusted issuers](/docs/user-guides/compliance/configure-trusted-issuers)) * For asset-scoped feeds: the target asset must already be deployed ## Steps [#steps] ### Navigate to Data Feeds [#navigate-to-data-feeds] Go to **Platform Settings** > **Data Feeds** in the sidebar. The page lists all registered feeds. Data Feeds page ### Open the Create Feed form [#open-the-create-feed-form] Click **Create feed** to open the feed creation dialog. Create feed form ### Select the feed scope [#select-the-feed-scope] Choose the scope that matches your use case: * **Global** - For market-wide data (BTC/USD, ETH/USD). The subject address is set to the zero address automatically. * **Asset-scoped** - For data tied to a specific token. A **Subject address** selector appears where you select the target asset. * **Identity-scoped** - For data tied to a specific identity. A **Subject address** selector appears where you select the target identity. For asset-scoped feeds, select the asset from the dropdown. The dropdown lists all deployed assets with their name and contract address. ### Configure feed properties [#configure-feed-properties] Fill in the remaining fields: * **Topic name** - Select a scalar feed topic from the dropdown. Only topics with the `(int256 value)` signature appear. * **Source type** - Choose **Issuer-signed** for EIP-712 signed submissions or **Chainlink** for external oracle integration. * **Decimals** - Set the decimal precision (0-18). For USD prices, 8 decimals is common. For integer metrics, use 0. * **Description** - Describe what the feed measures (e.g., "BTC/USD spot price from major exchanges"). Expand the **Advanced options** section to configure: * **History mode** - Choose how past values are retained: * **Latest only** - Keeps only the most recent value (default) * **Bounded** - Keeps a fixed number of past values (specify **History size**) * **Full** - Keeps all values permanently * **Require positive** - Enable to reject zero or negative values * **Drift allowance** - Maximum allowed time difference in seconds between the submitted `observedAt` timestamp and the block timestamp. Set to `0` to disable. Filled create feed form ### Review and confirm [#review-and-confirm] Click **Continue** to see the configuration summary. Review all properties carefully as they **cannot be changed** after deployment. Click **Register feed** and enter your PIN to authenticate the transaction. Feed creation confirmation ### Verify the feed was created [#verify-the-feed-was-created] After the transaction confirms, the new feed appears in the Data Feeds list. Click on the feed row to view its detail page with the full configuration and latest value. Data Feeds list with created feed Feed detail page ## Creating an asset-scoped feed [#creating-an-asset-scoped-feed] Asset-scoped feeds are tied to a specific deployed token and are used for asset-specific metrics like NAV, interest rates, or risk scores. The process is the same as above, with one additional step: when you select **Asset-scoped** as the scope, a **Subject address** dropdown appears. Select the target asset from the list. The target asset must already be deployed before you can create an asset-scoped feed for it. If you need to create an asset first, see [Create asset](/docs/user-guides/asset-creation/create-asset). ## Feed properties summary [#feed-properties-summary] All properties are immutable after deployment. | Property | Required | Default | Notes | | ---------------- | ------------------------------ | ---------------- | --------------------------------------------- | | Scope | Yes | Global | Determines the subject address | | Subject address | Only for asset/identity scopes | - | Auto-filled for global scope | | Data format | Yes | Numeric (Scalar) | Only scalar is currently supported | | Topic name | Yes | - | Must be a scalar feed topic | | Source type | Yes | Issuer-signed | Chainlink requires an external oracle address | | Decimals | Yes | 18 | Range: 0-18 | | Description | Yes (issuer-signed) | - | Free text describing the feed | | History mode | Yes | Latest only | Controls value retention | | History size | Only for Bounded mode | - | Minimum: 1 | | Require positive | No | Off | Rejects non-positive values | | Drift allowance | No | 0 (disabled) | In seconds | ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------------ | -------------------------------------------------------------------------------------------------- | | Create feed button not visible | Verify you have the **Feeds manager** role on the System Access Manager | | No topics in the dropdown | Create a scalar feed topic first (see [Create a topic](/docs/user-guides/data-feeds/create-topic)) | | Continue button disabled | Ensure all required fields are filled (topic name and description are commonly missed) | | Asset not in subject dropdown | Verify the asset is deployed; check under **Asset management** in the sidebar | | Transaction fails | Check wallet gas balance and PIN/OTP configuration | ## Next steps [#next-steps] After creating a feed, [publish a feed update](/docs/user-guides/data-feeds/publish-feed-update) to submit the first value. # Create a topic Source: https://docs.settlemint.com/docs/user-guides/data-feeds/create-topic Register a scalar feed verification topic to define the data schema for your data feeds. Before creating a data feed, you need a verification topic that defines the data schema. Scalar feed topics use the fixed signature `(int256 value)` and are created on the **Verification Topics & Issuers** page. ## Prerequisites [#prerequisites] * **Verification policy manager** role (`claimPolicyManager`) assigned on the System Access Manager * **Issuer-Signed Scalar Feed** addon installed (see [Install addons](/docs/user-guides/system-addons/install-addons)) ## Topic kinds [#topic-kinds] The topic form distinguishes between general verification topics and feed topics: | Kind | Signature | Purpose | | ---------------- | ----------------------------------------------- | ------------------------------------------------------------------- | | **Verification** | Custom ABI (e.g., `(string)`, `(uint256,bool)`) | General-purpose identity verifications such as KYC or accreditation | | **Scalar feed** | `(int256 value)` (fixed) | Numeric data feeds for prices, rates, and metrics | | **Bytes feed** | Disabled in the topic form | Not selectable for data feed setup | Only **Scalar feed** topics can be used when creating data feeds. The disabled **Bytes feed** option cannot be selected for this workflow. ## Steps [#steps] ### Navigate to verification topics and issuers [#navigate-to-verification-topics-and-issuers] Go to **Platform Settings** > **Verification Topics & Issuers** in the sidebar. This page lists all registered topics and trusted issuers. Verification Topics & Issuers page ### Open the add topic form [#open-the-add-topic-form] Click **Add topic** to open the topic creation dialog. Add topic form ### Select the Scalar feed kind [#select-the-scalar-feed-kind] Choose **Scalar feed** as the topic kind. The signature field automatically locks to `(int256 value)` and cannot be modified. Enter a descriptive **Topic name** that identifies what data this topic represents (e.g., "FEED: BTC/USD", "FEED: Interest Rate"). Scalar feed topic configuration While there is no enforced naming convention, prefixing feed topic names with "FEED:" helps distinguish them from compliance verification topics in dropdown lists. ### Confirm the transaction [#confirm-the-transaction] Click **Continue** to see the summary, then click **Add topic** and enter your PIN to authenticate the on-chain transaction. Topic creation PIN confirmation ### Verify the topic was created [#verify-the-topic-was-created] After the transaction confirms, the new topic appears in the verification topics list. It is now available for use when creating data feeds and when configuring trusted issuers. ## Next steps [#next-steps] After creating a topic: 1. **Add a trusted issuer** for this topic so that an identity can publish feed updates. See [Configure trusted issuers](/docs/user-guides/compliance/configure-trusted-issuers). 2. **Create a feed** that uses this topic. See [Create a feed](/docs/user-guides/data-feeds/create-feed). ## Troubleshooting [#troubleshooting] | Issue | Solution | | -------------------------------- | ------------------------------------------------------------------------------------- | | Add topic button not visible | Verify you have the **Verification policy manager** role on the System Access Manager | | Scalar feed option not available | Ensure the **Issuer-Signed Scalar Feed** addon is installed | | Topic name already exists | Choose a unique name; duplicate topic names are not allowed | | Transaction fails | Check that your wallet has sufficient gas and PIN/OTP is configured | # Data feeds overview Source: https://docs.settlemint.com/docs/user-guides/data-feeds/overview Understand issuer-signed scalar data feeds, their architecture, required permissions, and configuration properties. Data feeds deliver signed, on-chain price and metric data that smart contracts can consume for calculations such as NAV, collateral ratios, and yield distributions. The platform uses **Issuer-Signed Scalar Feeds** where authorized issuers submit EIP-712 signed values through a feed adapter contract. ## Architecture [#architecture] A data feed combines three on-chain concepts: 1. **Verification topic** - Defines the data schema. Scalar feed topics use the fixed signature `(int256 value)`. 2. **Feed contract** - Stores configuration (decimals, history, drift) and holds submitted values. 3. **Trusted issuer** - An identity authorized to publish updates to feeds using a given topic. ```mermaid graph LR A[Issuer] -->|EIP-712 signed update| B[Feed Adapter] B -->|stores value| C[Feed Contract] C -->|read by| D[Smart Contracts] E[Topic Registry] -->|schema| C F[Trusted Issuer Registry] -->|authorization| B ``` ## Required permissions [#required-permissions] Different operations require different platform roles. Roles are assigned on the **System Access Manager** page under **Platform Settings**. | Operation | Required role | UI label | | -------------------------------------------- | -------------------- | --------------------------------------------------------------- | | Create / delete / update verification topics | `claimPolicyManager` | **Verification policy manager** | | Register / replace / remove feeds | `feedsManager` | **Feeds manager** | | Publish feed updates | No role required | Any user, but must be a **trusted issuer** for the feed's topic | Changing roles on the System Access Manager Publishing a feed update does not require a platform role, but the submitting identity must be registered as a **trusted issuer** for the feed's verification topic. See [Configure trusted issuers](/docs/user-guides/compliance/configure-trusted-issuers) for setup instructions. ## Feed scopes [#feed-scopes] Every feed is scoped to a subject address that determines what entity the data describes. | Scope | Subject address | Use case | | ------------------- | ---------------------------- | --------------------------------------------------------------------- | | **Global** | Zero address (`0x000...000`) | Market-wide data such as BTC/USD or ETH/USD exchange rates | | **Asset-scoped** | Token contract address | Asset-specific data such as a bond's NAV or a deposit's interest rate | | **Identity-scoped** | Identity contract address | Entity-specific data such as a credit score or risk rating | ## Exchange-rate target currencies [#exchange-rate-target-currencies] DALP uses exchange-rate feeds to convert portfolio and asset values into the currencies your organization supports for reporting. During organization setup, the base currency must be included in the selected currency list, and at least one selected currency must be different from the base currency. After setup, admins can review enabled currencies from **Platform Settings > Currencies & Exchange Rates**. New supported currencies can be added from that page. Existing currencies cannot be removed, because their feeds are registered on-chain and may already be used by historical valuations, NAV reporting, and portfolio views. When a supported currency is added, DALP creates the corresponding issuer-signed scalar feed for the currency pair. The feed is populated by the exchange-rate refresh cycle after the feed is available to the platform, so a newly added currency may appear before its first refreshed value is ready. Adding target currencies requires a system with feed-directory support. If the active system does not support currency feed creation, use a system that has feed infrastructure enabled before adding currencies. ## Feed properties reference [#feed-properties-reference] These properties are set when creating a feed and cannot be changed after deployment. | Property | Values | Description | | -------------------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Data format** | Numeric (Scalar) | The data type stored by the feed. Currently only scalar (`int256`) is supported. | | **Topic name** | Selected from registered topics | The verification topic that defines the feed's data schema. Only topics with the `(int256 value)` signature are available for scalar feeds. | | **Source type** | Issuer-signed, Chainlink | How values are submitted. Issuer-signed uses EIP-712 signatures; Chainlink reads from an external oracle contract. | | **Decimals** | 0 - 18 | The number of decimal places for the stored value. For example, a USD price with 8 decimals stores `100000000` to represent `1.00`. | | **Description** | Free text | A human-readable description of what the feed measures. Required for issuer-signed feeds. | | **History mode** | Latest only, Bounded, Full | Controls how many past values the feed retains. | | **History size** | Integer >= 1 | Only used with **Bounded** history mode. Sets the maximum number of historical values to keep in a circular buffer. | | **Require positive** | On / Off | When enabled, the feed contract rejects any submitted value that is zero or negative. | | **Drift allowance** | Integer (seconds) | Maximum allowed time difference between the `observedAt` timestamp in a submission and the current block timestamp. Set to `0` to disable drift checking. | ### History modes explained [#history-modes-explained] * **Latest only** - The feed stores only the most recent value. Previous values are overwritten. Best for feeds where only the current price matters. * **Bounded** - The feed keeps a fixed-size circular buffer of past values (set by **History size**). When the buffer is full, the oldest value is replaced. Useful for moving averages or volatility calculations. * **Full** - The feed stores every submitted value permanently. Provides a complete audit trail but uses more on-chain storage. Data feeds power real-time asset analytics and pricing ## Setup workflow [#setup-workflow] Setting up a data feed involves four steps: ### Create a scalar feed topic [#create-a-scalar-feed-topic] Register a verification topic with the `feedScalar` kind. This defines the data schema for your feeds. See [Create a topic](/docs/user-guides/data-feeds/create-topic). ### Register a trusted issuer [#register-a-trusted-issuer] Add the identity that will publish feed updates as a trusted issuer for the topic. See [Configure trusted issuers](/docs/user-guides/compliance/configure-trusted-issuers). ### Create the feed [#create-the-feed] Register a new feed contract with the desired scope, topic, and configuration properties. See [Create a feed](/docs/user-guides/data-feeds/create-feed). ### Publish updates [#publish-updates] Submit signed values to the feed. See [Publish a feed update](/docs/user-guides/data-feeds/publish-feed-update). ## Related guides [#related-guides] * [Create a topic](/docs/user-guides/data-feeds/create-topic) - Register a scalar feed verification topic * [Create a feed](/docs/user-guides/data-feeds/create-feed) - Register a global or asset-scoped feed * [Publish a feed update](/docs/user-guides/data-feeds/publish-feed-update) - Submit a signed value * [Configure trusted issuers](/docs/user-guides/compliance/configure-trusted-issuers) - Authorize issuers for topics * [Install addons](/docs/user-guides/system-addons/install-addons) - Install the Issuer-Signed Scalar Feed addon # Publish a feed update Source: https://docs.settlemint.com/docs/user-guides/data-feeds/publish-feed-update Submit a signed value to an issuer-signed scalar data feed. Publishing a feed update submits a new signed value to an existing data feed. The value is signed using EIP-712 and stored on-chain by the feed adapter contract. ## Prerequisites [#prerequisites] * A registered **data feed** exists (see [Create a feed](/docs/user-guides/data-feeds/create-feed)) * Your identity is registered as a **trusted issuer** for the feed's verification topic (see [Configure trusted issuers](/docs/user-guides/compliance/configure-trusted-issuers)) Publishing does not require a specific platform role, but your on-chain identity must be registered as a trusted issuer for the feed's topic. Without this, the feed adapter contract will reject the submission. ## Registering as a trusted issuer [#registering-as-a-trusted-issuer] Before you can publish, ensure the publishing identity is set up as a trusted issuer: ### Navigate to Verification Topics & Issuers [#navigate-to-verification-topics--issuers] Go to **Platform Settings** > **Verification Topics & Issuers**. Trusted Issuers section ### Add the publishing identity [#add-the-publishing-identity] Click **Add trusted issuer**, select the identity that will publish feed updates, and assign the scalar feed topic used by the feed. Add trusted issuer dialog For detailed instructions, see [Configure trusted issuers](/docs/user-guides/compliance/configure-trusted-issuers). ## Publishing a value [#publishing-a-value] ### Navigate to the feed [#navigate-to-the-feed] Go to **Platform Settings** > **Data Feeds** and find the feed you want to update. ### Open the publish action [#open-the-publish-action] You can publish from two places: * **From the table** - Click the three-dot menu (**...**) on the feed's row and select **Publish update**. * **From the feed detail page** - Open a feed, then click the three-dot menu in the top-right corner and select **Publish update**. If you are not a trusted issuer for the feed's topic, the publish option appears disabled with a tooltip explaining the requirement. Feed row actions menu ### Enter the feed value [#enter-the-feed-value] Fill in the publish form: * **Value** - The numeric value to submit. Enter the value in its human-readable form (e.g., `67500` for $67,500.00). The platform automatically encodes it using the feed's decimal precision before submitting. The observation timestamp is automatically set to the current time at submission. Publish feed update form The value is stored as an `int256` on-chain. The platform handles decimal encoding automatically: a value of `1.50` on a feed with 8 decimals is stored as `150000000` (`1.50 * 10^8`). ### Confirm the transaction [#confirm-the-transaction] Click **Continue** to review, then click **Submit** and enter your PIN to sign and submit the EIP-712 transaction. Publish confirmation ### Verify the update [#verify-the-update] After the transaction confirms, the feed's latest value is updated. You can verify by: * Checking the **Latest value** column on the Data Feeds list page * Opening the feed detail page to see the full value history (if history mode is Bounded or Full) ## Understanding feed validation [#understanding-feed-validation] The feed adapter contract validates each submission against the feed's configuration: | Validation | Triggered when | Rejection reason | | -------------------------- | ---------------------------- | --------------------------------------------------------------------- | | **Trusted issuer check** | Always | Submitting identity is not a trusted issuer for the feed's topic | | **Positive value check** | `requirePositive` is enabled | Submitted value is zero or negative | | **Drift check** | `driftAllowance` > 0 | Difference between `observedAt` and block timestamp exceeds allowance | | **Signature verification** | Always | EIP-712 signature is invalid or signer does not match | ## Troubleshooting [#troubleshooting] | Issue | Solution | | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | Publish option not visible in row menu | You may not be a trusted issuer for the feed's topic. Check the trusted issuers list. | | Transaction reverts | Verify: (1) identity is trusted issuer for topic, (2) value meets `requirePositive` constraint, (3) `observedAt` is within drift allowance | | Value appears incorrect on detail page | Check decimal encoding. The displayed value applies the feed's decimal precision. | | "Not a trusted issuer" error | Navigate to **Verification Topics & Issuers** and add your identity as a trusted issuer for the feed's topic | ## Related guides [#related-guides] * [Data feeds overview](/docs/user-guides/data-feeds/overview) - Architecture and property reference * [Create a feed](/docs/user-guides/data-feeds/create-feed) - Register a new feed * [Configure trusted issuers](/docs/user-guides/compliance/configure-trusted-issuers) - Authorize issuers for topics # Introduction Source: https://docs.settlemint.com/docs/user-guides/introduction Step-by-step instructions for platform administrators and operations teams running regulated digital asset operations after launch. This section provides step-by-step instructions for platform administrators and operations teams running regulated digital assets after launch. Each guide documents a specific operational task with prerequisites, detailed procedures, expected outcomes, and the controls needed to keep approvals, compliance, settlement, servicing, and evidence inside the DALP operating model. DALP platform dashboard — your central hub for asset management Looking to automate these workflows programmatically? See the [Developer guides](/docs/developer-guides) for API integration and type-safe client usage. ## Platform setup [#platform-setup] Complete reference for platform setup, administrator roles, and system configuration. Initialize your platform by creating the first administrator account. Grant administrative permissions to users in your organization. Modify the roles and permissions of existing platform administrators. ## User management [#user-management] Understanding the new user experience when joining the platform. Send invitations for team members and external partners to join your platform. Directly create user accounts with automatic wallet and identity setup. Register users in the identity registry for compliance tracking and token eligibility. ## Asset creation [#asset-creation] Deploy a new tokenized asset using the Asset Designer wizard. ## Asset servicing [#asset-servicing] Issue new units of a security to investor accounts. Permanently remove units of a security from circulation. Execute custodial transfers without holder consent for regulatory compliance. Control asset operations by pausing or unpausing to manage transfers. Modify administrator roles and permissions for specific assets. ## Compliance [#compliance] Learn about compliance modules and how to configure them during asset creation. Configure trusted entities who can issue verifications for compliance. Issue KYC verifications for users to enable asset transactions. Configure collateral backing requirements for assets. ## System addons [#system-addons] Learn about system addons that extend asset functionality. Atomic settlements for secure multi-party and cross-chain asset exchanges. Automated yield distribution for bonds and other yield-bearing assets. ## AI assistants [#ai-assistants] Use Claude, Codex, or OpenClaw to manage your platform through natural language conversations. ## Runbooks [#runbooks] Complete walkthrough for setting up a multi-organization demo environment. Complete workflow to register a user and issue KYC verification. # Account Abstraction Control Center Source: https://docs.settlemint.com/docs/user-guides/platform-setup/account-abstraction-control-center Use the Account Abstraction Control Center to review and manage bundler and paymaster infrastructure from Platform settings. The Account Abstraction Control Center is available at **Platform settings > Infrastructure > Account Abstraction**. You can also open it directly at `/platform-settings/account-abstraction`. The control center shows the Account Abstraction setting, bundler wallet details, paymaster details, and recent on-chain activity for the configured infrastructure addresses. Use it when you need to check or manage the infrastructure behind sponsored and bundled transactions. ## Access [#access] The Account Abstraction page appears when Account Abstraction is available for the platform. The Platform settings navigation shows the Account Abstraction entry to users with one of these roles: * Admin * System manager * Gas manager * Auditor Role access works as follows: * Admin and System manager users can manage the Account Abstraction setting and signer key rotation. * Admin, System manager, and Gas manager users can manage paymaster enablement. * System manager users can install the paymaster from the platform addons flow. * Gas manager users can manage gas operations, including paymaster funding. * Auditor users can open the page for review access. ## Account Abstraction setting [#account-abstraction-setting] The top control shows whether Account Abstraction is enabled for the platform. Admin and System manager users can change the setting after confirming the update. The control becomes available when the bundler wallet is configured. ## Bundler [#bundler] The bundler card shows: * The bundler wallet address * The current native token balance * The last balance update time * A fund action that displays the bundler wallet address * A link to view the bundler wallet in the configured block explorer When the bundler wallet is not configured, the card shows the not-configured state. ## Paymaster [#paymaster] When a paymaster is installed, the paymaster card shows: * The paymaster contract address * The EntryPoint deposit balance * The native token balance, when the balance is available to your role * The signer key address * The date the signer key was set * A paymaster enablement control with confirmation for Admin, System manager, and Gas manager users * A fund action for users who can manage gas * A signer key rotation action for Admin and System manager users * A link to view the paymaster contract in the configured block explorer Signer key rotation may require wallet verification before DALP applies the change. When a paymaster is not installed, the control center shows an install prompt. Users who can manage Account Abstraction can open the platform addons page. Completing the installation requires the addon-create permission described in [Install addons](/docs/user-guides/system-addons/install-addons). During organization setup, DALP installs the paymaster when the active network supports the paymaster add-on. When setup resumes after a partial installation, DALP reuses the existing paymaster and reconciles its signer key instead of creating another paymaster. ## Activity [#activity] The activity log combines recent events from the configured bundler and paymaster addresses. Each row identifies whether the event came from the bundler or the paymaster. The log is sorted with the newest events first. # Add administrators Source: https://docs.settlemint.com/docs/user-guides/platform-setup/add-admins Grant administrative permissions to users in your organization. Administrators have elevated permissions to manage platform operations. This guide explains how to grant administrative roles to any wallet address. ## Prerequisites [#prerequisites] * **Permission manager** role (required to grant platform administrator roles) * Target wallet address (can be any valid address) ## Available platform roles [#available-platform-roles] Platform administrator roles control system-wide operations. For complete role descriptions, permissions, and best practices, see [Platform Setup Overview](/docs/user-guides/platform-setup/platform-overview). Platform administrator roles control system-wide operations. Asset-specific roles (Asset Operator, Custodian, Supply Management, Emergency) are assigned per token during asset creation. ## Steps to add administrators [#steps-to-add-administrators] ### Navigate to platform admins [#navigate-to-platform-admins] Go to **Platform Settings** > **Platform Admins** in the sidebar. You'll see a table of all current platform administrators with their assigned roles. Platform Admins table ### Add new administrator [#add-new-administrator] Click the **Add admin** button to start adding a new platform administrator. This opens the admin creation flow. ### Choose recipient [#choose-recipient] Select the recipient you want to make an administrator using the recipient selector. You can choose from existing users in your organization or enter any valid wallet address manually. Recipient selector interface ### Select roles [#select-roles] Choose which platform administrator roles to grant. Review available roles and their permissions, select appropriate roles for the user's responsibilities, and consider the principle of least privilege. Role selection interface ### Review and save [#review-and-save] Click **Continue** to see the summary, then click **Save** to create the administrator. Enter your PIN when prompted—this authenticates the on-chain transaction that grants the permissions. ### Verify administrator creation [#verify-administrator-creation] After the transaction confirms, the new administrator appears in the Platform Admins table and immediately gains the assigned permissions. The user may need to refresh their session to see admin functions. ## Best practices [#best-practices] ### Role assignment principles [#role-assignment-principles] * **Least privilege** - Grant only necessary permissions * **Separation of duties** - Divide critical functions * **Regular review** - Audit role assignments periodically * **Document decisions** - Record why roles were granted ## Removing administrative roles [#removing-administrative-roles] To remove or modify roles for existing administrators, see [Change Admin Roles](/docs/user-guides/platform-setup/change-admin-roles). Removing roles immediately revokes permissions. Ensure the user has completed any pending administrative tasks first. ## Role permissions reference [#role-permissions-reference] For detailed permissions and capabilities of each role, see [Platform Setup Overview](/docs/user-guides/platform-setup/platform-overview). ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | Cannot see Add admin button | • Verify you have Permission manager role
• Check you're in Platform Settings > Platform Admins
• Ensure platform is properly initialized | | Transaction fails when saving | • Check wallet has sufficient gas
• Verify you still have admin permissions
• Ensure contract addresses are correct | | User cannot see new permissions | • Ask user to log out and back in
• Clear browser cache if needed
• Verify transaction was confirmed | ## Related guides [#related-guides] * [Platform Setup Overview](/docs/user-guides/platform-setup/platform-overview) - Complete setup and role reference * [Change Admin Roles](/docs/user-guides/platform-setup/change-admin-roles) - Modify existing assignments * [Invite Users](/docs/user-guides/user-management/invite-users) - Add new team members * [First Admin Setup](/docs/user-guides/platform-setup/first-admin-setup) - Initial platform setup # Change admin roles Source: https://docs.settlemint.com/docs/user-guides/platform-setup/change-admin-roles Modify the roles and permissions of existing platform administrators. Platform administrators may need their roles updated as responsibilities change or new features are added. This guide explains how to modify the roles of existing administrators through the platform settings. ## Prerequisites [#prerequisites] * **Permission manager** role (required to modify other administrators' roles) * Target administrator must already be a platform admin * Understanding of role implications and dependencies ## When to change admin roles [#when-to-change-admin-roles] ### Common scenarios [#common-scenarios] * **Initial setup** - First admin needs additional roles after platform initialization * **Role expansion** - Admin taking on new responsibilities * **Role reduction** - Removing unnecessary permissions following security best practices * **Temporary delegation** - Granting time-limited permissions * **Organizational changes** - Restructuring administrative responsibilities ### Security considerations [#security-considerations] * Follow principle of least privilege * Remove roles when no longer needed * Document role changes for audit purposes * Coordinate with affected administrators ## Steps to change roles [#steps-to-change-roles] ### Access platform admin settings [#access-platform-admin-settings] Navigate to **Platform Settings** > **Platform Admins** in the sidebar. You'll see a table of all platform administrators showing their name, currently assigned roles, last active time, and an actions menu. Platform admins table ### Select administrator [#select-administrator] Find the administrator whose roles you want to modify. Click the **three dots (⋯)** in the **Actions** column to open the action menu, then select **"Change roles"**. Admin table row with actions menu ### Modify role assignments [#modify-role-assignments] In the role management modal, toggle roles on or off as needed. To add roles, check the boxes for new roles. To remove roles, uncheck the boxes. Consider dependencies between roles and coordinate with the administrator if removing permissions. ### Save changes [#save-changes] Click **"Save changes"** to apply the modifications. Enter your PIN when prompted to authenticate the on-chain transaction. All role assignments are stored on-chain, so you'll need to authenticate with your PIN for each permission change. ### Verify role update [#verify-role-update] After the transaction confirms, the administrator's role list updates in the table and changes take effect immediately. The administrator may need to refresh their session to see the updated permissions. ## Available platform roles [#available-platform-roles] For complete role descriptions, permissions, and best practices, see [Platform Setup Overview](/docs/user-guides/platform-setup/platform-overview). ## Best practices [#best-practices] ### Role assignment principles [#role-assignment-principles] * **Separation of duties** - Divide critical functions among multiple admins * **Principle of least privilege** - Grant only necessary permissions * **Regular review** - Audit and update roles periodically * **Documentation** - Record role changes and rationale ### Security considerations [#security-considerations-1] * **Role rotation** - Periodically review and update assignments * **Emergency planning** - Ensure critical roles have backup coverage * **Access monitoring** - Track administrative actions * **Coordination** - Communicate role changes to affected administrators ### Operational efficiency [#operational-efficiency] * **Clear responsibilities** - Define what each role can and should do * **Training** - Ensure administrators understand their permissions * **Escalation paths** - Define how to request additional permissions * **Backup procedures** - Plan for administrator unavailability ## Role dependencies and conflicts [#role-dependencies-and-conflicts] * **Compliance Manager** + **Verification Policy Manager** - Complete compliance setup * **Asset Manager** - Full token lifecycle management * **Identity Manager** + **Verification issuer** - Complete user onboarding ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | Cannot see Platform Settings menu | • Verify you have Platform Admin role
• Check you're logged into the correct organization
• Ensure platform is properly initialized | | Change Roles option not available | • Confirm target user is already a platform admin
• Verify you have permission to modify roles
• Check the administrator's current status | | Transaction fails when saving | • Ensure wallet has sufficient gas
• Verify your PIN/OTP is correct
• Check network connectivity
• Try the operation again | | Roles not updating after transaction | • Wait for blockchain confirmation
• Refresh the page
• Ask administrator to log out and back in
• Check transaction was actually confirmed | ## Related guides [#related-guides] * [Platform Setup Overview](/docs/user-guides/platform-setup/platform-overview) - Complete setup reference * [Add Administrators](/docs/user-guides/platform-setup/add-admins) - Making users administrators * [First Admin Setup](/docs/user-guides/platform-setup/first-admin-setup) - Initial platform setup # First administrator setup Source: https://docs.settlemint.com/docs/user-guides/platform-setup/first-admin-setup Initialize your platform by creating the first administrator account and setting up system infrastructure. The first administrator is responsible for initializing the entire platform. This one-time process creates your organization, sets up system infrastructure, and establishes the foundation for all future operations. ## Prerequisites [#prerequisites] * Access to the platform URL * For public chains: ETH or native tokens for gas fees (will be needed for contract deployment) * Email address for administrator account ## Process overview [#process-overview] The first admin setup involves: 1. Creating your account and organization 2. Setting up a blockchain wallet with security 3. Creating your on-chain identity 4. Initializing system infrastructure 5. Configuring platform basics Only the first user can initialize the system. This sets up all core platform infrastructure including identity management, access control, and compliance capabilities. ## Steps [#steps] ### Create administrator account [#create-administrator-account] Navigate to your platform and click **Sign up**. Enter your email address and choose a strong password, then click **Create account**. Sign-up form ### Name your organization [#name-your-organization] Enter your organization name. This is the primary entity operating the platform, such as "Financial Institution S.A." or "Digital Securities Ltd." Click **Continue** after entering the name. ### Create blockchain wallet [#create-blockchain-wallet] Click **Create my wallet** to generate a new externally owned account (EOA). The system generates a unique wallet address and creates secure key storage. Take note of your wallet address for future reference. Wallet creation screen ### Secure with PIN [#secure-with-pin] Choose **PIN** as your security method. Enter a 6-digit PIN code, confirm it, and click **Continue**. This PIN is required for every blockchain transaction. Store it securely and never share it with anyone. ### Save backup codes [#save-backup-codes] The system generates recovery codes for wallet access. Click **Copy all** or **Download** to save the codes, store them securely offline, check the confirmation box, and click **Continue**. These codes are your only recovery method if you forget your PIN. Without them, you may permanently lose wallet access. Backup codes display ### Create on-chain identity [#create-on-chain-identity] Click **Create my on-chain ID** and enter your PIN when prompted. This deploys a smart contract that holds your compliance verifications, enables you to receive assets, and links to your wallet address. ### Complete profile (optional) [#complete-profile-optional] Add personal information (name, title, department) and click **Continue**. ### Initialize the system [#initialize-the-system] As the first user, you must initialize the platform for your organization. Click **Initialize system** and enter your PIN when prompted. This sets up your organization's platform infrastructure: * **Identity Registry** - Manages all user identities * **Access Manager** - Controls role-based permissions * **System Registries** - Manages platform components * **Compliance Framework** - Verification and rules engine Wait for initialization to complete (may take several minutes on public chains). This creates a dedicated copy of the platform infrastructure for your organization, allowing you to customize implementations and settings independently. Return to the setup flow with the same administrator account. If your organization was created but setup did not complete, DALP keeps the organization name locked, asks you to choose the base currency again, and resumes deployment through the same setup flow. Completed setup steps are reused on retry, so do not create a second administrator account or start a separate organization unless your operator tells you to abandon the original setup. After setup completes, the flow sends you to the dashboard instead of offering another retry. System initialization progress ### Configure base currency [#configure-base-currency] Select your platform's primary currency for pricing and accounting from the dropdown (EUR, USD, GBP, CHF, or others). Click **Save and continue**. ### Enable asset types [#enable-asset-types] Select which asset types your platform will support by checking the relevant boxes: Equity, Bond, Fund, Stablecoin, Deposit, and/or Real Estate. Click **Enable asset types** and enter your PIN. This enables only the asset types you select now. You can enable additional asset types later under Platform Settings if you want to expand your platform capabilities. Asset type selection ## Post-setup tasks [#post-setup-tasks] After completing the first admin setup: 1. **Grant yourself additional roles** - See [Add Administrators](/docs/user-guides/platform-setup/add-admins) 2. **Invite team members** - See [Invite Users](/docs/user-guides/user-management/invite-users) 3. **Configure compliance** - See [Configure Trusted Issuers](/docs/user-guides/compliance/configure-trusted-issuers) ## Troubleshooting [#troubleshooting] | Issue | Solution | | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | Identity creation fails | • Ensure wallet has sufficient gas funds
• Check PIN was entered correctly
• Verify network connectivity | | Transaction fails during initialization | • Ensure wallet has sufficient gas funds
• Check network connectivity
• Verify you're the first user | | Cannot proceed past currency selection | • Confirm all previous transactions completed
• Check browser console for errors
• Try refreshing after transactions confirm | ## Next steps [#next-steps] * [Add administrative roles](/docs/user-guides/platform-setup/add-admins) to your account * [Invite team members](/docs/user-guides/user-management/invite-users) to join the platform * Begin [creating assets](/docs/user-guides/asset-creation/create-asset) or explore [runbooks](/docs/user-guides/runbooks/equity-tokenization) # Platform setup overview Source: https://docs.settlemint.com/docs/user-guides/platform-setup/platform-overview Complete reference for platform setup, administrator roles, and operating controls for regulated digital assets. This overview covers platform setup, administrator roles, and the controls needed to run regulated digital asset operations. Use it as a reference when setting up the platform, assigning responsibilities, and preparing teams to operate assets after launch. Platform overview after initial configuration ## Setup workflow [#setup-workflow] The platform setup follows this sequence: 1. **[First administrator setup](/docs/user-guides/platform-setup/first-admin-setup)** - Initialize platform and create first admin 2. **[Add administrators](/docs/user-guides/platform-setup/add-admins)** - Grant roles to team members 3. **[Change admin roles](/docs/user-guides/platform-setup/change-admin-roles)** - Modify permissions as needed 4. **Configure compliance** - Set up verification framework and policies 5. **Create assets** - Begin tokenizing real-world assets ## Key concepts [#key-concepts] ### Platform vs asset administration [#platform-vs-asset-administration] * **Platform administrators** - Manage system-wide operations and configuration * **Asset administrators** - Control individual token operations (assigned per asset) * **Platform roles** - Apply across the entire platform * **Asset roles** - Specific to individual tokens ### Permission model [#permission-model] * **On-chain permissions** - All roles stored in smart contracts * **Principle of least privilege** - Grant minimum necessary permissions * **Separation of duties** - Divide critical functions among multiple admins * **Immediate effect** - Role changes take effect instantly ### Component management filters [#component-management-filters] Platform settings separates deployable components into instruments and add-ons. Each page includes search, filters, and status grouping so operators can find the right component before taking action. * **Instruments** - Use search and category filters for fixed income, flexible income, cash equivalent, and real-world asset instruments. The page groups instrument types by enabled, registered, and unregistered status. * **Add-ons** - Use search, system add-on type filters for add-ons, token features, and feeds, plus functional category filters for distribution, income and fees, exchange, data, and infrastructure add-ons. The page groups visible add-ons by enabled, registered, and unregistered status. This overview covers **platform administrator roles** only. Asset-specific roles (like Asset Operator, Custodian, Supply Management) are assigned per token and covered in asset creation guides. ## Available platform roles [#available-platform-roles] | Role | Description | Key Permissions | | ------------------------------- | ----------------------------- | -------------------------------------------------- | | **Permission manager** | Permission management | Manage other administrators' roles and permissions | | **System manager** | System configuration | Deploy contracts, manage system settings | | **Asset manager** | Asset creation and management | Create tokens, configure asset properties | | **Identity manager** | User administration | Invite users, manage user identities | | **Verification issuer** | Issue verifications | Create and manage verification records | | **Verification policy manager** | Trust framework configuration | Configure trusted issuers and verification topics | | **Compliance manager** | Global compliance rules | Set platform-wide compliance policies | | **Addon manager** | Platform extension management | Install and configure platform addons | ## Setup guides [#setup-guides] ### Platform initialization [#platform-initialization] * **[First Admin Setup](/docs/user-guides/platform-setup/first-admin-setup)** - Create first administrator and initialize platform infrastructure ### Administrator management [#administrator-management] * **[Add Administrators](/docs/user-guides/platform-setup/add-admins)** - Grant platform roles to team members * **[Change Admin Roles](/docs/user-guides/platform-setup/change-admin-roles)** - Modify existing role assignments # Actions work queue Source: https://docs.settlemint.com/docs/user-guides/runbooks/actions-work-queue Review upcoming, pending, and completed actions from the DALP Actions page. The **Actions** page is the operator work queue for tasks that need attention. Open it from the sidebar at **Actions** or go directly to `/actions`. Use this page to review actions you can execute, actions scheduled for later, and completed or expired history. The page groups the same action dataset into **Pending**, **Upcoming**, and **Completed** tabs. It refreshes the list while you keep it open. ## What appears in Actions [#what-appears-in-actions] The Actions queue combines on-chain and account-level tasks that are relevant to your organization and user account. Current action types include: | Type | What it covers | | ---------- | --------------------------------------------------------------------------------- | | Bond | Bond maturity, redemption, and yield-claim actions | | Settlement | XvP settlement approval and execution actions | | KYC | Account-level KYC update requests | | Action | Other action records that can be shown but are not yet executable from the portal | Each row shows the action name, target address when there is one, executor count when available, status, schedule time, and expiry time when applicable. ## Access and execution permissions [#access-and-execution-permissions] Users must be signed in to the onboarded DALP portal and have access to the organization system to view the Actions queue. The API scopes listed actions to that active system. Execution also depends on the action itself: * The action must be **Pending**. * The action type must be supported by the portal. * On-chain actions need a valid target address. * Rows show executor count when the action source provides executor addresses. Execution can still be rejected by wallet verification, on-chain checks, or action-specific validation. * Account-level KYC update requests are off-chain actions and open the KYC update form instead of an on-chain executor flow. If an action cannot be executed from the portal, the row shows **Unavailable**. ## Status tabs [#status-tabs] | Tab | Statuses shown | How to use it | | ------------- | --------------------- | ------------------------------------------------------------------------------------------------ | | **Pending** | Pending | Work that is active now. Supported pending actions show an **Execute** button. | | **Upcoming** | Upcoming | Work that is scheduled for a future activation time. | | **Completed** | Completed and expired | Operational history. Completed rows include completion time and executor address when available. | Expired actions are shown with completed history because they no longer require action. They remain useful for audit and operational follow-up. ## Execute a pending action [#execute-a-pending-action] ### Open the Actions page [#open-the-actions-page] Go to **Actions** in the sidebar. The page opens with the **Pending** tab selected. ### Review the row [#review-the-row] Check the action name, type, target address, executor count when shown, schedule time, and expiry time before acting. For KYC update requests, the row is shown as an account action because it does not target an on-chain contract address. ### Execute the action [#execute-the-action] Click **Execute** on a supported pending action. The portal opens the action-specific flow: * Bond maturity confirmation for maturity actions * Redemption for redeem actions * Yield claim for claim-yield actions * KYC update form for KYC requests * XvP approval or execution flow for settlement actions If the action type cannot be executed from the portal yet, the row shows **Unavailable** instead of an executable action. ### Recheck the queue [#recheck-the-queue] After execution, return to the Actions page. The queue refreshes automatically, and the action moves out of **Pending** when the underlying operation is complete. ## Search, filter, and export [#search-filter-and-export] The table includes search, filters, column controls, pagination, and CSV export. Use these controls to narrow the queue by action name, type, date fields, target, or executor fields available in the table. ## Token-specific actions [#token-specific-actions] Some token detail pages also show the Actions component scoped to that token. Token-scoped views show actions whose target matches the token address for that page. Account-level KYC actions only appear in the main Actions queue. KYC actions do not have an on-chain target address. ## Related guides [#related-guides] Review and approve settlement flows. Execute settlement flows when the settlement is ready. Complete or update requested KYC information. # Equity tokenization Source: https://docs.settlemint.com/docs/user-guides/runbooks/equity-tokenization Complete walkthrough for setting up a multi-organization demo environment with ACME Holdings equity tokenization. This comprehensive example demonstrates tokenizing equity shares in a real-world scenario with multiple organizations, proper compliance workflows, and complete user management. Follow this guide to set up a fully functional demo environment. ## Prerequisites [#prerequisites] Before starting this example, ensure your blockchain network has the core platform contracts deployed: | Contract | Purpose | Deployed by | | ------------------------- | ------------------------------------------------------------------------------ | ----------------- | | **DALP Directory** | Registry and discovery service for all platform instances and their components | Platform deployer | | **DALP Identity Factory** | Creates on-chain identity contracts for users | Platform deployer | | **DALP System Factory** | Deploys organization-specific system contracts | Platform deployer | These contracts are typically deployed during initial platform setup by your implementation team. They provide the foundation for all organizations to build upon. **Additional requirements:** * Access to the platform web interface * For public chains: ETH or native tokens for transaction gas fees (see gas requirements below) * Email addresses for all demo participants **Gas requirements for public chains:** * **Daniel Admin**: Moderate amount for system deployment and permission management * **Clara Compliance**: Small amount for user registration and verification issuance * **Olivia Operator**: Moderate amount for asset creation, permissions, minting, and transfers * **Colin Collateral**: Small amount for issuing collateral verifications * **Invited users**: Small amount for identity creation (if using invitation flow) Begin equity tokenization in the Asset Designer ## Scenario Overview [#scenario-overview] This demonstration tokenizes 100,000 shares of ACME Holdings S.A., a Luxembourg company, using a multi-organization platform operated by Digital Securities S.A. ### Organizations [#organizations] | Organization | Role | Domain | Jurisdiction | | ------------------------------------ | ----------------------- | --------------------------- | ------------ | | **Digital Securities S.A.** | Platform operator | digital-securities.example | Luxembourg | | **ACME Holdings S.A.** | Issuer (equity owner) | acme-holdings.example | Luxembourg | | **Guardian Collateral Services Ltd** | Collateral agent | guardian-collateral.example | (generic) | | **Exchange Ltd** | Exchange/vault provider | exchange.example | (generic) | ### Key Personas [#key-personas] **Platform Operators (Digital Securities S.A.)** | Persona | Name | Email | Purpose | | ------------------ | ---------------- | --------------------------------------------------------------------------------------------- | ------------------------------ | | Platform Admin | Daniel Admin | [admin@digital-securities.example](mailto:admin@digital-securities.example) | System setup and configuration | | Asset Operator | Olivia Operator | [operator@digital-securities.example](mailto:operator@digital-securities.example) | Asset creation and management | | Compliance Officer | Clara Compliance | [compliance-kyc@digital-securities.example](mailto:compliance-kyc@digital-securities.example) | KYC and regulatory compliance | **External Partners** | Organization | Persona | Name | Email | Purpose | | ---------------------------- | --------------------- | ---------------- | --------------------------------------------------------------------------------------------------- | ----------------------- | | ACME Holdings S.A. | Issuer Representative | Ian Issuer | [issuer@acme-holdings.example](mailto:issuer@acme-holdings.example) | Legal equity owner | | Guardian Collateral Services | Collateral Agent | Colin Collateral | [collateral-agent@guardian-collateral.example](mailto:collateral-agent@guardian-collateral.example) | Collateral verification | | Exchange Ltd | Vault Provider | NovaX Vault | [vault@exchange.example](mailto:vault@exchange.example) | Token custody | ### ACME Equity Token Specifications [#acme-equity-token-specifications] **Real-world instrument:** | Property | Value | | ---------------------- | ------------------------------- | | **Issuer** | ACME Holdings S.A. (Luxembourg) | | **Security type** | Ordinary shares | | **Total shares** | 100,000 | | **Reference currency** | EUR | | **Price per share** | €0.71 | **On-chain representation:** | Property | Value | | ------------------------ | ------------------------------------ | | **Token name** | ACME Holdings Equity | | **Symbol** | ACME | | **Decimals** | 0 (1 token = 1 share) | | **Maximum supply** | 100,000 | | **Asset class** | EQUITY | | **ISIN** | LU0000ACME01 | | **Initial distribution** | 80,000 to issuer, 20,000 to exchange | ## Implementation steps [#implementation-steps] ### Platform initialization [#platform-initialization] **As the first user (Daniel Admin)**, start by setting up the platform administrator who will initialize the entire system. **Follow the [First Administrator Setup](/docs/user-guides/platform-setup/first-admin-setup) guide with these specific values:** * **Email**: [admin@digital-securities.example](mailto:admin@digital-securities.example) * **Organization name**: Digital Securities S.A. * **Base currency**: EUR * **Asset factories to enable**: Equity, ... (add whatever asset types you want to support) For public chains: Ensure Daniel's wallet has sufficient native tokens (ETH, MATIC, etc.) for system deployment transactions before proceeding. As the first user, Daniel Admin will deploy all system contracts including identity registry, access manager, and asset factories. ### Grant additional administrative permissions [#grant-additional-administrative-permissions] **Continue as Daniel Admin.** After platform setup, Daniel Admin has the minimum required roles (Permission manager + System Manager) following the principle of least privilege. For this demo setup, he needs additional roles to configure the system. **Use the [Change Admin Roles](/docs/user-guides/platform-setup/change-admin-roles) guide to add these roles to Daniel Admin:** * **Add-on Manager**: Manage platform extensions and modules * **Identity Manager**: Required to invite other users Access Platform Settings > Platform Admins, find Daniel Admin, click the three dots, select "Change roles", and add the roles listed above. ### Invite platform team [#invite-platform-team] **Continue as Daniel Admin.** Add the other platform operators and external partners who will participate in this demo. **You can use either the [Invite users](/docs/user-guides/user-management/invite-users) guide or the [Create users](/docs/user-guides/user-management/create-users) guide:** * **Invite users**: Send invitations for users to onboard themselves (more realistic) * **Create users**: Directly create accounts with random passwords for faster demo setup or if users don't need to access the platform directly **Platform operators:** 1. **Olivia Operator** ([operator@digital-securities.example](mailto:operator@digital-securities.example)) * **Responsibilities**: Creates tokens, manages corporate actions, handles minting/burning 2. **Clara Compliance** ([compliance-kyc@digital-securities.example](mailto:compliance-kyc@digital-securities.example)) * **Responsibilities**: Manages KYC/AML verification, registers identities, issues verifications **External organizations:** 3. **Ian Issuer** ([issuer@acme-holdings.example](mailto:issuer@acme-holdings.example)) * **Responsibilities**: Legal owner of equity being tokenized 4. **Colin Collateral** ([collateral-agent@guardian-collateral.example](mailto:collateral-agent@guardian-collateral.example)) * **Responsibilities**: Independent verification of collateral backing 5. **NovaX Vault** ([vault@exchange.example](mailto:vault@exchange.example)) * **Responsibilities**: Institutional vault for token custody ### User onboarding process (if "Invite user" was chosen) [#user-onboarding-process-if-invite-user-was-chosen] All invited users follow the same onboarding process. See [User Onboarding](/docs/user-guides/user-management/user-onboarding) for the complete flow. **Each user will:** 1. Accept their invitation 2. Generate blockchain wallet with PIN security 3. Save backup codes 4. Create on-chain identity 5. Complete profile For public chains: If using the invitation flow, invited users will need native tokens in their wallets to pay for identity creation. If using the create-user flow, the identity manager pays for identity creation. ### Assign platform admins [#assign-platform-admins] **Continue as Daniel Admin.** After team onboarding, assign specific roles to platform admins. **Use [Add Administrators](/docs/user-guides/platform-setup/add-admins) to assign:** **Olivia Operator** needs these roles: * **Asset Manager** - Create and configure new assets **Why these roles:** Olivia should be able to manage assets. **Clara Compliance** needs these roles: * **Identity Manager** - Manage the users registry, which users are known. * **Verification Issuer** - Issue KYC and compliance verifications * **Compliance Manager** - Will be able to manage global compliance rules * **Verification Policy Manager** - Configure verification topics and trusted issuers **Why these roles:** Clara manages all regulatory compliance and user verification. **Colin Collateral** needs these roles: * **Verification Issuer** - Issue collateral verifications **Why this role:** Colin's only function is verifying collateral backing. ### Configure trusted issuers [#configure-trusted-issuers] **Log in as Clara Compliance** to set up the verification framework by designating who can issue which types of verifications. For public chains: Ensure Clara's wallet has native tokens for trusted issuer configuration transactions. **Follow [Configure Trusted Issuers](/docs/user-guides/compliance/configure-trusted-issuers) to configure:** **Clara Compliance as trusted issuer for investor-level verifications:** * **Know Your Customer (KYC)** - She verifies all investor identities * **Accredited Investor** - For US qualified investor status (optional, if needed) * **Qualified Institutional Investor** - For institutional investors under EU rules (optional, if needed) * **Anti-Money Laundering** - Source of funds verification (optional, if needed) * **Professional Investor** - For MiFID professional investor classification (optional, if needed) **Why Clara issues these:** As Compliance Officer, she's responsible for all investor-related verification. **Colin Collateral as trusted issuer for collateral verification:** * **Collateral** - Verifies that sufficient collateral backs the token **Why Colin issues this:** Guardian Collateral Services is the independent third party verifying collateral. **Olivia Operator as trusted issuer for asset-level verifications:** * **Base Price** - Provides reference pricing for the asset * **Unique Identifier** - Manages ISIN and internal reference numbers * **Asset Classification** - Confirms asset category and type * **Asset Location** - Confirms jurisdiction of underlying asset (optional, needed for real-estate) **Why Olivia issues these:** As Asset Operator, she manages asset metadata and classification. ### Register investors in registry [#register-investors-in-registry] **Log in as Clara Compliance** and use [Register User](/docs/user-guides/user-management/register-user): **Register Ian Issuer:** * Choose **Luxembourg** as jurisdiction **Register NovaX Vault:** * Choose **Luxembourg** as jurisdiction **Result:** Both users now show "Registered" status and can receive KYC verifications. Platform operators (Daniel, Olivia, Clara) don't need registration unless they'll hold tokens. ### Issue KYC verifications [#issue-kyc-verifications] **Continue as Clara Compliance** and use [Verify KYC](/docs/user-guides/compliance/verify-kyc): **Verify Ian Issuer:** * Issue **Know Your Customer** verification **Verify NovaX Vault:** * Issue **Know Your Customer** verification **Result:** Both users now have KYC verifications and can receive ACME tokens. ### Create ACME Equity Token [#create-acme-equity-token] Create the equity token using the Asset Designer with full compliance configuration. For public chains: Ensure Olivia's wallet has native tokens for asset creation and permission management transactions. **Log in as Olivia Operator and follow [Create Asset](/docs/user-guides/asset-creation/create-asset):** **Asset Designer Configuration:** **Step 1: Asset Class** * Select **Flexible Income** class * Choose **Equity** type **Step 2: Basic Details** * **Name**: ACME Holdings Equity * **Symbol**: ACME * **Decimals**: 0 (whole shares only) * **Jurisdiction**: Luxembourg **Step 3: Asset Classification** * **Category**: Common Equity * **Class**: Common Equity * **Unique Identifier**: LU0000ACME01 (ISIN format) * **Internal Reference**: ACME-EQ-001 **Step 4: Pricing** * **Currency**: EUR * **Base Price**: 0.71 (€0.71 per share) **Step 5: Compliance Modules** Enable **Smart Identity Verification**: * Required verification: "Know Your Customer" * Purpose: Only KYC-verified users can receive assets Enable **Collateral Requirement** (optional): * Topic: "Collateral" * Ratio: 100% (full collateral backing) * Purpose: Ensures backing for tokenized equity ### Configure token permissions [#configure-token-permissions] After asset creation, Olivia has the minimum required roles (Permission manager + Governance) following the principle of least privilege. For this demo, she needs additional token-specific roles. **Use [Change Asset Admin Roles](/docs/user-guides/asset-servicing/change-asset-admin-roles) to assign Olivia Operator these additional roles:** * **Custodian** - Execute transfers and forced transfers * **Emergency** - Pause/unpause token operations * **Supply Management** - Mint and burn token supply ### Issue collateral verification [#issue-collateral-verification] Colin must issue a collateral verification before minting is possible. This is a requirement when collateral modules are enabled. For public chains: Ensure Colin's wallet has native tokens for issuing collateral verification transactions. **Log in as Colin Collateral and follow the [Collateral](/docs/user-guides/compliance/collateral) guide to issue verification with these specific details:** * **Amount**: 100000 (covering full supply) * **Expiration**: 1 year from now **Purpose:** This confirms Guardian Collateral Services has verified sufficient backing for the full token supply. ### Unpause the token [#unpause-the-token] **Log in as Olivia Operator** and use the [Pause/Unpause Asset](/docs/user-guides/asset-servicing/pause-unpause-asset) guide to activate the ACME asset. Collateral verification and token unpausing can be done in any order. Both are needed before minting, but they don't depend on each other. **Why Olivia can unpause:** She has the Emergency role on the ACME token, which is required for pause/unpause operations. ### Mint and distribute ACME tokens [#mint-and-distribute-acme-tokens] **Continue as Olivia Operator** and use [Mint Assets](/docs/user-guides/asset-servicing/mint-assets): For public chains: Ensure Olivia's wallet has sufficient native tokens for minting transactions, which can be gas-intensive for large amounts. **Mint to ACME Holdings:** * **Recipient**: Ian Issuer (select from contacts or use wallet address) * **Amount**: 100000 (full token supply) * **Purpose**: Initial distribution to issuer ### Execute forced transfer to exchange [#execute-forced-transfer-to-exchange] **Continue as Olivia Operator.** Transfer 20,000 tokens from the issuer to the exchange using forced transfer capability. For public chains: Ensure Olivia's wallet has native tokens for the forced transfer transaction. **Use [Forced Transfer](/docs/user-guides/asset-servicing/forced-transfer) with these details:** **Transfer configuration:** * **From address**: ACME Holdings (select from contacts) * **To address**: NovaX Exchange (select from contacts) * **Amount**: 20000 * **Purpose**: Allocate tokens to exchange for secondary market custody **Why use forced transfer:** This demonstrates custodian capabilities to move tokens between verified parties, which is important for corporate actions and compliance scenarios. **Why Olivia can execute this:** She has the Custodian role on the ACME token, which is required for forced transfer operations. Deployed equity token details ## Troubleshooting [#troubleshooting] | Issue | Solution | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Minting fails | • Ensure collateral verification covers mint amount (if enabled)
• Verify all recipients have required KYC verifications
• Check operator has Supply Management role
• Confirm wallet has sufficient gas tokens (ETH, MATIC, etc.) for public chains | | User cannot receive assets | • Verify user is registered in identity registry
• Ensure user has valid KYC verification
• Check token compliance requirements
• Confirm user's wallet address is correct | | Verifications not trusted | • Verify issuer is configured as trusted for specific topic
• Check verification data format and expiration
• Ensure issuer transaction was confirmed
• Refresh page after configuration changes | | Role assignment fails | • Confirm you have role management permissions
• Verify target user completed onboarding
• Ensure user belongs to your organization (for internal roles)
• Check transaction has sufficient gas tokens for public chains | For additional support, consult the individual feature guides or contact your implementation team. # User onboarding Source: https://docs.settlemint.com/docs/user-guides/runbooks/onboard-user-im Complete workflow to register a user in the identity registry and issue KYC verification. This guide walks Identity Managers through the complete user onboarding workflow. It covers the steps after a user has created their account and wallet through the [User Onboarding](/docs/user-guides/user-management/user-onboarding) process. To complete all steps in this runbook, you will need both the **Identity Manager** and **Verification Issuer** roles. ## Prerequisites [#prerequisites] * **Identity Manager** role for registry registration * **Verification Issuer** role for issuing KYC verifications * Must be configured as [trusted issuer](/docs/user-guides/compliance/configure-trusted-issuers) for KYC topic * User must have completed onboarding with on-chain identity ## About the onboarding workflow [#about-the-onboarding-workflow] As an Identity Manager, you complete two key steps to fully onboard a user: 1. **Register in identity registry** - Add the user to the on-chain registry with their jurisdiction 2. **Issue KYC verification** - Attest that you have verified the user's identity ### User states [#user-states] | State | Description | Can receive assets | | ------------------------ | --------------------------------------------- | ---------------------- | | **Pending Registration** | User completed onboarding but not in registry | No | | **Registered** | Added to registry, awaiting verification | Depends on asset rules | | **Verified** | Has required KYC verification | Yes | ## Onboarding steps [#onboarding-steps] ### Verify user readiness [#verify-user-readiness] Before starting, confirm the user has completed their onboarding: 1. Navigate to **Participants** > **Users** 2. Find the user and check their status 3. Verify they have an **Identity address** assigned If the user shows no identity address, they haven't completed onboarding yet. See [User Onboarding](/docs/user-guides/user-management/user-onboarding) for what the user needs to complete. User list showing identity address column ### Register in identity registry [#register-in-identity-registry] Registration adds the user to the on-chain identity registry with their jurisdiction. **Follow [Register User](/docs/user-guides/user-management/register-user) to:** * Select the user from the Users list * Click **Register Identity** * Choose the appropriate jurisdiction based on user documentation * Complete the registration transaction Choose based on the user's legal residence or incorporation. This determines applicable compliance rules. After transaction confirmation, verify the user status changed to **"Registered"**. User profile showing Registered status ### Issue KYC verification [#issue-kyc-verification] With the user registered, issue a KYC verification to attest their identity. **Follow [Verify KYC](/docs/user-guides/compliance/verify-kyc) to:** * Access the user's profile and click **Manage verifications** * Click **Issue verification** * Select **Know Your Customer (KYC)** topic * Complete the verification transaction A green shield next to the verification confirms you are a trusted issuer for KYC. Without trusted issuer status, your verification won't satisfy compliance requirements. Verification list showing KYC with green shield ### Verify completion [#verify-completion] After issuing the KYC verification: * User appears with verified status * User can now receive assets that require KYC verification * Additional verifications may be needed depending on asset-specific compliance rules Fully verified user profile ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------------------- | ----------------------------------------------------------------------------- | | Cannot see Register Identity button | Verify you have Identity Manager role and user has on-chain identity | | Registration transaction fails | Check wallet has sufficient gas and correct user is selected | | Cannot see Issue verification button | Verify you have Verification Issuer role and are configured as trusted issuer | | Verification not showing green shield | Confirm you're a trusted issuer for KYC topic | | User still cannot receive assets | Check asset-specific compliance requirements beyond KYC | ## Related guides [#related-guides] * [User Onboarding](/docs/user-guides/user-management/user-onboarding) - Understanding the user's onboarding flow * [Provide KYC Data](/docs/user-guides/user-management/provide-kyc-data) - How users complete their KYC profile * [Manage KYC Data](/docs/user-guides/compliance/manage-kyc-data) - Review and approve KYC submissions * [Register User](/docs/user-guides/user-management/register-user) - Detailed registration reference * [Verify KYC](/docs/user-guides/compliance/verify-kyc) - Detailed verification reference * [Configure Trusted Issuers](/docs/user-guides/compliance/configure-trusted-issuers) - Set up verification permissions * [Compliance Overview](/docs/user-guides/compliance/overview) - Understanding verification framework # Install addons Source: https://docs.settlemint.com/docs/user-guides/system-addons/install-addons Learn how to discover and install system addons from the on-chain Directory registry. Addons extend your tokenized assets with advanced capabilities. This guide explains how to install system addons to extend your platform's capabilities. The platform automatically discovers available addons from the on-chain Directory registry, ensuring you always see the latest available addon types. Available addons including XVP settlement and yield schedules ## Prerequisites [#prerequisites] * Platform administrator access * Understanding of which addon types your organization needs ## Install from Settings [#install-from-settings] Go to **Settings** > **Add-ons** in the platform navigation. The page displays all addon types registered in the Directory. Each addon shows its name, description, and category. Available addon types include: * **XvP Settlement** — Atomic multi-party exchanges * **Yield Schedule** — Automated yield distributions * **Vault** — Multi-signature treasury control * **Airdrop** — Token distribution mechanisms Click **Install** on the addon you want to enable. The system deploys the addon to your platform. Once installed, the addon status changes to "Installed" and becomes available for use with your assets. After installation, you can configure addon-specific settings such as: * Default parameters for new instances * Role assignments * Integration options ## Install during onboarding [#install-during-onboarding] When creating a new tokenized asset, you can install addons as part of the onboarding wizard: Begin creating a new asset through the onboarding wizard. On the Add-ons step, browse available addon types from the Directory. Select all addons your asset needs. For each selected addon, provide the necessary configuration: - Initial parameters - Access controls - Integration settings Finish the wizard. Selected addons are installed and configured automatically. ## Understanding addon types [#understanding-addon-types] The Directory registry supports several addon categories: ### Distribution addons [#distribution-addons] * **Push Airdrop** — Admin-initiated token distribution * **Vesting Airdrop** — Time-locked token releases with schedules * **Time-bound Airdrop** — Limited window token claims ### Exchange addons [#exchange-addons] * **XvP Settlement** — Atomic cross-party asset exchanges ### Custody addons [#custody-addons] * **Vault** — Multi-signature treasury management ### Income addons [#income-addons] * **Yield Schedule** — Automated yield and dividend distributions ## After installation [#after-installation] Once an addon is installed: 1. **Authorized users can create instances** — Users with appropriate permissions can create specific addon instances for their assets 2. **Operations are recorded on-chain** — All addon activities create audit trails 3. **API access is available** — Integrate addon functionality into your workflows Contact your platform administrator if you need assistance selecting or configuring addons. ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------------- | ------------------------------------------------------------------------------------- | | Addon not appearing in list | Verify the addon type is registered in the Directory. Contact platform administrator. | | Cannot install addon | Ensure you have administrator permissions. | | Addon installed but not working | Check addon configuration and user permissions. | ## Related [#related] * [System addons introduction](/docs/user-guides/system-addons/introduction) * [XvP settlement overview](/docs/user-guides/system-addons/xvp-settlement/overview) # Introduction Source: https://docs.settlemint.com/docs/user-guides/system-addons/introduction System addons extend your tokenized assets with advanced capabilities like atomic settlements, yield management, and automated distributions. Learn how platform administrators discover and install addons from the on-chain Directory registry. System addons are optional modules that extend the functionality of your tokenized assets. Each addon provides specialized capabilities that work alongside your core asset operations. System addons extend platform capabilities with optional features ## Available addons [#available-addons] The platform automatically discovers available addons from the on-chain Directory registry. This means the addon list is always up-to-date—no manual configuration required. When new addon types are registered to the Directory, they automatically appear in your Add-ons settings. | Addon | Purpose | Common use cases | | ------------------ | ---------------------------------------------- | ----------------------------------------------------------- | | **XvP Settlement** | Atomic multi-party asset exchanges | Bond-for-cash trades, currency swaps, cross-chain transfers | | **Yield Schedule** | Automated distribution of returns | Bond coupons, dividend payments, interest distributions | | **Vault** | Multi-signature treasury control | DAO treasuries, corporate custody, maker-checker workflows | | **Airdrop** | Token distribution | Community rewards, team vesting, acquisition swaps | | **Feeds** | On-chain data feeds with issuer-signed updates | Price feeds, NAV reporting, oracle data | Feeds can be reviewed from platform settings and managed through the feed APIs. See the [Feeds developer guide](/docs/developer-guides/feeds/overview) for details. Learn how to install addons from the Settings page or during onboarding. ## How addon discovery works [#how-addon-discovery-works] Addons are registered in the system's on-chain Directory. The platform queries this registry to build the list of available addons. This approach ensures: * **Always current**: New addon types become available automatically when registered to the Directory * **Consistent**: All environments use the same addon list from their respective Directory * **Extensible**: Platform operators can add new addon types without code changes When you access the Add-ons settings page, the platform fetches the current list of registered addon types from the Directory. Any addon type listed in the Directory appears as available for installation. ## How addons work [#how-addons-work] Addons are deployed as separate smart contracts registered in the system's addon registry. They operate independently of individual assets, providing platform-wide capabilities that any authorized user can use for their tokenized assets. Each addon: * Is registered once at the system level by platform administrators * Operates independently of other addons * Has its own role-based access controls * Provides both UI workflows and API access * Records all operations on-chain for auditability ### Addon deployment vs. instances [#addon-deployment-vs-instances] **System-level deployment** (administrators): * Enables an addon for the entire platform * Done during initial setup or via platform settings * Example: "Deploy XvP Settlement addon" **Creating instances** (authorized users): * After deployment, users create specific addon instances for their needs * Examples: "Create a yield schedule for this bond", "Create an XvP settlement between these parties" * Each instance operates independently with its own configuration ## Enabling addons [#enabling-addons] Addons are configured at the system level by platform administrators. Once enabled, authorized users can access addon features through the platform UI or API. To install an addon: 1. Go to **Settings** > **Add-ons** 2. Browse available addons from the Directory registry 3. Click **Install** on the addon you need 4. Configure the addon for your organization You can also install addons during the onboarding wizard when setting up a new asset. Contact your platform administrator to enable addons for your organization. ## Further reading [#further-reading] Atomic settlements for secure multi-party and cross-chain asset exchanges. Automated yield distribution for bonds and other yield-bearing assets. # Approve Source: https://docs.settlemint.com/docs/user-guides/system-addons/xvp-settlement/actions/approve Learn how to approve your flows in an XvP settlement. Approving locks your assets into the settlement contract as escrow until execution or cancellation. When you're a sender in an XvP settlement, you must approve to lock your assets into the settlement contract. This step is required before the settlement can execute. ## What approval means [#what-approval-means] When you approve a settlement: 1. Your tokens are transferred from your wallet to the XvP contract 2. The assets are held in escrow until the settlement executes or is cancelled 3. Your approval is recorded on-chain 4. The settlement progresses toward execution You only need to approve if you are sending assets in the settlement. Recipients do not need to take any action. ## Prerequisites [#prerequisites] * You are listed as a sender in one or more local flows * Your wallet holds sufficient assets for the flows you're sending ## Steps [#steps] ### Find the settlement [#find-the-settlement] Navigate to **Settlements** in the sidebar. Pending settlements where you need to approve are marked with an action indicator. Click on the settlement to open its detail page. ### Review the flows [#review-the-flows] Before approving, carefully review: * All flows in the settlement (who sends what to whom) * Your specific flows (what you're sending and to whom) * The cutoff date * For HTLC settlements: the hashlock and timelock ### Approve your flows [#approve-your-flows] Click **Approve** on the settlement detail page. A confirmation dialog shows: * The tokens being locked * The amounts being transferred to escrow * The recipient addresses Review and click **Confirm**, then authenticate with your PIN or OTP. ### Verify approval status [#verify-approval-status] After the transaction confirms: * Your approval status changes to "Approved" * The settlement shows updated approval count (e.g., "2 of 3 approved") * If you were the final sender to approve, the settlement may execute (if auto-execute is enabled) ## What happens after approval [#what-happens-after-approval] | Settlement type | What happens when all approve | | ---------------------------- | -------------------------------------------------------- | | **Local (auto-execute on)** | Settlement executes immediately | | **Local (auto-execute off)** | Settlement enters Ready state, awaiting manual execution | | **HTLC** | Settlement enters Armed state, awaiting secret reveal | ## Revoking approval [#revoking-approval] Revocation rules differ by settlement type: **Local settlements:** * You can revoke your approval at any time before execution * Revocation is allowed even after the settlement becomes Ready (fully approved) **HTLC settlements:** * You can revoke only before the settlement becomes Armed (before all approvals are received) * Once Armed (fully approved), revocation is not allowed See [Revoke approval](/docs/user-guides/system-addons/xvp-settlement/actions/revoke-approval) for detailed steps. ## Troubleshooting [#troubleshooting] | Issue | Solution | | ----------------------- | --------------------------------------------------- | | Approve button disabled | You may not be a sender in any local flows | | Insufficient balance | Ensure your wallet has enough assets for your flows | | Transaction reverts | Check compliance requirements on the token | ## Next steps [#next-steps] How to revoke your approval before settlement is armed. How to manually trigger execution when auto-execute is off. # HTLC settlements Source: https://docs.settlemint.com/docs/user-guides/system-addons/xvp-settlement/actions/cancel/htlc-settlements Learn how to cancel an HTLC settlement. Before arming, any participant can cancel. After arming, all local participants must vote to cancel unanimously. Cancelling an HTLC settlement returns all locked assets to their original senders. The cancellation process depends on whether the settlement is armed (all approvals received). ## Cancellation rules [#cancellation-rules] | Settlement state | Who can cancel | How | | ------------------------------ | --------------------------------- | ----------------------- | | **Pending** (not all approved) | Any local participant | Unilateral | | **Armed** (all approved) | All local participants must agree | Voting (propose cancel) | | **Secret revealed** | Cannot cancel | - | | **Executed** | Cannot cancel | - | | **Expired** | N/A - use withdraw | - | *** ## Before armed (pending state) [#before-armed-pending-state] When not all parties have approved, any local participant can cancel unilaterally. ### Steps [#steps] ### Open the settlement [#open-the-settlement] Navigate to **Settlements** and find the HTLC settlement you want to cancel. ### Cancel the settlement [#cancel-the-settlement] Click **Cancel** on the settlement detail page. Review the confirmation and click **Confirm**. ### Verify cancellation [#verify-cancellation] The settlement status changes to **Cancelled** and all locked assets are returned. *** ## After armed (voting mechanism) [#after-armed-voting-mechanism] Once an HTLC settlement enters the **Armed** state (all local senders have approved), unilateral cancellation is no longer allowed. This protects parties who have already committed assets on other chains. Instead, cancellation requires **unanimous agreement** from all local participants through a voting mechanism. Every local participant (sender and recipient in local flows) must propose cancellation. The settlement is cancelled only when the final vote is received. ### How the voting mechanism works [#how-the-voting-mechanism-works] 1. Any local participant can propose cancellation 2. The settlement tracks how many participants have voted 3. Participants can withdraw their proposal before the final vote 4. When ALL local participants have voted, the settlement is automatically cancelled ### Steps to propose cancellation [#steps-to-propose-cancellation] ### Open the settlement [#open-the-settlement-1] Navigate to **Settlements** and find the armed HTLC settlement. ### Propose cancellation [#propose-cancellation] Click **Propose Cancel** on the settlement detail page. Confirm the transaction. Your vote is recorded on-chain. ### Wait for other participants [#wait-for-other-participants] The settlement shows the current vote count. Contact other participants and ask them to also propose cancellation. ### Final vote triggers cancellation [#final-vote-triggers-cancellation] When the last participant proposes cancellation, the settlement is automatically cancelled and all assets are returned. ### Withdrawing a cancel proposal [#withdrawing-a-cancel-proposal] If you change your mind before the final vote, you can withdraw your proposal: 1. Open the armed settlement 2. Click **Withdraw Cancel Proposal** 3. Confirm the transaction Your vote is removed and the settlement remains armed. *** ## What happens after cancellation [#what-happens-after-cancellation] * All escrowed assets are returned to their original sender addresses * The settlement permanently enters Cancelled state * No further actions can be taken on this settlement ## Why armed settlements require unanimous voting [#why-armed-settlements-require-unanimous-voting] The voting requirement exists because: * Parties may have already committed assets on other chains * Unilateral cancellation could leave counterparties stranded * Unanimous agreement ensures all parties consent to unwinding the settlement If you cannot reach unanimous agreement, wait for the cutoff date to pass and use [Withdraw expired](/docs/user-guides/system-addons/xvp-settlement/actions/withdraw-expired). ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------------------------ | --------------------------------------------------------------------- | | Cancel button not visible | You must be a local participant (sender or recipient in a local flow) | | "Propose Cancel" shown instead of "Cancel" | Settlement is armed - all local participants must vote to cancel | | Settlement not cancelling | Not all participants have voted yet - check the vote count | | Cannot propose cancel | Secret may have been revealed, making cancellation impossible | | Want to undo my cancel vote | Click "Withdraw Cancel Proposal" before the final vote is cast | ## Next steps [#next-steps] Complete multi-party cross-chain settlement example. Recover assets from expired settlements. # Cancel Source: https://docs.settlemint.com/docs/user-guides/system-addons/xvp-settlement/actions/cancel Overview of cancelling XvP settlements. See specific guides for local and HTLC settlement cancellation procedures. Cancelling a settlement stops the exchange and returns all locked assets to their original senders. The cancellation process varies by settlement type. ## Local settlements [#local-settlements] Any local participant can cancel a local settlement at any time before execution. No agreement from other parties is required. Simple unilateral cancellation for local settlements. ## HTLC settlements [#htlc-settlements] HTLC settlement cancellation depends on the settlement state: * **Before armed**: Any local participant can cancel unilaterally * **After armed**: All local participants must vote to cancel unanimously Cancellation rules and voting mechanism for HTLC settlements. ## Cannot cancel? [#cannot-cancel] If a settlement is executed or the secret has been revealed, cancellation is not possible. If the settlement has expired, use [Withdraw expired](/docs/user-guides/system-addons/xvp-settlement/actions/withdraw-expired) instead. # Local settlements Source: https://docs.settlemint.com/docs/user-guides/system-addons/xvp-settlement/actions/cancel/local-settlements Learn how to cancel a local XvP settlement and return locked assets to senders. Any local participant can cancel at any time before execution. Cancelling a local settlement stops the exchange and returns all locked assets to their original senders. For local settlements, cancellation is straightforward. ## Who can cancel [#who-can-cancel] Any **local participant** (sender or recipient in a local flow) can cancel a local settlement at any time before execution. | Settlement state | Can cancel? | | ------------------------------ | ----------------- | | **Pending** (not all approved) | Yes | | **Ready** (all approved) | Yes | | **Executed** | No | | **Expired** | No - use withdraw | Unlike HTLC settlements, local settlements can be cancelled by any single participant at any time. No voting or agreement from other parties is required. ## Prerequisites [#prerequisites] * You are a local participant (sender or recipient in a local flow) * The settlement has not been executed or expired ## Steps [#steps] ### Open the settlement [#open-the-settlement] Navigate to **Settlements** and find the settlement you want to cancel. ### Cancel the settlement [#cancel-the-settlement] Click **Cancel** on the settlement detail page. Review the confirmation and click **Confirm**. Authenticate with your PIN or OTP. ### Verify cancellation [#verify-cancellation] After the transaction confirms: * Settlement status changes to **Cancelled** * All locked assets are returned to their original senders * The settlement cannot be reactivated ## What happens after cancellation [#what-happens-after-cancellation] * All escrowed assets are returned to their original sender addresses * The settlement permanently enters Cancelled state * No further actions can be taken on this settlement ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------- | --------------------------------------------------------------------- | | Cancel button not visible | You must be a local participant (sender or recipient in a local flow) | | Cancel button disabled | Settlement may be already cancelled or executed | | Transaction fails | Check if settlement was executed or cancelled by another party | ## Alternative: Withdraw from expired [#alternative-withdraw-from-expired] If the settlement expired (cutoff date passed), use [Withdraw expired](/docs/user-guides/system-addons/xvp-settlement/actions/withdraw-expired) instead. ## Next steps [#next-steps] Step-by-step guide to completing a local settlement. Create a new settlement after cancellation. # Create Source: https://docs.settlemint.com/docs/user-guides/system-addons/xvp-settlement/actions/create Learn how to create a new XvP settlement using the wizard. Configure flows, set cutoff dates, and enable cross-chain coordination with hashlocks. The settlement wizard guides you through creating a new XvP settlement. You'll configure the asset flows, set a cutoff date, and optionally enable HTLC mode for cross-chain coordination. ## Prerequisites [#prerequisites] * Access to the XvP Settlement addon in the platform * Knowledge of the parties involved (wallet addresses) * Token contract addresses for assets being exchanged * For HTLC settlements: coordination with counterparties on other chains ## Steps [#steps] ### Open the settlement wizard [#open-the-settlement-wizard] Navigate to **Settlements** in the sidebar and click **Create Settlement**. ### Add flows [#add-flows] Configure the asset transfers that make up your settlement. Each flow specifies: * **Token** - The asset being transferred * **Sender** - The wallet address sending the asset * **Recipient** - The wallet address receiving the asset * **Amount** - The quantity to transfer Click **Add Flow** to add additional flows. Most settlements have at least two flows (one in each direction). Mark flows as **External** if they execute on another chain. External flows are informational and don't execute locally. ### Configure settlement options [#configure-settlement-options] Set the following options: | Option | Description | | ---------------- | ------------------------------------------------------------------- | | **Name** | A descriptive name for the settlement (e.g., "BOND-for-USDC Trade") | | **Cutoff date** | Deadline after which the settlement expires | | **Auto-execute** | When enabled, settlement executes automatically upon final approval | ### Configure HTLC (for cross-chain) [#configure-htlc-for-cross-chain] If your settlement includes external flows, you must configure HTLC settings: **Generate a secret:** * Click **Generate Secret** to create a cryptographic secret * The hashlock is automatically computed * **Save the secret securely** - you'll need it to complete the settlement **Or provide your own:** * Enter an existing hashlock if coordinating with a settlement on another chain * You'll need the original secret to execute If you generate the secret, store it securely. Without the secret, you cannot trigger execution on any chain using this hashlock. ### Review and create [#review-and-create] Review all flows and settings on the summary page: * Verify all addresses and amounts are correct * Confirm the cutoff date allows sufficient time for all parties to approve * For HTLC, verify the hashlock matches what other chains will use Click **Create** and authenticate with your PIN or OTP to submit the transaction. ## After creation [#after-creation] Once your settlement is created: * It appears in the **Settlements** list with **Pending** status * All senders in local flows receive notification to approve * The settlement ID and details are visible on the detail page * For HTLC settlements, share the hashlock with counterparties on other chains ## Importing flows from JSON [#importing-flows-from-json] For complex settlements with many flows, you can import from JSON: 1. Click **Import Flows** 2. Paste or upload a JSON file with this structure: ```json [ { "type": "local", "assetId": "0x1234...", "assetSymbol": "USDC", "assetDecimals": 6, "from": "0xaaaa...", "to": "0xbbbb...", "amount": "1000000" }, { "type": "external", "assetId": "0x5678...", "from": "0xcccc...", "to": "0xdddd...", "amount": "500000000000000000", "externalChainId": 1, "externalAssetDecimals": 18 } ] ``` **Field reference:** | Field | Required | Description | | ----------------------- | -------- | ------------------------------------------- | | `type` | Yes | `"local"` or `"external"` | | `assetId` | Yes | Token contract address | | `from` | Yes | Sender wallet address | | `to` | Yes | Recipient wallet address | | `amount` | Yes | Amount as string (in token's smallest unit) | | `assetSymbol` | No | Token symbol (e.g., "USDC") | | `assetDecimals` | No | Decimals for local flows | | `externalChainId` | No | Chain ID (required for external flows) | | `externalAssetDecimals` | No | Decimals for external flows | ## Next steps [#next-steps] How senders lock their assets into the settlement. See a complete local settlement example. ## Related [#related] * [XvP settlement overview](/docs/user-guides/system-addons/xvp-settlement/overview) * [XvP settlement overview](/docs/user-guides/system-addons/xvp-settlement/overview) # Execute Source: https://docs.settlemint.com/docs/user-guides/system-addons/xvp-settlement/actions/execute Learn how to manually execute an XvP settlement when auto-execute is disabled. Manual execution gives you a final review step before committing. When auto-execute is disabled, you must manually trigger execution after all approvals are received. This gives participants a final review step before the settlement commits. ## When manual execution is needed [#when-manual-execution-is-needed] Manual execution is required when: * Auto-execute was disabled during settlement creation * The settlement is in **Ready** state (local) or secret was revealed (HTLC) * All pre-conditions are met but execution hasn't occurred Most settlements use auto-execute, which triggers immediately when all conditions are met. Manual execution is only needed when explicitly disabled. ## Prerequisites [#prerequisites] For local settlements: * Settlement is in **Ready** state (all senders have approved) * Auto-execute is disabled For HTLC settlements: * Settlement is in **Armed** state and secret has been revealed * Auto-execute is disabled ## Steps [#steps] ### Verify settlement is ready [#verify-settlement-is-ready] Navigate to **Settlements** and open the settlement detail page. Confirm: * Status shows "Ready to execute" or similar * All required approvals are complete * For HTLC: the secret has been revealed ### Execute the settlement [#execute-the-settlement] Click **Execute** on the settlement detail page. Review the confirmation showing all flows that will execute. Click **Confirm** and authenticate with your PIN or OTP. ### Verify execution [#verify-execution] After the transaction confirms: * Settlement status changes to **Executed** * All flows complete atomically * Recipients receive their assets * The settlement is final ## Who can execute? [#who-can-execute] Anyone can trigger manual execution once all conditions are met. The execution is completely permissionless. While typically executed by participants (senders, recipients, or addresses in flows), any Ethereum address can call the execute function. ## What happens during execution [#what-happens-during-execution] The settlement contract: 1. Validates all flows one final time 2. Transfers assets from escrow to recipients atomically 3. Records the execution on-chain 4. Marks the settlement as complete If any flow fails (e.g., compliance block added after approval), the entire settlement reverts. ## Troubleshooting [#troubleshooting] | Issue | Solution | | -------------------------- | --------------------------------------------------------- | | Execute button not visible | Auto-execute may be enabled, or settlement isn't ready | | Execute button disabled | Not all approvals received, or secret not revealed (HTLC) | | "Settlement expired" | Cutoff date passed - use withdraw instead | ## Next steps [#next-steps] How to recover assets from expired settlements. Return to the XvP Settlement overview. ## Related [#related] * [XvP settlement overview](/docs/user-guides/system-addons/xvp-settlement/overview) * [Reveal secret](/docs/user-guides/system-addons/xvp-settlement/actions/reveal-secret) # Reveal secret Source: https://docs.settlemint.com/docs/user-guides/system-addons/xvp-settlement/actions/reveal-secret Learn how to reveal the HTLC secret to trigger execution of a cross-chain settlement. Once revealed, the secret becomes public and can be used on all linked chains. For HTLC (cross-chain) settlements, revealing the secret triggers execution of the settlement. The secret proves knowledge of the original value that was used to create the hashlock. ## When to reveal the secret [#when-to-reveal-the-secret] Reveal the secret when: * The settlement is in **Armed** state (all local senders have approved) * You are ready to complete the exchange * You want to claim the assets locked in this settlement Typically, the party who generated the secret reveals it first on the chain where they receive assets. Once revealed, the secret is public and anyone can use it on other chains. ## Prerequisites [#prerequisites] * The settlement is in Armed state * You have the secret that matches the settlement's hashlock * You're ready to complete the settlement (no going back after reveal) ## Steps [#steps] ### Open the settlement [#open-the-settlement] Navigate to **Settlements** and find the HTLC settlement in Armed state. Verify: * All local senders have approved * The settlement shows "Waiting for secret" * The cutoff date has not passed ### Reveal the secret [#reveal-the-secret] Click **Reveal Secret** on the settlement detail page. Enter the secret value (the original value before hashing). Optionally, enter email addresses to share the secret with. Once the secret is revealed on-chain, these recipients will receive it via email. This is typically used to notify your counterparty on the external chain so they can complete their side of the settlement. ### Confirm execution [#confirm-execution] Review the confirmation dialog showing: * The secret being revealed * The flows that will execute * The assets you'll receive Click **Confirm** and authenticate with your PIN or OTP. ### Verify execution [#verify-execution] After the transaction confirms: * The settlement enters **Executed** state * All local flows complete atomically * Recipients receive their assets * The secret is now visible on-chain ## What happens after reveal [#what-happens-after-reveal] Once you reveal the secret: 1. **This chain:** The settlement executes immediately (if auto-execute is on) 2. **Other chains:** Anyone can see the secret on-chain and use it 3. **Secret becomes public:** There's no security risk since the settlement is complete ## Sharing the secret [#sharing-the-secret] After revealing, you can share the secret with counterparties on other chains: * **Option A:** They find it on-chain (in the transaction that revealed it) * **Option B:** You send it via email or messaging (it's already public) Sharing via email is convenient because counterparties don't need to monitor the blockchain. ## Timing considerations [#timing-considerations] Reveal the secret with enough time buffer before timelocks expire: | Action | Recommendation | | --------------------- | ------------------------------------------------ | | Reveal on first chain | At least 24 hours before second chain's timelock | | Monitor confirmation | Ensure transaction confirms before moving on | ## Troubleshooting [#troubleshooting] | Issue | Solution | | ---------------------- | ------------------------------------------------------------------------- | | "Invalid secret" error | The secret doesn't match the hashlock - verify you have the correct value | | "Settlement not armed" | All senders must approve before the secret can be revealed | | "Settlement expired" | The cutoff date passed - withdraw your assets instead | | Transaction reverts | Check gas limits and network conditions | ## Next steps [#next-steps] How to recover assets if the settlement expires. Understand how the hashlock and secret mechanism works. ## Related [#related] * [XvP settlement overview](/docs/user-guides/system-addons/xvp-settlement/overview) * [HTLC walkthrough](/docs/user-guides/system-addons/xvp-settlement/htlc/walkthrough) # Revoke approval Source: https://docs.settlemint.com/docs/user-guides/system-addons/xvp-settlement/actions/revoke-approval Learn how to revoke your approval from an XvP settlement. Revoking returns your locked assets to your wallet. If you've approved a settlement but need to back out, you can revoke your approval. Revocation rules differ between local and HTLC settlements. Revoking returns your locked assets to your wallet. ## When you can revoke [#when-you-can-revoke] Revocation rules differ by settlement type: **Local settlements:** | Settlement state | Can revoke? | | -------------------------- | ----------- | | Pending (not all approved) | Yes | | Ready (all approved) | Yes | | Executed | No | | Cancelled | N/A | | Expired | N/A | **HTLC settlements:** | Settlement state | Can revoke? | | -------------------------- | ----------- | | Pending (not all approved) | Yes | | Armed (all approved) | No | | Executed | No | | Cancelled | N/A | | Expired | N/A | **Local settlements:** You can revoke at any time before execution, even after the settlement becomes Ready (fully approved). **HTLC settlements:** You can only revoke before the settlement becomes Armed. Once all senders have approved (Armed state), revocation is not allowed. ## Prerequisites [#prerequisites] * You have previously approved the settlement * For local settlements: Settlement has not been executed * For HTLC settlements: Settlement is still in Pending state (not all senders have approved yet) ## Steps [#steps] ### Open the settlement [#open-the-settlement] Navigate to **Settlements** and find the settlement where you want to revoke approval. ### Revoke your approval [#revoke-your-approval] Click **Revoke Approval** on the settlement detail page. Confirm the action and authenticate with your PIN or OTP. ### Verify asset return [#verify-asset-return] After the transaction confirms: * Your approval status returns to "Pending" * Your locked assets are returned to your wallet * The settlement approval count decreases ## What happens after revocation [#what-happens-after-revocation] * Your assets are immediately returned to your wallet * Your approval status returns to unapproved * The settlement approval count decreases * Other parties' approvals are unaffected * You can approve again later if you change your mind * Other parties may choose to cancel the settlement ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------- | ----------------------------------------------------------------------------------- | | Revoke button not visible | For HTLC: Settlement may be Armed. For local: Settlement may be executed or expired | | Revoke button disabled | You may not have an active approval on this settlement | | Transaction fails | Check that the settlement hasn't been executed, cancelled, or (for HTLC) Armed | ## Next steps [#next-steps] How to cancel a settlement entirely. How to approve your flows again after revoking. ## Related [#related] * [XvP settlement overview](/docs/user-guides/system-addons/xvp-settlement/overview) * [Cancel settlements](/docs/user-guides/system-addons/xvp-settlement/actions/cancel) # Withdraw expired Source: https://docs.settlemint.com/docs/user-guides/system-addons/xvp-settlement/actions/withdraw-expired Learn how to recover your assets from an expired XvP settlement. When the cutoff date passes without execution, original senders can withdraw their locked assets. When a settlement's cutoff date passes without execution, the settlement expires. Original senders can then withdraw their locked assets from the settlement contract. ## When you can withdraw [#when-you-can-withdraw] Withdrawal is available when: * The settlement has reached its cutoff date * The settlement was not executed before expiration * You are an original sender who approved and locked assets When withdrawal is triggered, only the original senders receive their locked assets back. Recipients receive nothing since no transfer occurred. However, **anyone can trigger** the withdrawal transaction - there are no caller restrictions. All refunds happen in a single atomic transaction. ## Prerequisites [#prerequisites] * The settlement is in **Expired** state * The settlement has not been cancelled * The withdrawal has not already been executed (one-time operation) ## Steps [#steps] ### Find expired settlements [#find-expired-settlements] Navigate to **Settlements** and filter by **Expired** status. Settlements that have passed their cutoff date without execution will appear here. ### Verify expiration [#verify-expiration] Open the settlement detail page and confirm: * Status shows **Expired** * The cutoff date has passed * You have locked assets in the settlement ### Withdraw your assets [#withdraw-your-assets] Click **Withdraw** on the settlement detail page. Review the confirmation showing: * The assets being returned * The amounts you locked * Your wallet address Click **Confirm** and authenticate with your PIN or OTP. ### Verify withdrawal [#verify-withdrawal] After the transaction confirms: * All locked assets are returned to their original senders in a single transaction * The settlement shows the withdrawal is complete * This withdrawal can only occur once for the entire settlement ## What happens during withdrawal [#what-happens-during-withdrawal] * **Single transaction refunds everyone**: All locked assets are transferred from the settlement contract back to their original senders atomically * **Anyone can trigger**: Any address can call the withdrawal function - there are no caller restrictions * **On-chain record**: The withdrawal transaction and all refunds are recorded on-chain ## Common expiration scenarios [#common-expiration-scenarios] ### Local settlement expiration [#local-settlement-expiration] A party never approved: * Settlement never reached Ready state * Cutoff date passed * All senders who approved can withdraw their locked assets ### HTLC settlement expiration [#htlc-settlement-expiration] Secret was never revealed: * Settlement reached Armed state (all approved) * No one revealed the secret before cutoff * All senders can withdraw their locked assets For HTLC settlements, expiration on one chain doesn't affect other chains. Check the settlement status on each chain and withdraw where applicable. ## Timing considerations [#timing-considerations] Withdraw promptly after expiration: * Assets remain in the contract until withdrawn * There's no deadline to withdraw, but prompt action is recommended * The settlement cannot be reactivated after expiration ## Troubleshooting [#troubleshooting] | Issue | Solution | | --------------------------- | ------------------------------------------------------------ | | Withdraw button not visible | Verify settlement has expired and you locked assets | | Withdraw button disabled | You may have already withdrawn, or didn't have locked assets | | "Settlement not expired" | Cutoff date hasn't passed yet - wait or check the date | | Transaction fails | Check if assets were already withdrawn | ## Next steps [#next-steps] Create a new settlement to retry the exchange. Return to the XvP Settlement overview. ## Related [#related] * [XvP settlement overview](/docs/user-guides/system-addons/xvp-settlement/overview) * [HTLC overview](/docs/user-guides/system-addons/xvp-settlement/htlc/overview) # HTLC explained Source: https://docs.settlemint.com/docs/user-guides/system-addons/xvp-settlement/htlc/htlc-explained Understand how Hash Time-Locked Contracts enable trustless cross-chain asset exchanges. Learn about hashlocks, timelocks, and why the mechanism is cryptographically secure. Hash Time-Locked Contracts (HTLCs) enable trustless asset exchanges across different blockchains. This page explains how the mechanism works and why it provides strong security guarantees. ## The two core components [#the-two-core-components] ### Hashlock: the cryptographic lock [#hashlock-the-cryptographic-lock] A hashlock is a cryptographic puzzle created by hashing a secret value: ``` hashlock = keccak256(secret) ``` Only someone who knows the original secret can "unlock" the hashlock. Think of it like a combination lock where only the secret holder knows the combination. **Key properties of the hashlock:** * No one can figure out the secret from the hashlock alone * The same secret always produces the same hashlock ### Timelock: the deadline [#timelock-the-deadline] A timelock is a deadline after which the settlement expires. It serves two purposes: 1. **Protection from stuck assets** - If something goes wrong, assets are not locked forever 2. **Coordination mechanism** - Creates a window during which the exchange must complete ## How HTLC ensures atomicity [#how-htlc-ensures-atomicity] The best way to understand HTLC security is through a concrete example. ### Example: Bank sells bonds to an Investor [#example-bank-sells-bonds-to-an-investor] **Setup:** * Investor has 100,000 USDC on Polygon * Bank has 1,000 BOND tokens on Ethereum * They want to exchange these assets atomically ### Step 1: Bank generates the secret [#step-1-bank-generates-the-secret] The Bank generates a random secret and computes the hashlock: * **Secret:** `0x7a8b9c...` (only the Bank knows this) * **Hashlock:** `keccak256(secret) = 0xdef456...` (shared with the Investor) ### Step 2: Bank creates settlement on Ethereum [#step-2-bank-creates-settlement-on-ethereum] | Field | Value | | -------- | ---------------------------- | | Flow | Bank → Investor: 1,000 BOND | | Hashlock | `0xdef456...` | | Timelock | January 15, 2025 at 12:00 PM | The Bank approves, locking 1,000 BOND into the settlement contract. ### Step 3: Investor creates settlement on Polygon [#step-3-investor-creates-settlement-on-polygon] The Bank shares only the hashlock (not the secret) with the Investor. | Field | Value | | -------- | ------------------------------------------------------- | | Flow | Investor → Bank: 100,000 USDC | | Hashlock | `0xdef456...` (same as Ethereum) | | Timelock | January 14, 2025 at 12:00 PM (24 hours before Ethereum) | The Investor approves, locking 100,000 USDC into the settlement contract. Both settlements are now in the **Armed** state, waiting for the secret. ### Step 4: The swap executes [#step-4-the-swap-executes] 1. **Bank reveals secret on Polygon** (January 13) - Settlement executes, Bank receives 100,000 USDC 2. **Secret is now public** - The Investor can see it on-chain 3. **Investor reveals secret on Ethereum** (January 13) - Settlement executes, Investor receives 1,000 BOND Both parties received their assets. The exchange was atomic across chains. ## Why HTLC is secure [#why-htlc-is-secure] ### The Investor cannot cheat [#the-investor-cannot-cheat] The Investor might try to claim the Bank's BOND on Ethereum without providing USDC. To do this, they would need to reveal the secret on Ethereum. **But the Investor only knows the hashlock, not the secret.** The hashlock is created using keccak256, a cryptographic hash function with these properties: 1. **One-way function** - Given the hashlock, it's computationally impossible to reverse-engineer the secret 2. **Brute force is impractical** - The secret is a 256-bit value with 2^256 possibilities. Even trying 1 trillion guesses per second would take longer than the age of the universe 3. **Collision resistance** - No other value produces the same hashlock The only way the Investor can execute the Ethereum settlement is to wait for the Bank to reveal the secret on Polygon first. ### The Bank cannot cheat [#the-bank-cannot-cheat] After revealing the secret on Polygon, the Bank cannot withdraw their BOND on Ethereum because: * The Ethereum timelock doesn't expire until January 15 * The Investor has until then to use the (now public) secret * The Bank's BOND remain locked in the settlement contract ### Timelock order is critical [#timelock-order-is-critical] The timelocks must be staggered correctly: **Correct order:** Polygon expires January 14, Ethereum expires January 15 The Investor has 24 hours after the secret is revealed on Polygon to use it on Ethereum. **If timelocks were reversed:** | Chain | Reversed Timelocks (UNSAFE) | Correct Timelocks | | -------------------------------- | --------------------------- | -------------------- | | Polygon (Investor → Bank: USDC) | January 15, 12:00 PM | January 14, 12:00 PM | | Ethereum (Bank → Investor: BOND) | January 14, 12:00 PM | January 15, 12:00 PM | **Setup (January 12):** * Bank creates settlement on Ethereum with January 14 timelock (WRONG - too early) * Investor creates settlement on Polygon with January 15 timelock * Both settlements are Armed **Bank waits strategically:** * Bank does NOT reveal the secret yet * Both settlements remain in Armed state **The Exploit begins (January 14 at 12:01 PM):** 1. Ethereum timelock expires (January 14 deadline passed) 2. Bank calls `withdraw()` on Ethereum settlement 3. Bank receives their 1,000 BOND back (settlement expired without executing) **The Exploit completes (January 14 at 12:30 PM):** 4. Polygon settlement is still valid (doesn't expire until January 15) 5. Bank reveals secret on Polygon 6. Bank receives 100,000 USDC from Polygon **Final State:** * Bank has: 100,000 USDC (claimed from Polygon) + 1,000 BOND (withdrawn from Ethereum) * Investor has: Lost 100,000 USDC, received nothing * Investor cannot claim BOND because Ethereum settlement already expired and was withdrawn **The key rule:** When you don't have the secret, make sure the cutoff date of the HTLC that you create is before the cutoff date of the HTLC leg that has the secret. This ensures you have time to use the secret after it's revealed, before your settlement expires. The settlement where you receive assets should have an earlier timelock than the settlement where you send assets. This ensures you have time to claim your assets after the secret is revealed. ## How XvP implements HTLC [#how-xvp-implements-htlc] XvP settlements incorporate HTLC with these features: | Feature | Implementation | | ------------------------- | ----------------------------------------------------------------------- | | **Secret generation** | UI can generate secrets, or you can provide your own | | **Hashlock verification** | Contract validates secret matches hashlock | | **Armed state** | All local senders must approve before secret can be revealed | | **Auto-execute** | When enabled, settlement executes automatically when secret is revealed | | **Cutoff date** | Enforces the timelock expiration | The `revealSecret()` function: 1. Validates the provided secret matches the hashlock 2. Confirms all local senders have approved 3. If auto-execute is enabled, executes all local flows atomically 4. Records the secret on-chain for transparency ## Failure scenarios [#failure-scenarios] | Scenario | What happens | | ---------------------------------- | ------------------------------------------------------------------------- | | Bank never reveals secret | Both settlements expire, all parties withdraw their locked assets | | Investor never approves on Polygon | Bank's BOND remain locked until Ethereum timelock expires | | Network congestion delays Investor | If Investor can't use secret before Ethereum expires, they lose the trade | To mitigate network delays, set timelocks with sufficient buffer (24-48 hours between chains is recommended). ## Further reading [#further-reading] Complete multi-party cross-chain settlement example. How to reveal the HTLC secret to trigger execution. ## Related [#related] * [HTLC overview](/docs/user-guides/system-addons/xvp-settlement/htlc/overview) * [HTLC walkthrough](/docs/user-guides/system-addons/xvp-settlement/htlc/walkthrough) # Overview Source: https://docs.settlemint.com/docs/user-guides/system-addons/xvp-settlement/htlc/overview HTLC settlements coordinate asset exchanges across different blockchains using hash time-locked contracts. Learn when to use HTLC and how cross-chain coordination works. HTLC (Hash Time-Locked Contract) settlements enable coordinated asset exchanges when tokens exist on different blockchains. A cryptographic hash links the settlements on each chain, ensuring either both sides execute or neither does. ## When to use HTLC settlements [#when-to-use-htlc-settlements] Use HTLC settlements when: * Assets being exchanged exist on different blockchains * You need trustless coordination without a central intermediary * Cross-chain atomic guarantees are required For exchanges where all assets are on the same chain, use [local settlements](/docs/user-guides/system-addons/xvp-settlement/local/overview) instead. ## How HTLC settlements work [#how-htlc-settlements-work] HTLC settlements use two linked components: 1. **Hashlock** - A cryptographic hash that links settlements across chains 2. **Timelock** - A deadline after which the settlement expires One party generates a secret and shares only the hash (hashlock) with others. Settlements on each chain are created with the same hashlock. When the secret is revealed on one chain, it becomes public and can be used to execute settlements on all linked chains. ### The "Armed" state [#the-armed-state] HTLC settlements have a unique state called "Armed" that occurs after all local senders approve but before the secret is revealed: | State | Description | | ------------- | ------------------------------------------------------- | | **Pending** | Settlement created, waiting for sender approvals | | **Armed** | All senders approved, waiting for secret to be revealed | | **Executed** | Secret revealed, all flows completed | | **Cancelled** | Settlement cancelled before armed | | **Expired** | Cutoff date passed before secret was revealed | ## External flows [#external-flows] HTLC settlements include two types of flows: * **Local flows** - Execute on the current chain when the settlement completes * **External flows** - Informational references to assets on other chains External flows don't execute locally. They serve as documentation of what should happen on the other chain. Each party is responsible for creating and managing the settlement on their respective chain. External flows help all parties understand the complete transaction structure. They don't transfer any assets. ## Auto-execute behavior [#auto-execute-behavior] When auto-execute is enabled (the default), the settlement executes automatically when: 1. All local senders have approved (settlement is Armed) 2. The correct secret is revealed If auto-execute is disabled, someone must manually trigger execution after the secret is revealed. ## Timelock coordination [#timelock-coordination] For cross-chain safety, timelocks must be staggered: * The settlement where the secret is revealed **first** should have a **later** timelock * This gives the other party time to use the revealed secret before their settlement expires For a detailed explanation of how hashlocks ensure security, see [HTLC explained](/docs/user-guides/system-addons/xvp-settlement/htlc/htlc-explained). ## Cancellation rules [#cancellation-rules] | Situation | Cancellation possible? | | ---------------------- | ---------------------------------------------------------------------------------- | | Before secret revealed | Yes - if ALL local participants (both senders and recipients) propose cancellation | | After secret revealed | No - cancellation is not allowed | HTLC settlements can be cancelled only if ALL local participants (both senders and recipients) send a cancellation vote before the secret is revealed. Once unanimous consent is reached, the settlement is cancelled and assets are refunded. See [how to cancel a settlement](/docs/user-guides/system-addons/xvp-settlement/actions/cancel). ## Example scenario [#example-scenario] **Cross-chain bond purchase:** * Investor has 100,000 USDC on Polygon * Bank has 1,000 BOND tokens on Ethereum * They want to swap atomically across chains The Bank creates the Ethereum settlement with a hashlock. The Investor creates a matching settlement on Polygon with the same hashlock. After both settlements are Armed, the Bank reveals the secret on Polygon to claim USDC. The Investor then uses that secret on Ethereum to claim BOND tokens. For a complete multi-party example with balance tracking, see the [HTLC walkthrough](/docs/user-guides/system-addons/xvp-settlement/htlc/walkthrough). ## Further reading [#further-reading] Explore how hash time-locked contracts ensure security. Complete multi-party cross-chain settlement example. How to reveal the HTLC secret to trigger execution. # Walkthrough Source: https://docs.settlemint.com/docs/user-guides/system-addons/xvp-settlement/htlc/walkthrough Follow a complete multi-party cross-chain settlement where a Builder sells tokenized real estate to two Buyers, with a Notary receiving fees across Ethereum and Polygon. This walkthrough demonstrates a complex multi-party HTLC settlement involving four parties across two blockchains. You'll see how XvP coordinates cross-chain exchanges and handles multiple flows. This walkthrough assumes you understand how HTLC works. If you're new to HTLCs, we recommend reading the [HTLC explanation](/docs/user-guides/system-addons/xvp-settlement/htlc/htlc-explained) first to learn about hashlocks, timelocks, and why the mechanism is cryptographically secure. ## Scenario: Tokenized real estate sale [#scenario-tokenized-real-estate-sale] ### The parties [#the-parties] | Party | Role | Chain | | ----------- | ------------------------------------ | -------- | | **Builder** | Sells property tokens | Ethereum | | **Buyer 1** | Purchases 50% ownership | Polygon | | **Buyer 2** | Purchases 50% ownership | Polygon | | **Notary** | Handles legal transfer, receives fee | Polygon | ### The deal [#the-deal] * Builder sells an apartment tokenized as 100 APT-101 tokens on Ethereum * Buyer 1 and Buyer 2 each purchase 50% (50 tokens each) * Total price: 500,000 USDC + 5,000 USDC notary fee | Item | Amount | | --------------- | ------------------------------- | | Property price | 500,000 USDC | | Buyer 1's share | 250,000 USDC + 2,500 notary fee | | Buyer 2's share | 250,000 USDC + 2,500 notary fee | | Notary fee | 5,000 USDC total | ## Settlement structures [#settlement-structures] ### Ethereum settlement (created by Builder) [#ethereum-settlement-created-by-builder] | Field | Value | | ---------------------------------- | -------------------------------------------- | | **Local flows** | | | Flow 1 | Builder → Buyer 1: 50 APT-101 | | Flow 2 | Builder → Buyer 2: 50 APT-101 | | **External flows** (informational) | | | Flow 3 | Buyer 1 → Builder: 250,000 USDC (on Polygon) | | Flow 4 | Buyer 2 → Builder: 250,000 USDC (on Polygon) | | Flow 5 | Buyer 1 → Notary: 2,500 USDC (on Polygon) | | Flow 6 | Buyer 2 → Notary: 2,500 USDC (on Polygon) | | **Hashlock** | `0xdef456...` | | **Timelock** | January 20, 2025 at 5:00 PM | | **Auto-execute** | Enabled | ### Polygon settlement (created by Buyer 1) [#polygon-settlement-created-by-buyer-1] | Field | Value | | ---------------------------------- | ------------------------------------------------------ | | **Local flows** | | | Flow 1 | Buyer 1 → Builder: 250,000 USDC | | Flow 2 | Buyer 2 → Builder: 250,000 USDC | | Flow 3 | Buyer 1 → Notary: 2,500 USDC | | Flow 4 | Buyer 2 → Notary: 2,500 USDC | | **External flows** (informational) | | | Flow 5 | Builder → Buyer 1: 50 APT-101 (on Ethereum) | | Flow 6 | Builder → Buyer 2: 50 APT-101 (on Ethereum) | | **Hashlock** | `0xdef456...` (same as Ethereum) | | **Timelock** | January 19, 2025 at 5:00 PM (24 hours before Ethereum) | | **Auto-execute** | Enabled | On Ethereum, only the Builder approves (sending tokens). On Polygon, only Buyer 1 and Buyer 2 approve (sending USDC). Recipients don't take action. ## Balance tracking [#balance-tracking] ### Ethereum chain [#ethereum-chain] | Step | Action | Builder | Buyer 1 | Buyer 2 | XvP Contract | | ------- | ---------------------------- | ----------- | ---------- | ---------- | ------------ | | Initial | - | 100 APT-101 | 0 | 0 | 0 | | Create | Builder creates settlement | 100 APT-101 | 0 | 0 | 0 | | Approve | Builder approves → **Armed** | 0 | 0 | 0 | 100 APT-101 | | Execute | Secret revealed | 0 | 50 APT-101 | 50 APT-101 | 0 | ### Polygon chain [#polygon-chain] | Step | Action | Builder | Notary | Buyer 1 | Buyer 2 | XvP Contract | | --------- | ---------------------------- | ------------ | ---------- | ------------ | ------------ | ------------ | | Initial | - | 0 | 0 | 252,500 USDC | 252,500 USDC | 0 | | Create | Buyer 1 creates settlement | 0 | 0 | 252,500 USDC | 252,500 USDC | 0 | | Approve 1 | Buyer 1 approves | 0 | 0 | 0 | 252,500 USDC | 252,500 USDC | | Approve 2 | Buyer 2 approves → **Armed** | 0 | 0 | 0 | 0 | 505,000 USDC | | Execute | Secret revealed | 500,000 USDC | 5,000 USDC | 0 | 0 | 0 | ## Step-by-step walkthrough [#step-by-step-walkthrough] ### Builder generates the secret [#builder-generates-the-secret] Builder opens the settlement wizard on Ethereum and generates a cryptographic secret: * **Secret:** `0x7a8b9c...` (Builder saves this securely) * **Hashlock:** `keccak256(secret) = 0xdef456...` The Builder keeps the secret private and shares only the hashlock with the Buyers. ### Builder creates settlement on Ethereum [#builder-creates-settlement-on-ethereum] Builder configures the settlement: 1. Adds two local flows (property tokens to each Buyer) 2. Adds four external flows (USDC from Buyers on Polygon) 3. Sets timelock to January 20, 2025 at 5:00 PM 4. Enables auto-execute 5. Submits the transaction ### Builder shares hashlock with Buyers [#builder-shares-hashlock-with-buyers] Builder shares the hashlock `0xdef456...` with Buyer 1 and Buyer 2. The Buyers can verify: * The settlement exists on Ethereum * It contains the correct flows (they'll receive property tokens) * The hashlock matches what they'll use on Polygon **Important:** Builder does NOT share the secret yet - only the hashlock. ### Buyer 1 creates settlement on Polygon [#buyer-1-creates-settlement-on-polygon] Buyer 1 creates the matching settlement on Polygon: 1. Adds four local flows (USDC from both Buyers to Builder and Notary) 2. Enters the same hashlock `0xdef456...` 3. Sets timelock to January 19, 2025 at 5:00 PM (24 hours before Ethereum) 4. Enables auto-execute 5. Submits the transaction ### Builder approves on Ethereum [#builder-approves-on-ethereum] Builder approves the Ethereum settlement: 1. Opens the settlement detail page 2. Clicks **Approve** 3. Confirms the transaction (transfers 100 APT-101 to the XvP contract) The Ethereum settlement enters **Armed** state (only one local sender: Builder). ### Buyers approve on Polygon [#buyers-approve-on-polygon] **Buyer 1 approves:** 1. Opens the Polygon settlement 2. Clicks **Approve** 3. Confirms (transfers 252,500 USDC to XvP contract) Settlement shows "1 of 2 approvals." **Buyer 2 approves:** 1. Opens the Polygon settlement 2. Clicks **Approve** 3. Confirms (transfers 252,500 USDC to XvP contract) The Polygon settlement enters **Armed** state. ### Builder reveals secret on Polygon [#builder-reveals-secret-on-polygon] Builder reveals the secret to claim the USDC: 1. Opens the Polygon settlement 2. Clicks **Reveal Secret** 3. Enters the secret `0x7a8b9c...` 4. Confirms the transaction **Auto-execute triggers immediately:** * Builder receives 500,000 USDC * Notary receives 5,000 USDC The Polygon settlement is now **Executed**. ### Secret is revealed on Ethereum [#secret-is-revealed-on-ethereum] Now that the secret is public (visible on Polygon), anyone can use it on Ethereum. **Option A:** Buyers find the secret on-chain in the Polygon transaction. **Option B:** Builder emails the secret to Buyers (it's already public, no security risk). One of the Buyers reveals the secret on Ethereum: 1. Opens the Ethereum settlement 2. Clicks **Reveal Secret** 3. Enters `0x7a8b9c...` 4. Confirms **Auto-execute triggers immediately:** * Buyer 1 receives 50 APT-101 * Buyer 2 receives 50 APT-101 The Ethereum settlement is now **Executed**. ## What if something goes wrong? [#what-if-something-goes-wrong] | Scenario | What happens | | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | | Secret never revealed before January 19 | Polygon settlement expires. Buyers withdraw their USDC. | | Secret revealed on Polygon but not Ethereum before January 20 | Buyers have until January 20 to reveal on Ethereum. After that, Builder can withdraw property tokens. | | One Buyer doesn't approve on Polygon | Settlement never reaches Armed state. After timelock, all approved parties can withdraw. | | Network congestion on Ethereum | Buyers should reveal the secret well before the January 20 deadline to account for delays. | ## Next steps [#next-steps] Detailed guide to using the settlement wizard. How to reveal the HTLC secret to trigger execution. How to recover assets from expired settlements. # Overview Source: https://docs.settlemint.com/docs/user-guides/system-addons/xvp-settlement/local/overview Local settlements execute all asset transfers atomically on a single blockchain. Learn when to use local settlements and how the approval workflow operates. Local settlements handle multi-party asset exchanges where all assets exist on the same blockchain. All flows execute together in a single transaction, eliminating counterparty risk. ## When to use local settlements [#when-to-use-local-settlements] Use local settlements when: * All assets being exchanged are on the same blockchain * No cross-chain coordination is required For exchanges involving assets on different chains, see [HTLC settlements](/docs/user-guides/system-addons/xvp-settlement/htlc/overview). Local settlement transaction monitoring ## How local settlements work [#how-local-settlements-work] 1. **Creator defines flows** - Specify each transfer: the asset, amount, sender, and recipient 2. **Senders approve** - Each party sending assets locks them into the settlement contract 3. **Settlement executes** - Executes automatically after all approvals (if auto-execute is enabled) or via manual trigger 4. **Assets distributed** - Recipients receive their assets in the same transaction Only parties who are sending assets need to approve. Recipients do not need to take any action. ## State diagram [#state-diagram] Local settlements follow this lifecycle: | State | Description | | ------------- | ------------------------------------------------ | | **Pending** | Settlement created, waiting for sender approvals | | **Ready** | All senders have approved, ready for execution | | **Executed** | All flows completed successfully | | **Cancelled** | Settlement cancelled by a participant | | **Expired** | Cutoff date passed before execution | ## Auto-execute behavior [#auto-execute-behavior] When auto-execute is enabled (the default), the settlement executes automatically as soon as all senders approve. No manual execution step is required. If auto-execute is disabled, anyone can manually trigger execution after all approvals are received. This allows for a final review before committing. ## Cancellation rules [#cancellation-rules] Any local participant (sender or recipient) can cancel the settlement at any point before execution. When cancelled, all locked assets are returned to their original senders. See [how to cancel a settlement](/docs/user-guides/system-addons/xvp-settlement/actions/cancel). ## Example scenario [#example-scenario] **Corporate bond trade with broker fee:** * Investment Bank holds 1,000 corporate bonds (CORP-BOND) * Asset Manager wants to purchase them for 100,000 USDC * Broker facilitated the trade and receives a 500 USDC fee The settlement includes three flows: 1. Investment Bank → Asset Manager: 1,000 CORP-BOND 2. Asset Manager → Investment Bank: 100,000 USDC 3. Asset Manager → Broker: 500 USDC The Broker creates the settlement defining all three flows. The Investment Bank approves flow 1, locking their bonds into the contract. The Asset Manager approves flows 2 and 3, locking 100,500 USDC total. Once all approvals are received, the settlement executes—the Asset Manager receives bonds, the Investment Bank receives payment, and the Broker receives their fee, all in the same transaction. For a complete walkthrough with balance tracking, see the [local settlement walkthrough](/docs/user-guides/system-addons/xvp-settlement/local/walkthrough). ## Further reading [#further-reading] Step-by-step example of a complete local settlement. How to create a new settlement using the wizard. How to approve your portion of a settlement. # Walkthrough Source: https://docs.settlemint.com/docs/user-guides/system-addons/xvp-settlement/local/walkthrough Follow a complete example of a local settlement where a Builder sells property tokens to a Buyer, with a Notary receiving a fee. This walkthrough demonstrates a complete local settlement from creation to execution. You'll see how assets flow between participants and what happens at each step. ## Scenario [#scenario] **Real estate trade with notary fee** * Builder holds 1,000 property tokens (PROP) * Buyer wants to purchase them for 100,000 EURD (Euro Deposit) * Notary facilitated the trade and receives a 500 EURD fee All assets exist on the same blockchain, making this a local settlement. ### Starting balances [#starting-balances] | Participant | PROP | EURD | | ------------ | ----- | ------- | | Builder | 1,000 | 0 | | Buyer | 0 | 100,500 | | Notary | 0 | 0 | | XvP Contract | 0 | 0 | ### Settlement structure [#settlement-structure] **Flows:** 1. Builder → Buyer: 1,000 PROP 2. Buyer → Builder: 100,000 EURD 3. Buyer → Notary: 500 EURD **Configuration:** * Cutoff date: 24 hours from creation * Auto-execute: Enabled XVP local settlement setup ## Step-by-step walkthrough [#step-by-step-walkthrough] ### Notary creates the settlement [#notary-creates-the-settlement] The Notary opens the settlement wizard and configures three flows: * Flow 1: Builder sends 1,000 PROP to Buyer * Flow 2: Buyer sends 100,000 EURD to Builder * Flow 3: Buyer sends 500 EURD to Notary The Notary sets a 24-hour cutoff and enables auto-execute. **Balances after creation:** | Participant | PROP | EURD | | ------------ | ----- | ------- | | Builder | 1,000 | 0 | | Buyer | 0 | 100,500 | | Notary | 0 | 0 | | XvP Contract | 0 | 0 | No assets have moved yet. The settlement is in **Pending** state. ### Builder approves [#builder-approves] The Builder reviews the settlement and clicks **Approve**. Their 1,000 PROP are transferred to the XvP contract as escrow. **Balances after Builder approves:** | Participant | PROP | EURD | | ------------ | ----- | ------- | | Builder | 0 | 0 | | Buyer | 0 | 100,500 | | Notary | 0 | 0 | | XvP Contract | 1,000 | 0 | The settlement shows "1 of 2 approvals" and remains in **Pending** state. ### Buyer approves [#buyer-approves] The Buyer receives notification of the pending settlement. They review all flows, confirm the amounts are correct, and click **Approve**. Their 100,500 EURD (100,000 for the property + 500 notary fee) are transferred to the XvP contract. Since auto-execute is enabled and this is the final approval, the settlement immediately executes. **Balances after Buyer approves (and auto-execute triggers):** | Participant | PROP | EURD | | ------------ | ----- | ------- | | Builder | 0 | 100,000 | | Buyer | 1,000 | 0 | | Notary | 0 | 500 | | XvP Contract | 0 | 0 | The settlement is now in **Executed** state. ## What happens during execution [#what-happens-during-execution] When all approvals are received, the settlement: 1. Validates all flows (sufficient balances, compliance checks) 2. Executes all transfers atomically in a single transaction 3. Distributes assets to recipients 4. Records the execution on-chain If any transfer fails (insufficient balance, compliance block, etc.), the entire settlement reverts and all assets remain in escrow. ## Alternative scenarios [#alternative-scenarios] ### Manual execution [#manual-execution] If auto-execute is disabled, after all senders approve: 1. Settlement enters **Ready** state 2. Anyone can click **Execute** to trigger the settlement 3. All flows execute atomically This allows for a final review before committing. ### Buyer doesn't approve [#buyer-doesnt-approve] If the Buyer doesn't approve before the cutoff date: 1. Settlement enters **Expired** state when the cutoff passes 2. The Builder can click **Withdraw** to reclaim their 1,000 PROP 3. The settlement cannot be executed after expiration ### Cancellation before execution [#cancellation-before-execution] If any local participant decides to cancel before execution: 1. The participant clicks **Cancel** 2. All locked assets are returned to their original senders 3. Settlement enters **Cancelled** state 4. No assets exchanged ## Summary [#summary] | Step | Action | Settlement state | Assets moved | | ---- | ------------------------- | ---------------- | ---------------------------------- | | 1 | Notary creates settlement | Pending | None | | 2 | Builder approves | Pending (1/2) | PROP → XvP | | 3 | Buyer approves | Executed | EURD → XvP, then atomic settlement | ## Next steps [#next-steps] Detailed guide to using the settlement wizard. See how cross-chain settlements work with hash time-locked contracts. # Overview Source: https://docs.settlemint.com/docs/user-guides/system-addons/xvp-settlement/overview XvP Settlement enables atomic multi-party asset exchanges where all transfers execute together or none execute at all. Learn about DvP, PvP, and cross-chain settlement scenarios. XvP Settlement provides atomic swap capabilities for tokenized assets. The "X" in XvP stands for the variable leg of the exchange, which can be either Delivery (DvP) or Payment (PvP). ## What is XvP Settlement? [#what-is-xvp-settlement] XvP Settlement eliminates counterparty risk by guaranteeing that either all parties receive their assets or no assets change hands. Single-chain settlements execute atomically in one transaction; cross-chain settlements use cryptographic locks to coordinate execution across chains. This "all-or-nothing" guarantee prevents partial settlement failures. **Settlement types:** * **DvP (Delivery vs Payment)** - Exchange assets for cash (e.g., bonds for USDC) * **PvP (Payment vs Payment)** - Exchange one cash asset for another (e.g., EUR stablecoin for USD stablecoin) XVP settlement addon configuration ## Common use cases [#common-use-cases] * **Bond trades** - Investor purchases corporate bonds with stablecoin * **Secondary market sales** - Holder sells tokenized equity to another investor * **Currency exchanges** - Treasury converts between stablecoin denominations * **Multi-party transactions** - Complex deals involving three or more parties * **Cross-chain swaps** - Exchange assets held on different blockchains ## Settlement types [#settlement-types] XvP supports two settlement modes based on where your assets are located: | Type | When to use | Coordination method | | --------- | ---------------------------- | -------------------------- | | **Local** | All assets on the same chain | Direct atomic execution | | **HTLC** | Assets on different chains | Hash time-locked contracts | ### Local settlements [#local-settlements] Use local settlements when all assets involved are on the same blockchain. The settlement executes atomically when all parties approve their flows. [Learn about local settlements](/docs/user-guides/system-addons/xvp-settlement/local/overview) ### HTLC (cross-chain) settlements [#htlc-cross-chain-settlements] Use HTLC settlements when assets are spread across different blockchains. A cryptographic hash links the settlements on each chain, ensuring coordinated execution. [Learn about HTLC settlements](/docs/user-guides/system-addons/xvp-settlement/htlc/overview) ## Key concepts [#key-concepts] Understanding these terms will help you work with XvP settlements: | Term | Definition | | ----------------- | ------------------------------------------------------------------------------------------------------- | | **Flow** | A single asset transfer from one address to another, including the token, amount, sender, and recipient | | **Local flow** | A flow that executes on the current chain | | **External flow** | A reference to a flow on another chain (informational only, does not execute locally) | | **Approval** | When a sender locks their assets into the settlement contract | | **Hashlock** | A cryptographic hash that links settlements across chains | | **Secret** | A value that, when revealed, proves ownership and triggers execution | | **Cutoff date** | The deadline after which the settlement expires and locked assets can be withdrawn | ## Settlement lifecycle [#settlement-lifecycle] Every settlement progresses through these states: * **Pending** - Settlement created, awaiting approvals from senders * **Approved** - All senders have approved (for HTLC, enters "Armed" state awaiting secret) * **Executed** - All flows completed successfully * **Cancelled** - Settlement cancelled before execution * **Expired** - Cutoff date passed without execution Local and HTLC settlements have slightly different state transitions. See the respective overview pages for detailed state diagrams. XVP REST API for programmatic settlement access ## Further reading [#further-reading] Single-chain atomic settlements with automatic execution. Cross-chain coordination using hash time-locked contracts. Step-by-step guide to creating your first settlement. # Yield schedule Source: https://docs.settlemint.com/docs/user-guides/system-addons/yield-schedule Configure scheduled yield, dividend, and interest entitlements for token holders. Holders claim completed-period yield from the configured payment source. The yield schedule addon configures returns for token holders. Completed periods determine entitlements. Holders claim available yield from the configured payment source. ## Common use cases [#common-use-cases] * **Bond coupons** - Scheduled interest entitlements that bondholders claim for completed periods * **Equity dividends** - Company profit entitlements that shareholders claim from the payment source * **Fund distributions** - Periodic investment gain entitlements that unit holders claim * **Deposit interest** - Completed-period interest entitlements that deposit token holders claim ## How it works [#how-it-works] The yield schedule addon: 1. **Tracks holder balances** at snapshot dates 2. **Calculates entitlements** based on holdings and yield rates 3. **Uses the configured payment source** to fund holder claims 4. **Records holder claims** on-chain for audit trails Yield schedule configuration in asset pricing ## Key features [#key-features] | Feature | Description | | ------------------------------ | -------------------------------------------------------------------- | | **Snapshot-based calculation** | Captures holder balances at specific dates to determine entitlements | | **Flexible schedules** | Supports configured period schedules | | **Pro-rata calculation** | Calculates each holder's claimable share based on their balance | | **Payment token flexibility** | Fund yield claims in the same asset or a different payment token | ## Funding yield claims [#funding-yield-claims] Yield funding depends on the schedule type. Legacy fixed-yield schedules use the schedule's denomination asset balance. Fixed-treasury yield schedules use the configured treasury address as the payment source. For fixed-treasury yield schedules, configure the treasury address when applicable. The treasury address must hold the required denomination asset tokens before a payment date. Users can top up the schedule balance or feature treasury by entering a denomination asset amount and confirming the transfer with wallet verification when the action is available. The app shows the current balance and the new balance before submission. ## Bond maturity funding [#bond-maturity-funding] For bonds, the asset details show the denomination funding status needed for maturity: * Current denomination holdings * Required denomination amount * Covered percentage * Shortfall to mature, when the required denomination amount exceeds the available balance Use these values to top up the reserve before or at maturity so the bond has enough denomination asset tokens available for repayment. ## Prerequisites [#prerequisites] * Asset must be configured with yield schedule addon enabled * Governance permission to configure a fixed-treasury yield treasury address when applicable * Treasury or denomination asset source must hold enough payment tokens for scheduled payments or bond maturity * Wallet verification for funding transfers and treasury updates * Withdrawals require the yield schedule address to match the authorized token ## Further reading [#further-reading] For technical details on yield schedule implementation, see the [architecture documentation](/docs/architecture/components/token-features). # Create users Source: https://docs.settlemint.com/docs/user-guides/user-management/create-users Directly create user accounts with automatic wallet and identity setup for faster onboarding. The "Create User" feature allows administrators to directly create user accounts with automatic wallet and identity setup. Unlike invitations, this creates accounts immediately without sending emails, making it ideal for demo environments, testing scenarios, and users who don't need platform access. ## Prerequisites [#prerequisites] * **Identity Manager** role or equivalent permissions * Completed platform setup with organization created * Understanding of when to use direct creation vs. invitations ## When to use Create Users [#when-to-use-create-users] ### Recommended scenarios [#recommended-scenarios] * **Demo preparation** - Setting up users quickly for demonstrations * **Testing environments** - Creating test accounts with known credentials * **Passive token holders** - Users who only receive tokens and don't perform on-chain actions * **Training scenarios** - Preparing accounts for workshops or training ### When to use invitations instead [#when-to-use-invitations-instead] * **Production environments** - Users should control their own credentials * **Security-sensitive setups** - Users should generate their own wallets * **Active participants** - Users who need to mint, transfer, or manage roles require login access to create API keys Creating users directly does not send automatic emails. The administrator manages all account details and can share credentials manually as needed. ## User creation process [#user-creation-process] ### Navigate to user management [#navigate-to-user-management] Go to **Participants** > **Users** in the sidebar. You'll see a list of all users with a **Create user** button. Users page with Create user button ### Enter user information [#enter-user-information] Click **"Create user"** to open the creation form. Enter the user's display name and email address, then click **Save user**. ### Authenticate identity creation [#authenticate-identity-creation] Enter your PIN when prompted. This is required because creating a user involves blockchain transactions to generate the user's wallet (EOA) and deploy their on-chain identity contract. ### User creation complete [#user-creation-complete] Once the blockchain transactions confirm, the user account is created with an automatically generated wallet address and deployed on-chain identity. The user appears in the Users table with "Active" status. ## Post-creation steps [#post-creation-steps] ### For administrators [#for-administrators] 1. **Provide platform access** - Share the platform URL with users so they can access via "Forgot password" 2. **Enroll in registries** - Add users to relevant asset registries for token eligibility 3. **Assign administrative roles** - Grant platform admin roles if needed 4. **Verify identity** - Complete KYC verification for compliance if required User passwords are randomly generated and unknown to administrators. Users must use the "Forgot password" flow to set their own secure password. ### For users [#for-users] The created user needs to: 1. **Navigate to platform login** 2. **Click "Forgot password"** or "Reset password" 3. **Enter their email address** 4. **Check email** for reset instructions 5. **Set new password** following link in email 6. **Log in** with email and new password ## What gets created automatically [#what-gets-created-automatically] ### Account setup [#account-setup] * User account with email as username * Secure random password (must be reset) ### Blockchain components [#blockchain-components] * **Wallet address** - New externally owned account (EOA) * **Private key storage** - Securely encrypted * **On-chain identity** - Smart contract deployed * **Identity registration** - Registered as pending in identity registry ## Limitations and considerations [#limitations-and-considerations] * **Password reset required** - Extra step for user access * **No wallet security** - Account security only, no PIN setup * **Skipped onboarding** - Users bypass standard invitation flow * **No email notifications** - No clean invitation emails for users * **Pre-onboarded state** - User already "onboarded" without self-service setup ## Comparison with Invite Users [#comparison-with-invite-users] | Aspect | Create User | Invite User | | ---------------- | ---------------- | -------------------------- | | **Speed** | Immediate | Depends on user response | | **Security** | Admin-managed | User-controlled | | **Best for** | Demos, testing | Production, external users | | **Organization** | Your org only | Can create own orgs | | **Setup** | Automatic | User-guided | | **Control** | Admin controlled | User controlled | ## Troubleshooting [#troubleshooting] | Issue | Solution | | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | Create User button not available | • Verify you have Identity Manager role
• Check you're in the correct organization
• Ensure platform is properly initialized | | User creation fails | • Verify email address is unique
• Check required fields are completed
• Ensure sufficient gas for identity deployment | | User cannot log in | • Confirm they've reset password using email link
• Verify email address is correct
• Check account creation was successful | | Wallet/identity issues | • Identity contract deployment may take time
• Check blockchain transaction confirmations
• Verify gas was sufficient for deployment | ## Related guides [#related-guides] * [Invite Users](/docs/user-guides/user-management/invite-users) - Alternative invitation-based approach * [User Onboarding](/docs/user-guides/user-management/user-onboarding) - Understanding the user experience * [Add Administrators](/docs/user-guides/platform-setup/add-admins) - Assigning roles after creation # Invite users Source: https://docs.settlemint.com/docs/user-guides/user-management/invite-users Send invitations for team members and external partners to join your platform. Invite users to join your organization. The invitation system supports both email delivery and manual link sharing for flexibility in different environments. For faster demo setup, consider using [Create Users](/docs/user-guides/user-management/create-users) which directly creates accounts with automatic wallet and identity setup. Use invitations for production environments and external partners. ## Prerequisites [#prerequisites] * **Identity Manager** role * Completed platform setup with organization created * Email configuration (optional, for automated delivery) ## Invitation process [#invitation-process] ### Navigate to invitations [#navigate-to-invitations] Go to **Participants** > **Invitations** in the sidebar. You'll see a list of pending and accepted invitations. Invitations dashboard ### Create new invitation [#create-new-invitation] Click **Invite user** to open the invitation form. Enter the email address of the person you want to invite and click **Next**. ### Copy invitation link [#copy-invitation-link] After entering the email, the invitation link is displayed. Copy the link to share manually, or rely on the automatic email delivery (if email is configured). The link should look like: `/auth/invitation?invitationId=&email=` **Important**: The recipient should open the link in an incognito window (or while signed out). The link will redirect them to a **dedicated sign-up page** where: * The email field is **pre-filled** with the invited email address and **cannot be changed** * They only need to enter their name and password * After signing up, the invitation is automatically accepted and they're redirected to the onboarding wizard Invitation link display ### Track invitation status [#track-invitation-status] Monitor invitation progress in the dashboard. Status shows **Pending** until the user accepts and completes signup, then changes to **Accepted**. ## What happens after acceptance [#what-happens-after-acceptance] When users accept invitations: 1. **Account creation** - They set up credentials 2. **Organization join** - Automatically added to your org 3. **Wallet setup** - Create blockchain wallet with security 4. **Identity creation** - Deploy on-chain identity 5. **Profile completion** - Add personal information See [User Onboarding](/docs/user-guides/user-management/user-onboarding) for the complete new user experience. ## Troubleshooting [#troubleshooting] | Issue | Solution | | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Invitation email not received | • Check spam/junk folders
• Verify email address spelling
• Use manual link sharing
• Check email service configuration | | User cannot accept invitation | • Check invitation link wasn't modified
• Ensure platform is accessible
• Try generating new invitation | | "You are not the recipient" | • Sign out and sign up with the exact invited email
• Open the link in an incognito/private window
• Click "Sign out" on the error page to switch accounts | | Cannot create invitation | • Verify you have Identity Manager role
• Check platform is properly initialized
• Ensure valid email format | ## Related guides [#related-guides] * [User Onboarding](/docs/user-guides/user-management/user-onboarding) - New user experience * [Add Administrators](/docs/user-guides/platform-setup/add-admins) - Grant roles after joining * [Verify KYC](/docs/user-guides/compliance/verify-kyc) - Issue KYC verifications for users # Participants hub Source: https://docs.settlemint.com/docs/user-guides/user-management/participants-hub Use the Participants hub to review users, entities, identity registration status, KYC evidence, holdings, activity, and security signals. The **Participants** hub brings together the people and on-chain entities that can interact with assets on the platform. Use it to review who has platform credentials, which identities and contract accounts are registered, and what evidence is available before taking an identity, KYC, or security action. The hub is split into two primary lists: * **Users**: participants with platform credentials. The list shows each user's name, email, wallet address, participant type, linked identity address, and registration status. * **Entities**: participants without login credentials, such as assets, vaults, smart accounts, or other system identities. The list shows each entity's name, contract address, identity address, entity type, and registration status. Both lists open a detail workspace when you select a row. ## Users list [#users-list] Go to **Participants** > **Users** to review participants with login credentials. The users table supports search, filters, export, and column visibility controls. A user can appear as an admin, trusted issuer, or investor based on their platform and on-chain roles. If your role can create users, the page also shows **Create user**. See [Create users](/docs/user-guides/user-management/create-users) for the direct account creation workflow. If you do not have permission to view users, the page shows an access-restricted state instead of the table. ## User detail workspace [#user-detail-workspace] Open a user from the table to review the user's participant record. The header shows the user's display name, participant type, registration status, wallet address, and identity address when available. The user detail workspace can include these cards and drill-down views: * **Basic info**: account details such as display name, email, wallet, identity address, and recent account activity. * **Security**: two-factor authentication and active-session information. This card is available to roles that can manage user security. * **Verifications**: identity claims associated with the user's on-chain identity. * **Information**: KYC profile status, approved name when available, pending-update state, and open action requests. * **Holdings**: assets held by the user's wallet. The card uses the selected user's wallet as its balance scope. It is available to platform administrators and to roles that can read users in the current organization when the wallet belongs to a user in that organization. A wallet outside the active organization remains restricted. * **Activity log**: recent blockchain activity for the user's wallet, with a full activity view for deeper review. The workspace can also show a setup checklist for identity and verification work. The checklist appears for identity and verification roles when the user still needs follow-up, such as identity registration, KYC verification, updated KYC information, or a re-issued KYC verification. ## Entities list [#entities-list] Go to **Participants** > **Entities** to review participants that do not log in as platform users. This includes on-chain identities linked to assets, vaults, smart accounts, contracts, wallets, or system actors. The entities table supports filters, export, and column visibility controls. It is sorted by recent activity by default. If you do not have permission to view entities, the page shows an access-restricted state instead of the table. ## Entity detail workspace [#entity-detail-workspace] Open an entity from the table to review its on-chain participant record. The header shows the entity name, registration status, contract address, and identity address when available. The entity detail workspace can include: * **Basic info**: identity and contract-account details. * **Verifications**: identity claims associated with the entity. * **Activity log**: recent blockchain activity for the entity's account, with a full activity view for deeper review. * **Data feeds**: feed configuration and publishing state for entities that participate in data-feed workflows. ## Permissions and access [#permissions-and-access] Participants views are permission-aware: * Users can be listed and opened by platform roles that can read user records. * Entities can be listed by identity managers, system managers, and claim issuers. Entity detail workspaces can be opened by identity managers and claim issuers. * Creating users, registering identities, reviewing KYC evidence, and managing user security require narrower permissions. When an action is unavailable, check that you are in the expected organization and that your account has the relevant identity, verification, or system-management role. ## Related guides [#related-guides] * [Invite users](/docs/user-guides/user-management/invite-users) * [Create users](/docs/user-guides/user-management/create-users) * [Provide KYC data](/docs/user-guides/user-management/provide-kyc-data) * [Register user](/docs/user-guides/user-management/register-user) * [Manage KYC data](/docs/user-guides/compliance/manage-kyc-data) * [Verify KYC](/docs/user-guides/compliance/verify-kyc) # Provide KYC data Source: https://docs.settlemint.com/docs/user-guides/user-management/provide-kyc-data Complete your KYC profile with personal information and supporting documents to enable identity verification and asset transactions. This guide explains how to complete your KYC (Know Your Customer) profile with personal information and supporting documents. Completing your KYC profile is required before an Identity Manager can verify your identity and enable you to receive assets. ## Prerequisites [#prerequisites] * Completed platform onboarding with wallet and on-chain identity * Access to identity documents for upload ## About KYC profiles [#about-kyc-profiles] Your KYC profile stores the personal information needed for identity verification. The platform uses a versioned workflow: | Status | Description | What you can do | | ---------------- | --------------------------------------- | ------------------------------------- | | **Draft** | Profile in progress, not yet submitted | Edit fields, upload documents, submit | | **Submitted** | Awaiting review by Identity Manager | View only | | **Under review** | Identity Manager is reviewing your data | View only | | **Approved** | Identity verified, ready for assets | Create new version if data changes | | **Rejected** | Changes required before approval | View feedback, create corrected draft | ## Completing your KYC profile [#completing-your-kyc-profile] ### Access your profile [#access-your-profile] Navigate to your account settings by clicking your avatar in the top right corner and selecting **Profile**. Find the **KYC Information** card. If you skipped the personal information step during onboarding, you can still start your KYC profile here by saving your first draft. KYC profile card ### Fill in personal information [#fill-in-personal-information] Complete the following fields: * **First name** and **Last name**: Your legal name as it appears on identity documents * **Date of birth**: Your birth date for age verification * **Country**: Your country of citizenship or legal residence * **Residency status**: Select your tax residency classification (Resident, Non-resident, Dual resident, or Unknown) * **National ID**: Your government-issued identification number (passport, national ID, or equivalent) ### Save your draft [#save-your-draft] Click **Save draft** to store your information. You can return and edit your profile at any time while it remains in draft status. ### Upload supporting documents [#upload-supporting-documents] After saving, the document upload section becomes available. Upload documents that verify your identity: * Government-issued ID (passport, national ID card, driver's license) * Proof of address (utility bill, bank statement) * Additional documents as required by your organization Each document shows its type, filename, and upload date. You can delete documents and upload replacements while in draft status. ### Submit for review [#submit-for-review] When your profile is complete, click **Submit for review**. A confirmation dialog explains that you cannot edit your submission while under review. Click **Submit** to confirm. Your status changes to **Submitted** and an Identity Manager will review your information. ## After submission [#after-submission] Once submitted, you cannot edit your KYC profile until the review is complete. The status banner at the top of your KYC card shows the current state: * **Submitted**: Your data is queued for review * **Under review**: An Identity Manager is actively reviewing your information * **Approved**: Your identity has been verified * **Rejected**: Changes are required (see feedback below) ## Handling rejections [#handling-rejections] If your submission is rejected, the status banner displays the reason provided by the Identity Manager. Common reasons include: * Document quality issues (blurry, incomplete, or expired) * Mismatched information between fields and documents * Missing required documents * Incomplete information ### Making corrections [#making-corrections] ### Review the feedback [#review-the-feedback] Read the rejection reason or update request carefully. The feedback box shows exactly what needs to be corrected. ### Create a new draft [#create-a-new-draft] When your profile is rejected or an update is requested, edit any field to create a new draft version. The platform preserves your version history. ### Make corrections [#make-corrections] Update the fields or documents as specified in the feedback. Address all issues mentioned before resubmitting. ### Submit again [#submit-again] Click **Submit for review** to send your corrected profile for another review cycle. ## Viewing version history [#viewing-version-history] Your KYC profile maintains a complete version history. Click **View history** to see all previous submissions, including: * Version number and submission date * Status of each version * Reviewer notes (for approved versions) * Rejection reasons (for rejected versions) This history helps you track changes and understand the review timeline. ## Best practices [#best-practices] ### Document quality [#document-quality] * Ensure documents are clear, legible, and complete * Upload color scans or photos when possible * Verify all pages of multi-page documents are included * Check that documents are not expired ### Information accuracy [#information-accuracy] * Use your legal name exactly as it appears on documents * Double-check all dates and numbers before submitting * Ensure country and residency status match your documentation ### Timely response [#timely-response] * Respond promptly to rejection feedback * Contact your Identity Manager if requirements are unclear * Keep your profile updated if personal information changes ## Troubleshooting [#troubleshooting] | Issue | Solution | | ----------------------------- | ------------------------------------------------------------------------------------------------------------ | | Cannot edit submitted profile | Wait for review completion. Profiles under review cannot be edited. | | Document upload fails | Check file size and format. Supported formats include PDF, PNG, and JPEG. | | Cannot submit profile | Ensure all required fields are completed and you have at least one document uploaded. | | Repeated rejections | Contact your Identity Manager for clarification on requirements. | | Cannot see KYC card | Verify you completed onboarding with an on-chain identity. Contact your administrator if the issue persists. | ## Related guides [#related-guides] * [User Onboarding](/docs/user-guides/user-management/user-onboarding) - Complete platform onboarding first * [Register User](/docs/user-guides/user-management/register-user) - Learn about user registration in the identity registry * [Verify KYC](/docs/user-guides/compliance/verify-kyc) - Understand how on-chain verifications are issued after KYC approval * [Manage KYC Data](/docs/user-guides/compliance/manage-kyc-data) - Learn about the KYC submission review process # Recover a user's identity Source: https://docs.settlemint.com/docs/user-guides/user-management/recover-user-identity Use the console identity recovery workflow when a user loses access to the wallet linked to their identity. Use identity recovery when a user loses access to the wallet linked to their on-chain identity. The console workflow checks whether the user can be recovered, shows the affected identity and token balances, then creates replacement wallet and identity records when an Identity manager confirms the recovery. Identity recovery is for lost or compromised wallet access. It is not a general transfer tool, a custody service, or a replacement for KYC review. ## Before you start [#before-you-start] You need: * An administrator account with the Identity manager role. * The user record for the person whose wallet access is lost. * Enough operational evidence to confirm that recovery is the right action for that user. Recovery links the user to a replacement identity path. Trusted issuers may still need to issue fresh claims for the recovered identity before the user can pass compliance checks again. ## Recover the user [#recover-the-user] ### Open the user's security details [#open-the-users-security-details] Go to the user's profile from the participants or user management area, then open the security section. The recovery action appears only for Identity managers and only when the selected user has a wallet. ### Start identity recovery [#start-identity-recovery] Click **Recover identity**. DALP loads a recovery preview before it allows execution. The preview shows the user, the wallet being recovered, the current identity status, token balances that may need recovery, and any blocking reasons. ### Resolve blockers before continuing [#resolve-blockers-before-continuing] If the preview says the user cannot be recovered, stop and resolve the listed blocker. Move balances manually only when your operating procedure explicitly requires a different workflow. ### Review the impact summary [#review-the-impact-summary] Confirm the impact before execution. The workflow can create a new wallet, create a replacement identity, revoke active sessions, reset MFA, and attempt token recovery for balances shown in the preview. ### Confirm and execute [#confirm-and-execute] Enter the confirmation text shown in the dialog, then submit the recovery. After execution starts, the wizard locks forward progress and shows recovery phases instead of letting the operator navigate back into the decision steps. ### Wait for completion [#wait-for-completion] Watch the progress list until the workflow completes, fails, or completes with token-level failures. Token recovery progress shows how many affected token balances have been recovered out of the total balances in scope. ## After recovery [#after-recovery] Check the final summary before closing the dialog. It can show the replacement wallet, the replacement identity, and the number of recovered token balances. If recovery completes with token-level failures, treat the identity recovery as partially successful. The user is linked to the replacement identity path, but the listed token balances still need operator follow-up. Common failure reasons include a paused token, missing custodian permissions, no recoverable tokens, an RPC error, or an unknown token recovery error. Recheck the user's identity and claim state after the workflow completes. If the user needs KYC, accreditation, or other trusted-issuer claims, issue or request those claims on the recovered identity before the user resumes regulated activity. ## Related [#related] * [Identity recovery API](/docs/developer-guides/api-integration/identity-recovery) * [User onboarding](/docs/user-guides/user-management/user-onboarding) * [Create users](/docs/user-guides/user-management/create-users) * [Verify KYC](/docs/user-guides/compliance/verify-kyc) # Register user Source: https://docs.settlemint.com/docs/user-guides/user-management/register-user Register users in the identity registry for compliance tracking and token eligibility. This guide explains how to register users in the identity registry - a required step for compliance tracking and token eligibility. ## Prerequisites [#prerequisites] * **Identity Manager** role or equivalent permissions * Users must have completed onboarding with on-chain identity * Understanding of user jurisdiction requirements ## About identity registry [#about-identity-registry] The identity registry is an on-chain system that: * Tracks all verified identities for your organization * Links wallet addresses to identity contracts * Enforces jurisdictional rules ### Identity states [#identity-states] | State | Description | Can receive assets | | ------------------------ | ---------------------------------- | ------------------ | | **Pending Registration** | Onboarded but not in registry | No | | **Registered** | In registry, awaiting verification | Depends on rules | | **Verified** | Has required verifications | Yes | ## Registering users [#registering-users] ### Access user management [#access-user-management] Navigate to **Participants** > **Users**. You'll see all users with their name, email, EOA (wallet address), type, identity address, and status. ### Select unregistered user [#select-unregistered-user] Find and click on a user you want to register. The user details page shows their information, identity contract details, current registration status, and available actions. Unregistered user profile ### Initiate registration [#initiate-registration] Click **Register Identity** (top right button) to begin the registration process. This opens the registration flow. ### Select jurisdiction [#select-jurisdiction] Choose the user's country from the dropdown. Jurisdiction determines applicable regulatory requirements, compliance rules, investment limits, and verification requirements. Country selection can be used in compliance rules but doesn't automatically apply specific rules. Choose based on user's legal residence or incorporation, not temporary location. ### Complete registration [#complete-registration] Click **Continue** after selecting jurisdiction, review the summary, then click **Register identity**. Enter your PIN when prompted and wait for blockchain confirmation. The registry contract adds the user's identity to the registry and links it to their wallet address. ### Verify registration [#verify-registration] After transaction confirmation, the user status changes to **"Registered"**. The user is now in the identity registry and ready to receive verifications. ## Best practices [#best-practices] ### Enrollment timing [#enrollment-timing] * Enroll users promptly after onboarding completion * Verify user information before enrollment * Document enrollment decisions for audit ### Jurisdiction selection [#jurisdiction-selection] * Use user's legal residence or incorporation * Verify jurisdiction through documentation * Consider tax and regulatory implications * Update if user's status changes ### Security considerations [#security-considerations] * Verify identity before enrollment * Monitor for duplicate enrollments * Audit enrollment activities * Maintain enrollment records ## Integration with compliance [#integration-with-compliance] ### Token transfers [#token-transfers] * Enrolled identities can receive assets (with proper verifications) * Non-enrolled identities are blocked from asset transactions * Different assets may have different enrollment requirements ### Compliance modules [#compliance-modules] * Identity verification module checks registry enrollment * Transfer restrictions enforce enrollment requirements * Compliance reporting tracks enrolled vs. non-enrolled users ### Verification process [#verification-process] * Enrollment is prerequisite for issuing verifications * Verifications can only be added to enrolled identities * Multiple verifications can be issued per enrolled identity ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | | Cannot see Register button | • Verify you have Identity Manager role
• Check user has completed onboarding
• Ensure user has on-chain identity | | Registration transaction fails | • Check wallet has sufficient gas
• Verify correct user is selected
• Ensure network connectivity | | User still shows as Pending | • Wait for blockchain confirmation
• Refresh the page
• Check transaction was successful | | Cannot select jurisdiction | • Ensure valid country is available
• Check compliance settings
• Verify no existing registration | ## Related guides [#related-guides] * [User Onboarding](/docs/user-guides/user-management/user-onboarding) - Understanding identity creation * [Provide KYC Data](/docs/user-guides/user-management/provide-kyc-data) - How users complete their KYC profile * [Manage KYC Data](/docs/user-guides/compliance/manage-kyc-data) - Review KYC submissions before registration * [Verify KYC](/docs/user-guides/compliance/verify-kyc) - Issue verifications after enrollment * [Configure Trusted Issuers](/docs/user-guides/compliance/configure-trusted-issuers) - Set up verification framework * [Compliance Overview](/docs/user-guides/compliance/overview) - Complete compliance reference # User onboarding Source: https://docs.settlemint.com/docs/user-guides/user-management/user-onboarding Understanding the new user experience when joining the platform. Users join the platform through an invitation flow or an administrator-created account. The path determines how the user sets a password, secures a wallet, creates an on-chain identity, and completes profile or KYC data. ## User onboarding paths [#user-onboarding-paths] There are two main ways users join an existing platform: ### 1. Invited users (invitation flow) [#1-invited-users-invitation-flow] * Receive invitation link via email or direct sharing * Complete guided onboarding process * Join existing organization or create their own ### 2. Created users (admin-created) [#2-created-users-admin-created] * Accounts created directly by administrators * **Simplified onboarding** - only need to set up wallet security * Must use "Forgot password" to access platform first The first administrator follows a different process covered in [First Admin Setup](/docs/user-guides/platform-setup/first-admin-setup). This guide focuses on standard user onboarding flows. New user login page for first-time onboarding ## Complete onboarding flow (invited users) [#complete-onboarding-flow-invited-users] This is the full onboarding experience for users who receive invitations. ### Receive invitation [#receive-invitation] Users receive an invitation via email with a secure link or a direct link shared by an administrator. The invitation contains platform welcome information and organization details. ### Sign up [#sign-up] Clicking the invitation link takes users to the signup form where they enter their email address (must match the invitation) and choose a strong password. ### Accept invitation [#accept-invitation] After signing up, users see the organization they're joining and can confirm acceptance to proceed. ### Wallet creation [#wallet-creation] The platform automatically generates a blockchain wallet with a unique address. Users see their wallet address (0x...) and an explanation of its purpose. ### Security setup [#security-setup] Users must secure their wallet by entering a 6-digit PIN and confirming it. This PIN is used for all blockchain transactions. Users must remember their chosen security method. This cannot be easily reset without backup codes. ### Backup codes [#backup-codes] The system generates recovery codes that users must save securely. These are the only way to recover wallet access if the PIN is lost. Backup codes display ### On-chain identity [#on-chain-identity] Users click **Create my on-chain ID**, enter their PIN, and wait for transaction confirmation. The transaction deploys an identity contract linked to the user's wallet so trusted issuers can add verifications. ### Profile completion [#profile-completion] Users can optionally add KYC profile information (name, date of birth, country of residence, residency status, national ID) or skip and complete later. ## Simplified onboarding (created users) [#simplified-onboarding-created-users] Users created directly by administrators have a much simpler experience: ### Access platform [#access-platform] Navigate to the platform URL provided by the administrator and click **"Forgot password"** (since passwords are randomly generated). Enter the email address used during creation, check email for the reset link, and set a new password. ### Set up wallet security [#set-up-wallet-security] After logging in, users must secure their wallet by setting up a 6-digit PIN and saving the backup codes. Store the recovery codes offline because they are required for wallet recovery. **That's it!** Created users are immediately ready to use the platform since their account, wallet, and identity were pre-configured by the administrator. ## Post-onboarding status [#post-onboarding-status] After completing onboarding, next steps depend on the user's role: ### Administrative users [#administrative-users] * Joined organization ✓ * Wallet and identity created ✓ * **Need role assignment** - Must be granted administrative roles to perform duties * Cannot access admin functions until permissions granted * See [Add Administrators](/docs/user-guides/platform-setup/add-admins) for role assignment ### Investors/asset holders [#investorsasset-holders] * Joined organization ✓ * Wallet and identity created ✓ * **Need KYC completion** - Must finish KYC profile for verification * Cannot receive assets until KYC verified * See [Verify KYC](/docs/user-guides/compliance/verify-kyc) for verification process ## Troubleshooting [#troubleshooting] | Issue | Solution | | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | "Email already exists" | • User may have existing account
• Check for previous invitations
• Try password reset | | "Invalid invitation" | • Send fresh invitation
• Check link integrity
• Verify invitation was created correctly | | "Transaction failed" during wallet/identity setup | • Insufficient gas (public chains)
• Network connectivity issues
• Browser compatibility | | "Lost PIN" | • Use backup codes
• Contact administrator
• Use [identity recovery](/docs/user-guides/user-management/recover-user-identity) when wallet access cannot be restored | | "Cannot create identity" | • System contracts must be deployed
• Check user completed wallet setup
• Verify network status | ## Next steps after onboarding [#next-steps-after-onboarding] For administrators: * [Add Administrators](/docs/user-guides/platform-setup/add-admins) - Assign roles * [Verify KYC](/docs/user-guides/compliance/verify-kyc) - Issue KYC verifications For new users: * Complete profile information * Await role assignments * Review platform documentation * Begin platform activities ## Related guides [#related-guides] * [Invite Users](/docs/user-guides/user-management/invite-users) - Sending invitations * [Create Users](/docs/user-guides/user-management/create-users) - Direct user creation (bypasses onboarding) * [Recover a user's identity](/docs/user-guides/user-management/recover-user-identity) - Operator workflow for lost or compromised wallet access * [Provide KYC Data](/docs/user-guides/user-management/provide-kyc-data) - Complete KYC profile after onboarding * [Register User](/docs/user-guides/user-management/register-user) - Register users in identity registry (next step after onboarding) * [First Admin Setup](/docs/user-guides/platform-setup/first-admin-setup) - Platform initialization * [Add Administrators](/docs/user-guides/platform-setup/add-admins) - Role assignment # access-control.role-granted.provisional Source: https://docs.settlemint.com/docs/events/access-control-role-granted-provisional A role was granted on the access-control manager; the grant is provisional until the indexer reaches the configured reorg depth. A role was granted on the access-control manager; the grant is provisional until the indexer reaches the configured reorg depth. ## Delivery contract [#delivery-contract] | Field | Value | | ------------------------------- | ----------------------------------------------- | | Event type | `access-control.role-granted.provisional` | | Version | `1` | | Lifecycle state | `provisional` | | Counter-signed receipt required | `false` | | SDK type | `Webhook.AccessControlRoleGrantedProvisionalV1` | Related references: * `idxr_access_control_role_member` ## Payload schema [#payload-schema] ```json { "type": "object", "properties": { "accessManagerAddress": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "accountAddress": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "blockNumber": { "type": "string" }, "chainId": { "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991 }, "roleId": { "type": "string" }, "sender": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "systemAddress": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "transactionHash": { "type": "string", "pattern": "^0x[a-fA-F0-9]{64}$" } }, "required": [ "accessManagerAddress", "accountAddress", "blockNumber", "chainId", "roleId", "sender", "systemAddress", "transactionHash" ], "additionalProperties": false } ``` ## TypeScript SDK example [#typescript-sdk-example] ```typescript import { verifyWebhook, type Webhook } from "@settlemint/dalp-sdk"; const result = verifyWebhook({ rawBody, headers, secret: process.env.DALP_WEBHOOK_SECRET!, }); if (!result.ok) { throw new Error(`Webhook verification failed: ${result.code}`); } if (result.event.type === "access-control.role-granted.provisional") { const event: Webhook.Event<"access-control.role-granted.provisional"> = result.event; console.log(event.payload); } ``` ## curl example [#curl-example] ```bash curl -X POST https://consumer.example.com/dalp/webhooks \ -H "content-type: application/json" \ -H "webhook-id: evt_docs_access_control_role_granted_provisional_001" \ -H "webhook-timestamp: 1778112000" \ -H "webhook-signature: v1,docs-example-signature" \ --data '{"evt_id":"evt_docs_access_control_role_granted_provisional_001","type":"access-control.role-granted.provisional","version":1,"lifecycle_state":"provisional","request":{"idempotency_key":"idem_01JZP7R5W8M9N0P1Q2R3S4T5"},"related":{"idxr_access_control_role_member":"idxr_access_control_role_member_example"},"payload":{"accessManagerAddress":"0x1111111111111111111111111111111111111111","accountAddress":"0x2222222222222222222222222222222222222222","blockNumber":"18445201","chainId":537001,"roleId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","sender":"0x3333333333333333333333333333333333333333","systemAddress":"0x4444444444444444444444444444444444444444","transactionHash":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}}' ``` ## Version history [#version-history] * `v1`: Initial registry entry for `access-control.role-granted.provisional`. ## Deprecation [#deprecation] This event type is not deprecated. ## Manifest [#manifest] The machine-readable AsyncAPI entry is published in the [DALP events manifest](/.well-known/dalp-events.json). # access-control.role-revoked.provisional Source: https://docs.settlemint.com/docs/events/access-control-role-revoked-provisional A role was revoked on the access-control manager; the revocation is provisional until the indexer reaches the configured reorg depth. A role was revoked on the access-control manager; the revocation is provisional until the indexer reaches the configured reorg depth. ## Delivery contract [#delivery-contract] | Field | Value | | ------------------------------- | ----------------------------------------------- | | Event type | `access-control.role-revoked.provisional` | | Version | `1` | | Lifecycle state | `provisional` | | Counter-signed receipt required | `false` | | SDK type | `Webhook.AccessControlRoleRevokedProvisionalV1` | Related references: * `idxr_access_control_role_member` ## Payload schema [#payload-schema] ```json { "type": "object", "properties": { "accessManagerAddress": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "accountAddress": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "blockNumber": { "type": "string" }, "chainId": { "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991 }, "roleId": { "type": "string" }, "sender": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "systemAddress": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "transactionHash": { "type": "string", "pattern": "^0x[a-fA-F0-9]{64}$" } }, "required": [ "accessManagerAddress", "accountAddress", "blockNumber", "chainId", "roleId", "sender", "systemAddress", "transactionHash" ], "additionalProperties": false } ``` ## TypeScript SDK example [#typescript-sdk-example] ```typescript import { verifyWebhook, type Webhook } from "@settlemint/dalp-sdk"; const result = verifyWebhook({ rawBody, headers, secret: process.env.DALP_WEBHOOK_SECRET!, }); if (!result.ok) { throw new Error(`Webhook verification failed: ${result.code}`); } if (result.event.type === "access-control.role-revoked.provisional") { const event: Webhook.Event<"access-control.role-revoked.provisional"> = result.event; console.log(event.payload); } ``` ## curl example [#curl-example] ```bash curl -X POST https://consumer.example.com/dalp/webhooks \ -H "content-type: application/json" \ -H "webhook-id: evt_docs_access_control_role_revoked_provisional_001" \ -H "webhook-timestamp: 1778112000" \ -H "webhook-signature: v1,docs-example-signature" \ --data '{"evt_id":"evt_docs_access_control_role_revoked_provisional_001","type":"access-control.role-revoked.provisional","version":1,"lifecycle_state":"provisional","request":{"idempotency_key":"idem_01JZP7R5W8M9N0P1Q2R3S4T5"},"related":{"idxr_access_control_role_member":"idxr_access_control_role_member_example"},"payload":{"accessManagerAddress":"0x1111111111111111111111111111111111111111","accountAddress":"0x2222222222222222222222222222222222222222","blockNumber":"18445201","chainId":537001,"roleId":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","sender":"0x3333333333333333333333333333333333333333","systemAddress":"0x4444444444444444444444444444444444444444","transactionHash":"0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"}}' ``` ## Version history [#version-history] * `v1`: Initial registry entry for `access-control.role-revoked.provisional`. ## Deprecation [#deprecation] This event type is not deprecated. ## Manifest [#manifest] The machine-readable AsyncAPI entry is published in the [DALP events manifest](/.well-known/dalp-events.json). # asset.issuance.provisional Source: https://docs.settlemint.com/docs/events/asset-issuance-provisional An asset issuance transaction entered the reorg window and is provisionally visible. An asset issuance transaction entered the reorg window and is provisionally visible. ## Delivery contract [#delivery-contract] | Field | Value | | ------------------------------- | ------------------------------------ | | Event type | `asset.issuance.provisional` | | Version | `1` | | Lifecycle state | `provisional` | | Counter-signed receipt required | `false` | | SDK type | `Webhook.AssetIssuanceProvisionalV1` | Related references: * `idxr_asset` * `idxr_token` ## Payload schema [#payload-schema] ```json { "type": "object", "properties": { "assetAddress": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "chainId": { "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991 }, "issuer": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "quantity": { "type": "string" }, "transactionHash": { "type": "string", "pattern": "^0x[a-fA-F0-9]{64}$" } }, "required": ["assetAddress", "chainId", "issuer", "quantity", "transactionHash"], "additionalProperties": false } ``` ## TypeScript SDK example [#typescript-sdk-example] ```typescript import { verifyWebhook, type Webhook } from "@settlemint/dalp-sdk"; const result = verifyWebhook({ rawBody, headers, secret: process.env.DALP_WEBHOOK_SECRET!, }); if (!result.ok) { throw new Error(`Webhook verification failed: ${result.code}`); } if (result.event.type === "asset.issuance.provisional") { const event: Webhook.Event<"asset.issuance.provisional"> = result.event; console.log(event.payload); } ``` ## curl example [#curl-example] ```bash curl -X POST https://consumer.example.com/dalp/webhooks \ -H "content-type: application/json" \ -H "webhook-id: evt_docs_asset_issuance_provisional_001" \ -H "webhook-timestamp: 1778112000" \ -H "webhook-signature: v1,docs-example-signature" \ --data '{"evt_id":"evt_docs_asset_issuance_provisional_001","type":"asset.issuance.provisional","version":1,"lifecycle_state":"provisional","request":{"idempotency_key":"idem_01JZP7R5W8M9N0P1Q2R3S4T5"},"related":{"idxr_asset":"idxr_asset_example","idxr_token":"idxr_token_example"},"payload":{"assetAddress":"0x1111111111111111111111111111111111111111","chainId":537001,"issuer":"0x2222222222222222222222222222222222222222","quantity":"1000000000000000000000","transactionHash":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}' ``` ## Version history [#version-history] * `v1`: Initial registry entry for `asset.issuance.provisional`. ## Deprecation [#deprecation] This event type is not deprecated. ## Manifest [#manifest] The machine-readable AsyncAPI entry is published in the [DALP events manifest](/.well-known/dalp-events.json). # compliance.freeze.recalled Source: https://docs.settlemint.com/docs/events/compliance-freeze-recalled A compliance officer recalled an earlier compliance freeze event. A compliance officer recalled an earlier compliance freeze event. ## Delivery contract [#delivery-contract] | Field | Value | | ------------------------------- | ------------------------------------ | | Event type | `compliance.freeze.recalled` | | Version | `1` | | Lifecycle state | `recalled` | | Counter-signed receipt required | `true` | | SDK type | `Webhook.ComplianceFreezeRecalledV1` | Related references: * `idxr_compliance_status` * `idxr_token` ## Payload schema [#payload-schema] ```json { "type": "object", "properties": { "tokenAddress": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "chainId": { "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991 }, "subjectAddress": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "recalledBy": { "type": "string" }, "reasonCode": { "type": "string" }, "supersedes": { "type": "string", "pattern": "^evt_[a-zA-Z0-9]+$" } }, "required": ["tokenAddress", "chainId", "subjectAddress", "recalledBy", "reasonCode", "supersedes"], "additionalProperties": false } ``` ## TypeScript SDK example [#typescript-sdk-example] ```typescript import { verifyWebhook, type Webhook } from "@settlemint/dalp-sdk"; const result = verifyWebhook({ rawBody, headers, secret: process.env.DALP_WEBHOOK_SECRET!, }); if (!result.ok) { throw new Error(`Webhook verification failed: ${result.code}`); } if (result.event.type === "compliance.freeze.recalled") { const event: Webhook.Event<"compliance.freeze.recalled"> = result.event; console.log(event.payload); } ``` ## curl example [#curl-example] ```bash curl -X POST https://consumer.example.com/dalp/webhooks \ -H "content-type: application/json" \ -H "webhook-id: evt_docs_compliance_freeze_recalled_001" \ -H "webhook-timestamp: 1778112000" \ -H "webhook-signature: v1,docs-example-signature" \ --data '{"evt_id":"evt_docs_compliance_freeze_recalled_001","type":"compliance.freeze.recalled","version":1,"lifecycle_state":"recalled","supersedes":"evt_docs_original_001","request":{"idempotency_key":"idem_01JZP7R5W8M9N0P1Q2R3S4T5"},"related":{"idxr_compliance_status":"idxr_compliance_status_example","idxr_token":"idxr_token_example"},"payload":{"tokenAddress":"0x3333333333333333333333333333333333333333","chainId":537001,"subjectAddress":"0x4444444444444444444444444444444444444444","recalledBy":"compliance-ops","reasonCode":"manual-review-overturned","supersedes":"evt_recalledFreeze001"}}' ``` ## Version history [#version-history] * `v1`: Initial registry entry for `compliance.freeze.recalled`. ## Deprecation [#deprecation] This event type is not deprecated. ## Manifest [#manifest] The machine-readable AsyncAPI entry is published in the [DALP events manifest](/.well-known/dalp-events.json). # Idempotency and on-chain outcome Source: https://docs.settlemint.com/docs/events/idempotency-and-on-chain-outcome Reconcile cached DAPI mutation responses with webhook-only on-chain outcomes, including final, retracted, and recalled events. A cached DAPI mutation response reflects the synchronous mutation outcome only; it does not track on-chain fate. The originating transaction can still be dropped from the mempool, reorged out, or recalled for compliance reasons. Use webhooks to observe that on-chain status. This rule is the boundary between request idempotency and chain reconciliation. Treat an HTTP `201` returned from an idempotency cache as "the mutation was accepted and returned this response", not "the transaction is final forever". ## Reconciliation pattern [#reconciliation-pattern] Subscribe to the lifecycle events that can prove or revise an outcome: * `*.final` for events that passed the configured reorg depth. * `*.retracted` for events invalidated by a reorg. * `*.recalled` for operator-authorized compliance recall. Dedupe every delivery by the `webhook-id` header. Correlate webhook events back to the originating API call with `event.request.idempotency_key` when that value is present. If a later retracted or recalled event names an earlier event through `supersedes`, treat the later event as authoritative over the cached DAPI response. ## Retract and recall handling [#retract-and-recall-handling] When a `*.retracted` event arrives, mark the superseded event as no longer valid and reverse any external side effects that assumed finality. When a `*.recalled` event arrives, apply the recall even if the original delivery failed or is unknown to your system; unknown `supersedes` references should be ignored after audit logging. If both retraction and recall apply to the same original event, recall wins. Your consumer should keep the recall as the final superseding signal. Keep this [retract and recall handling](#retract-and-recall-handling) branch close to your idempotency correlation code so cached DAPI responses cannot mask later chain outcomes. ## Typed SDK supersedes accessor [#typed-sdk-supersedes-accessor] The typed SDK exposes `supersedes` only on `retracted` and `recalled` lifecycle states. That lets TypeScript consumers branch on lifecycle state before applying authoritative-over-cached-response behavior. ```typescript import { verifyWebhook, type Webhook } from "@settlemint/dalp-sdk"; const result = verifyWebhook({ rawBody, headers, secret }); if (!result.ok) throw new Error(result.code); const event: Webhook.Event = result.event; if (event.lifecycle_state === "retracted" || event.lifecycle_state === "recalled") { await markSuperseded({ originalEventId: event.supersedes, supersedingEventId: event.evt_id, idempotencyKey: event.request?.idempotency_key, }); } ``` `verifyWebhook` returns the discriminated event union after signature verification, so reconciliation code can narrow on `event.type` or `event.lifecycle_state`. # identity.registration.provisional Source: https://docs.settlemint.com/docs/events/identity-registration-provisional An identity was joined to an identity registry; the linkage is provisional until the indexer reaches the configured reorg depth. An identity was joined to an identity registry; the linkage is provisional until the indexer reaches the configured reorg depth. ## Delivery contract [#delivery-contract] | Field | Value | | ------------------------------- | ------------------------------------------- | | Event type | `identity.registration.provisional` | | Version | `1` | | Lifecycle state | `provisional` | | Counter-signed receipt required | `false` | | SDK type | `Webhook.IdentityRegistrationProvisionalV1` | Related references: * `idxr_account` * `idxr_identity` ## Payload schema [#payload-schema] ```json { "type": "object", "properties": { "accountAddress": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "blockNumber": { "type": "string" }, "chainId": { "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991 }, "country": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "identityAddress": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "registryAddress": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "status": { "type": "string" }, "transactionHash": { "type": "string", "pattern": "^0x[a-fA-F0-9]{64}$" } }, "required": [ "accountAddress", "blockNumber", "chainId", "country", "identityAddress", "registryAddress", "status", "transactionHash" ], "additionalProperties": false } ``` ## TypeScript SDK example [#typescript-sdk-example] ```typescript import { verifyWebhook, type Webhook } from "@settlemint/dalp-sdk"; const result = verifyWebhook({ rawBody, headers, secret: process.env.DALP_WEBHOOK_SECRET!, }); if (!result.ok) { throw new Error(`Webhook verification failed: ${result.code}`); } if (result.event.type === "identity.registration.provisional") { const event: Webhook.Event<"identity.registration.provisional"> = result.event; console.log(event.payload); } ``` ## curl example [#curl-example] ```bash curl -X POST https://consumer.example.com/dalp/webhooks \ -H "content-type: application/json" \ -H "webhook-id: evt_docs_identity_registration_provisional_001" \ -H "webhook-timestamp: 1778112000" \ -H "webhook-signature: v1,docs-example-signature" \ --data '{"evt_id":"evt_docs_identity_registration_provisional_001","type":"identity.registration.provisional","version":1,"lifecycle_state":"provisional","request":{"idempotency_key":"idem_01JZP7R5W8M9N0P1Q2R3S4T5"},"related":{"idxr_account":"idxr_account_example","idxr_identity":"idxr_identity_example"},"payload":{"accountAddress":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","blockNumber":"18445201","chainId":537001,"country":56,"identityAddress":"0xcccccccccccccccccccccccccccccccccccccccc","registryAddress":"0xdddddddddddddddddddddddddddddddddddddddd","status":"ACTIVE","transactionHash":"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"}}' ``` ## Version history [#version-history] * `v1`: Initial registry entry for `identity.registration.provisional`. ## Deprecation [#deprecation] This event type is not deprecated. ## Manifest [#manifest] The machine-readable AsyncAPI entry is published in the [DALP events manifest](/.well-known/dalp-events.json). # Webhook events Source: https://docs.settlemint.com/docs/events Subscribe to DALP lifecycle events with AsyncAPI, signed webhooks, idempotent delivery, and typed SDK verification. DALP events are the asynchronous integration surface for on-chain lifecycle changes. The [AsyncAPI manifest](/.well-known/dalp-events.json) is the canonical machine-readable registry, while these pages explain the event shapes and reconciliation rules used by external systems. ## Lifecycle [#lifecycle] If both retraction and recall apply to the same original event, recall wins. The retracted event is dropped and the recall remains the authoritative superseding event. ## Shape [#shape] DALP webhook payloads use a thin envelope around a typed event payload. The envelope carries delivery identity, lifecycle state, related DALP references, replay metadata, and the original request idempotency key when the event originated from a DAPI mutation. The event payload carries the domain fields specific to the event type. ### Thin vs fat payloads [#thin-vs-fat-payloads] Endpoints default to the **thin** payload shape: per-event personally-identifiable fields (wallet addresses, country codes, reason codes) are omitted from the signed payload before delivery. Subscribing to thin events keeps the platform aligned with EDPB Guidelines 02/2025 on blockchain personal data — consumers receive enough context to dereference indexed state via the API but no first-party PII leaves DALP without explicit consent. Endpoints can opt into the **fat** shape via `PATCH /v2/webhooks/{id}` with a `fatEventsAcknowledgment.fieldsAcknowledged` body listing every `.` the operator confirms is acceptable to deliver. The dapp's Switch-to-fat dialog computes the required field list from the endpoint's subscriptions and the central PII map; sending an incomplete list returns `WEBHOOK_FAT_ACK_INCOMPLETE` (DALP-0517). Operator-configured `payload_redactor_config` rules layer on top of the thin baseline — they can remove additional fields from fat payloads or hash leaf values inside thin payloads, but cannot re-add fields the thin shape strips. ## Signing [#signing] Every delivery includes `webhook-id`, `webhook-timestamp`, and `webhook-signature` headers. Verify the exact raw body bytes with `verifyWebhook` from `@settlemint/dalp-sdk` before parsing the event. ## Idempotency [#idempotency] DAPI `Idempotency-Key` caching and webhook reconciliation are intentionally decoupled. Read [Idempotency and on-chain outcome](/docs/events/idempotency-and-on-chain-outcome) before treating any cached mutation response as proof of final on-chain state. # settlement.transfer.retracted Source: https://docs.settlemint.com/docs/events/settlement-transfer-retracted A settlement transfer event was retracted because a reorg invalidated the source log. A settlement transfer event was retracted because a reorg invalidated the source log. ## Delivery contract [#delivery-contract] | Field | Value | | ------------------------------- | --------------------------------------- | | Event type | `settlement.transfer.retracted` | | Version | `1` | | Lifecycle state | `retracted` | | Counter-signed receipt required | `true` | | SDK type | `Webhook.SettlementTransferRetractedV1` | Related references: * `idxr_settlement` * `idxr_transfer` ## Payload schema [#payload-schema] ```json { "type": "object", "properties": { "settlementId": { "type": "string" }, "chainId": { "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991 }, "transactionHash": { "type": "string", "pattern": "^0x[a-fA-F0-9]{64}$" }, "supersedes": { "type": "string", "pattern": "^evt_[a-zA-Z0-9]+$" }, "reorgBlockNumber": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 } }, "required": ["settlementId", "chainId", "transactionHash", "supersedes", "reorgBlockNumber"], "additionalProperties": false } ``` ## TypeScript SDK example [#typescript-sdk-example] ```typescript import { verifyWebhook, type Webhook } from "@settlemint/dalp-sdk"; const result = verifyWebhook({ rawBody, headers, secret: process.env.DALP_WEBHOOK_SECRET!, }); if (!result.ok) { throw new Error(`Webhook verification failed: ${result.code}`); } if (result.event.type === "settlement.transfer.retracted") { const event: Webhook.Event<"settlement.transfer.retracted"> = result.event; console.log(event.payload); } ``` ## curl example [#curl-example] ```bash curl -X POST https://consumer.example.com/dalp/webhooks \ -H "content-type: application/json" \ -H "webhook-id: evt_docs_settlement_transfer_retracted_001" \ -H "webhook-timestamp: 1778112000" \ -H "webhook-signature: v1,docs-example-signature" \ --data '{"evt_id":"evt_docs_settlement_transfer_retracted_001","type":"settlement.transfer.retracted","version":1,"lifecycle_state":"retracted","supersedes":"evt_docs_original_001","request":{"idempotency_key":"idem_01JZP7R5W8M9N0P1Q2R3S4T5"},"related":{"idxr_settlement":"idxr_settlement_example","idxr_transfer":"idxr_transfer_example"},"payload":{"settlementId":"stl_01JZP7R5W8M9N0P1Q2R3S4T5V6","chainId":537001,"transactionHash":"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","supersedes":"evt_finalSettlement001","reorgBlockNumber":18445201}}' ``` ## Version history [#version-history] * `v1`: Initial registry entry for `settlement.transfer.retracted`. ## Deprecation [#deprecation] This event type is not deprecated. ## Manifest [#manifest] The machine-readable AsyncAPI entry is published in the [DALP events manifest](/.well-known/dalp-events.json). # token.transfer.final Source: https://docs.settlemint.com/docs/events/token-transfer-final A token transfer passed the configured reorg depth and is final. A token transfer passed the configured reorg depth and is final. ## Delivery contract [#delivery-contract] | Field | Value | | ------------------------------- | ------------------------------ | | Event type | `token.transfer.final` | | Version | `1` | | Lifecycle state | `final` | | Counter-signed receipt required | `true` | | SDK type | `Webhook.TokenTransferFinalV1` | Related references: * `idxr_token` * `idxr_transfer` ## Payload schema [#payload-schema] ```json { "type": "object", "properties": { "tokenAddress": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "chainId": { "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991 }, "from": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "to": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "amount": { "type": "string" }, "transactionHash": { "type": "string", "pattern": "^0x[a-fA-F0-9]{64}$" }, "blockNumber": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }, "logIndex": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 } }, "required": ["tokenAddress", "chainId", "from", "to", "amount", "transactionHash", "blockNumber", "logIndex"], "additionalProperties": false } ``` ## TypeScript SDK example [#typescript-sdk-example] ```typescript import { verifyWebhook, type Webhook } from "@settlemint/dalp-sdk"; const result = verifyWebhook({ rawBody, headers, secret: process.env.DALP_WEBHOOK_SECRET!, }); if (!result.ok) { throw new Error(`Webhook verification failed: ${result.code}`); } if (result.event.type === "token.transfer.final") { const event: Webhook.Event<"token.transfer.final"> = result.event; console.log(event.payload); } ``` ## curl example [#curl-example] ```bash curl -X POST https://consumer.example.com/dalp/webhooks \ -H "content-type: application/json" \ -H "webhook-id: evt_docs_token_transfer_final_001" \ -H "webhook-timestamp: 1778112000" \ -H "webhook-signature: v1,docs-example-signature" \ --data '{"evt_id":"evt_docs_token_transfer_final_001","type":"token.transfer.final","version":1,"lifecycle_state":"final","request":{"idempotency_key":"idem_01JZP7R5W8M9N0P1Q2R3S4T5"},"related":{"idxr_token":"idxr_token_example","idxr_transfer":"idxr_transfer_example"},"payload":{"tokenAddress":"0x5555555555555555555555555555555555555555","chainId":537001,"from":"0x6666666666666666666666666666666666666666","to":"0x7777777777777777777777777777777777777777","amount":"250000000000000000000","transactionHash":"0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc","blockNumber":18445240,"logIndex":7}}' ``` ## Version history [#version-history] * `v1`: Initial registry entry for `token.transfer.final`. ## Deprecation [#deprecation] This event type is not deprecated. ## Manifest [#manifest] The machine-readable AsyncAPI entry is published in the [DALP events manifest](/.well-known/dalp-events.json). # token.transfer.pending Source: https://docs.settlemint.com/docs/events/token-transfer-pending A token transfer was observed before block inclusion and may later finalize or retract. A token transfer was observed before block inclusion and may later finalize or retract. ## Delivery contract [#delivery-contract] | Field | Value | | ------------------------------- | -------------------------------- | | Event type | `token.transfer.pending` | | Version | `1` | | Lifecycle state | `pending` | | Counter-signed receipt required | `false` | | SDK type | `Webhook.TokenTransferPendingV1` | Related references: * `idxr_token` * `mempool_transfer` ## Payload schema [#payload-schema] ```json { "type": "object", "properties": { "tokenAddress": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "chainId": { "type": "integer", "exclusiveMinimum": 0, "maximum": 9007199254740991 }, "from": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "to": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" }, "amount": { "type": "string" }, "transactionHash": { "type": "string", "pattern": "^0x[a-fA-F0-9]{64}$" } }, "required": ["tokenAddress", "chainId", "from", "to", "amount", "transactionHash"], "additionalProperties": false } ``` ## TypeScript SDK example [#typescript-sdk-example] ```typescript import { verifyWebhook, type Webhook } from "@settlemint/dalp-sdk"; const result = verifyWebhook({ rawBody, headers, secret: process.env.DALP_WEBHOOK_SECRET!, }); if (!result.ok) { throw new Error(`Webhook verification failed: ${result.code}`); } if (result.event.type === "token.transfer.pending") { const event: Webhook.Event<"token.transfer.pending"> = result.event; console.log(event.payload); } ``` ## curl example [#curl-example] ```bash curl -X POST https://consumer.example.com/dalp/webhooks \ -H "content-type: application/json" \ -H "webhook-id: evt_docs_token_transfer_pending_001" \ -H "webhook-timestamp: 1778112000" \ -H "webhook-signature: v1,docs-example-signature" \ --data '{"evt_id":"evt_docs_token_transfer_pending_001","type":"token.transfer.pending","version":1,"lifecycle_state":"pending","request":{"idempotency_key":"idem_01JZP7R5W8M9N0P1Q2R3S4T5"},"related":{"idxr_token":"idxr_token_example","mempool_transfer":"mempool_transfer_example"},"payload":{"tokenAddress":"0x8888888888888888888888888888888888888888","chainId":537001,"from":"0x9999999999999999999999999999999999999999","to":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","amount":"1000000000000000000","transactionHash":"0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"}}' ``` ## Version history [#version-history] * `v1`: Initial registry entry for `token.transfer.pending`. ## Deprecation [#deprecation] This event type is not deprecated. ## Manifest [#manifest] The machine-readable AsyncAPI entry is published in the [DALP events manifest](/.well-known/dalp-events.json).