# Signing Flow

Source: https://docs.settlemint.com/docs/architects/flows/signing-flow
How DALP moves an EVM transaction from a verified user or API request through
compliance simulation, custody signing, provider policy review, and broadcast.




## System context [#system-context]

The signing flow is the shared path for every DALP operation that writes to an EVM network. DALP first verifies that the caller can submit the request. The Workflow Engine then builds the transaction payload, the SMART Protocol compliance model runs before signing, and the configured custody provider signs before broadcast.

DALP routes each write through one of two paths: a directly signed EVM transaction from an externally owned account (EOA), or a smart wallet that submits a UserOperation. The transaction queue chooses the path. Request controls, compliance checks, and custody policies apply on both. If you are integrating or auditing the smart wallet path, see [Advanced accounts route](#advanced-accounts-route) below; the rest of this page covers the EOA path.

<Mermaid
  chart="`
classDiagram
  class RequestControls {
    authenticates caller
    checks authorization
    verifies wallet evidence
  }
  class ExecutionEngine {
    builds transaction payload
    estimates gas and nonce
    resumes pending approvals
  }
  class SMARTProtocol {
    simulates compliance checks
    enforces on-chain rules
  }
  class UnifiedSigner {
    routes signing request
    normalises provider responses
  }
  class CustodyProvider {
    applies provider policy
    returns signature or approval state
  }
  class ChainGateway {
    broadcasts signed transaction
    returns receipt
  }

  RequestControls --> ExecutionEngine : accepted request
  ExecutionEngine --> SMARTProtocol : pre-check
  ExecutionEngine --> UnifiedSigner : sign request
  UnifiedSigner --> CustodyProvider : provider policy
  UnifiedSigner --> ChainGateway : signed transaction
  ChainGateway --> SMARTProtocol : on-chain execution

`"
/>

See also: [Wallet verification](/docs/compliance-security/security/wallet-verification) | [Key Management](/docs/architects/components/infrastructure/key-management) | [Transaction Signer](/docs/architects/components/infrastructure/transaction-signer) | [Identity & compliance](/docs/compliance-security/security/identity-compliance)

***

## Overview [#overview]

Standard DALP transactions pass through three checks before reaching the blockchain:

1. Request controls: DALP authenticates the caller, checks authorization, and applies wallet verification when a user session submits an operation that needs a blockchain signature. API-key integrations authenticate through the API key path instead of user wallet verification.
2. On-chain compliance: the SMART Protocol verifies identity claims, transfer restrictions, and supply limits via simulation.
3. Custodian policy: the configured custody provider applies operational controls such as amount limits, multi-party approval, or hardware-backed quorum before signing.

For transfers, minting, redemption, and similar lifecycle operations, all applicable request controls, compliance checks, and custody policies must pass before the operation completes. Custodian-only operations such as forced transfers use the custodian role and bypass standard transfer-compliance checks by design. Treat them as exceptional servicing controls, not as ordinary transfer flows. To locate where your operation failed, use the control model table and failure-modes table below.

## End-to-end sequence [#end-to-end-sequence]

<Mermaid
  chart="`sequenceDiagram
  autonumber
  participant App as DALP Application<br/>(API / Console)
  participant Guard as Request Controls<br/>(auth, permission, wallet verification)
  participant Engine as DALP Workflow Engine
  participant Chain as On-chain Compliance<br/>(SMART Protocol)
  participant Signer as Unified Signer<br/>Interface
  participant Custody as Configured Custody<br/>Provider
  participant Policy as Custodian Policy<br/>Engine
  participant RPC as EVM Node<br/>(Broadcast)

  App->>Guard: Submit operation<br/>(transfer, mint, redeem…)
  Guard->>Guard: Authenticate caller and check permission
  alt user session operation needs a signature
      Guard->>Guard: Verify wallet evidence<br/>(PIN, OTP, or backup code)
  else API-key integration
      Guard->>Guard: Authenticate through API key session
  end
  Guard->>Engine: Accepted operation
  Engine->>Engine: Build transaction<br/>(Contract Runtime)
  Engine->>Engine: Estimate gas, assign nonce<br/>(Transaction Signer)

  Note over Engine,Chain: Layer 1: On-chain compliance pre-check (simulation)

  Engine->>RPC: eth_call: simulate canTransfer()
  RPC->>Chain: Compliance Engine evaluates modules
  Chain->>Chain: Identity verification (KYC/AML claims)
  Chain->>Chain: Transfer restrictions (country, blocklist…)
  Chain->>Chain: Supply & amount limits (caps, volume modules)
  Chain-->>RPC: ✅ true (all modules pass)
  RPC-->>Engine: Simulation succeeded

  Note over Engine,Custody: Layer 2: Custody provider signing + policy

  Engine->>Signer: signTransaction(walletId, txData)
  Signer->>Custody: Submit for signing

  alt provider policy: no approval required
      Custody->>Policy: Evaluate transaction policy
      Policy-->>Custody: ✅ Approved within configured limits
      Custody->>Custody: Produce signature
      Custody-->>Signer: Signed transaction
  else provider policy or hardware-backed quorum requires approval
      Custody->>Policy: Evaluate transaction policy or quorum state
      Policy-->>Custody: ⏳ Pending approval
      Custody-->>Signer: ApprovalRequired (pendingApprovalId)
      Signer-->>Engine: PendingApproval state
      Note over Engine,Custody: Approvers complete the provider approval step
      Engine->>Signer: Poll / resume on approval event
      Signer->>Custody: Check approval status
      Custody-->>Signer: ✅ Approved
      Custody->>Custody: Produce signature
      Custody-->>Signer: Signed transaction
  end

  Note over Signer,RPC: Payload integrity and broadcast

  Signer-->>Engine: Signed transaction bytes
  Engine->>Engine: Validate nonce, target, calldata, chain ID, value, and signer
  Engine->>RPC: eth_sendRawTransaction<br/>(validated signed bytes)
  RPC-->>Engine: Transaction hash for submitted bytes
  RPC->>Chain: Execute transaction on-chain
  Chain->>Chain: canTransfer() enforced again (on-chain)
  Chain-->>RPC: Transaction confirmed
  RPC-->>Engine: Transaction receipt
  Engine-->>App: Operation complete

`"
/>

## Flow steps [#flow-steps]

1. Submit the request. DALP authenticates the caller and checks the permission required for the operation. User-session writes that need a blockchain signature include wallet verification evidence. API-key integrations use API-key authentication and do not prompt for a user's PIN, OTP, or backup code.

2. Prepare the transaction. The accepted operation enters the Workflow Engine, which builds the contract call payload, estimates gas, and assigns a nonce from the reserved nonce pool. No signing, state change, or on-chain submission occurs at this step.

3. Run the compliance pre-check. The engine simulates `canTransfer` via `eth_call` before any signing occurs. The simulation checks identity claims, compliance modules, and amount or volume limits without spending gas or changing state. If the simulation reverts, DALP surfaces the compliance module's revert reason immediately and stops the flow before the custody provider is involved. This early gate lets you resolve compliance issues without exhausting a nonce reservation or triggering a custody approval workflow.

4. Route through the unified signer. The Transaction Signer delegates to a provider-agnostic signing layer. This layer supports approved custody backends and local signing modes so that switching your configured backend changes only provider setup, not the transaction flow.

5. Apply custody provider policy. The active provider evaluates its own policy rules before signing. Custody backends can combine key shares, enforce approval workflows, or require hardware-backed quorum before returning a signature.

6. Check signed payload integrity. For sign-only custody paths that return signed EVM bytes after an approval step, DALP validates the signed transaction before broadcast. The signed payload must match the original transaction request for nonce, destination contract, calldata, chain ID, optional value, and signer address. If any field differs, DALP refuses to broadcast the payload.

7. Broadcast and execute on-chain. The validated signed bytes are submitted via `eth_sendRawTransaction`. The node returns the transaction hash for those submitted bytes. DALP records that hash, waits for the matching receipt, and only then marks the operation complete. The compliance engine enforces `canTransfer` again on-chain. If compliance state changed between simulation and broadcast, the transaction reverts.

## Payload integrity before broadcast [#payload-integrity-before-broadcast]

DALP treats custody approval as permission to sign a specific EVM payload, not as permission to broadcast any signed bytes returned later. When you review a signing failure, check whether the failure occurs before or after this validation step.

On async sign-only custody paths, the platform parses the signed transaction after approval. The transaction must match the prepared request before it reaches the RPC node. DALP submits those verified bytes to the node and records the hash returned for that submission. If durable execution replays after the node accepted the transaction but before the hash was recorded, DALP derives the same hash from the verified signed bytes and continues confirmation instead of broadcasting a different payload.

| Check          | What DALP verifies                                                                          | Why it matters                                                                          |
| -------------- | ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| Nonce          | The signed transaction uses the nonce reserved for the sender and chain                     | Prevents a stale or substituted payload from consuming the wrong nonce lane             |
| Destination    | The `to` address matches the contract selected by the original operation                    | Prevents a signature for one target from being replayed against another target          |
| Calldata       | The function data matches the prepared operation                                            | Prevents a changed method call or changed arguments from being broadcast after approval |
| Chain ID       | The signed transaction carries the expected EVM chain ID                                    | Prevents cross-chain replay of an approved payload                                      |
| Value          | When the original request includes native value, the signed payload carries the same amount | Prevents payable value substitution on the same nonce and calldata                      |
| Signer         | The recovered signer address matches the wallet that owns the nonce reservation             | Prevents a payload signed by another key from completing the reserved transaction       |
| Broadcast hash | The transaction hash is recorded from the node response for the validated signed bytes      | Binds completion tracking and receipt lookup to the payload that DALP submitted         |

If validation fails, DALP fails the operation before broadcast. If broadcast succeeds but the journal replays before the transaction hash is recorded, DALP can recover the already-known transaction hash from the same validated signed bytes and continue confirmation without reusing the nonce reservation.

## Advanced accounts route [#advanced-accounts-route]

The sequence above covers the externally owned account (EOA) path: the custody provider signs an EVM transaction and DALP broadcasts the raw bytes to the node. DALP also supports a smart wallet route. Your organisation's advanced accounts setting, plus an optional executor override on the request, determines which route the transaction queue selects.

On the smart wallet route, request controls, preparation, and the compliance pre-check are the same. Execution differs after signing:

* DALP builds a UserOperation instead of a plain EVM transaction, signs it with the controlling key, and submits it through the bundler to the ERC-4337 EntryPoint rather than broadcasting raw bytes to the node.
* When a paymaster is enabled and funded, it pays the gas, so the smart wallet does not need its own native token balance. When sponsorship is off, the smart wallet pays its own gas.
* When the smart wallet requires a signing threshold, DALP records a pending approval and waits for the co-signers before the UserOperation is submitted, in the same way the EOA path waits for a custody provider approval.

<Mermaid
  chart="`flowchart TD
  PREP[&#x22;Prepared operation in the queue&#x22;] --> ROUTE{&#x22;Queue resolves route&#x22;}
  ROUTE -->|EOA| SIGN[&#x22;Custody provider signs EVM transaction&#x22;]
  SIGN --> BROADCAST[&#x22;Broadcast raw bytes to the node&#x22;]
  ROUTE -->|Smart wallet| UO[&#x22;Build and sign UserOperation&#x22;]
  PM[&#x22;Paymaster sponsors gas when enabled&#x22;] -. optional .-> UO
  UO --> MS{&#x22;Threshold approval required?&#x22;}
  MS -->|Yes| WAIT[&#x22;Wait for co-signer approval&#x22;]
  MS -->|No| BUNDLER[&#x22;Submit through bundler to EntryPoint&#x22;]
  WAIT --> BUNDLER
  BROADCAST --> CONFIRM[&#x22;Confirm receipt and index&#x22;]
  BUNDLER --> CONFIRM
`"
/>

The control model is identical on both routes. Identity claims, transfer policy, and custody approval are evaluated against the operation, not against the execution route. Account abstraction changes which account submits the transaction and how gas is paid, not whether the operation is allowed. If you need the full route-resolution and signing model, see [Transaction signer](/docs/architects/components/infrastructure/transaction-signer); for the concepts, see [Advanced accounts](/docs/architecture/concepts/account-abstraction).

## Control model [#control-model]

| Layer                   | Where enforced                                         | What it controls                                                                                                                       | Configured by                                           |
| ----------------------- | ------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| **Request controls**    | Platform API and Console request path                  | Authentication, authorization, participant and executor selection, wallet verification for user-session signing requests               | Platform administrators and user security settings      |
| **On-chain compliance** | SMART Protocol contracts                               | Identity/KYC claims, country restrictions, blocklists, supply caps, investor counts, time locks, volume modules                        | Issuer / compliance manager via Platform API            |
| **Custodian policies**  | Configured custody provider policy and quorum controls | Per-transaction amount limits, rolling spend limits, approver workflows, IP/time restrictions, destination allowlists, quorum approval | Operations team in the custody provider control surface |

Key invariants to understand when you integrate or audit this flow:

* Request controls reject unauthenticated, unauthorized, or unverifiable user-session requests before the signing flow starts.
* On-chain compliance enforces regulatory transfer rules at protocol level for standard token lifecycle operations.
* Custodian policy provides operational controls and approval workflows at infrastructure level.
* Standard token operations must pass each applicable control layer before they complete.
* Custodian-only exception operations, including forced transfers, do not follow the standard `canTransfer` compliance path. Use them only for controlled servicing cases where the custodian role is authorised to intervene.
* On-chain amount limits (via custom compliance modules) are auditable on-chain; custodian limits are off-chain operational controls.

## Failure modes [#failure-modes]

Use this table to locate the layer where your operation failed and what to check next.

| Failure point            | Cause                                                                                           | Resolution                                                                      |
| ------------------------ | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| Request rejected         | Authentication, permission, wallet setup, or wallet verification failed                         | Check the caller, role, participant/executor, and wallet verification method    |
| 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 quorum policy                     | Adjust policy thresholds or request approval                                    |
| Pending approval timeout | Approvers have not completed the provider approval request                                      | Escalate or configure auto-reject after timeout                                 |
| Signed payload mismatch  | Signed bytes do not match the prepared nonce, target, calldata, chain, value, or signer         | Treat as failed before broadcast and review the custody approval record         |
| Signing failure          | Network, provider, or custody backend 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                                   |
| UserOperation failed     | On the smart wallet route, bundler simulation or on-chain execution of the UserOperation failed | Check the smart wallet gas status, paymaster funding, and the operation payload |
| Sponsorship unavailable  | The paymaster is disabled, unfunded, or the sponsorship check failed                            | Fund or enable the sponsorship reserve, or let the smart wallet pay its own gas |

## See also [#see-also]

To diagnose a signing failure, start with the failure-modes table above before reading these pages.

* [Wallet verification](/docs/compliance-security/security/wallet-verification) - per-request PIN, OTP, and backup-code checks before user-session signing operations
* [Key Management](/docs/architects/components/infrastructure/key-management) - key storage and custody backend options for signing operations
* [Transaction Signer](/docs/architects/components/infrastructure/transaction-signer) - gas management, nonce coordination, and retry logic
* [Advanced accounts](/docs/architecture/concepts/account-abstraction) - the smart wallet execution route and the boundary it keeps
* [Advanced accounts infrastructure](/docs/architects/components/infrastructure/advanced-accounts) - UserOperations, bundlers, EntryPoint, and paymasters
* [Identity & compliance](/docs/compliance-security/security/identity-compliance) - on-chain compliance modules including amount and volume controls
* [Broadcast](/docs/architects/components/infrastructure/broadcast) - EVM RPC node access and transaction broadcast
