> For the complete documentation index, see [llms.txt](https://docs.t-rex.network/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.t-rex.network/guides/cross-chain/35.-discard-or-resolve-a-validation.md).

# 35. Discard or resolve a validation

A validation reserves room on the register from the moment it is issued. When the transfer it authorized never executes, or executes on one of two satellites only, that reservation has to be given back, or it blocks other movements indefinitely. Nothing is released automatically. The Validation Keeper discards validations that expired without any report, and resolves transfers between two satellites of which one report never arrived. Both are routine operations of an asset that is active on satellites.

## At a glance

|                   |                                                                                                                                                                                                                                                                                                                            |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Triggered by**  | Validation Keeper                                                                                                                                                                                                                                                                                                          |
| **Actors**        | Validation Keeper, Issuer, Platform                                                                                                                                                                                                                                                                                        |
| **Contracts**     | `ModularCompliance`, `Token`                                                                                                                                                                                                                                                                                               |
| **Preconditions** | The caller holds `VALIDATION_KEEPER` in the asset's domain (see [25](/guides/issuer/25.-commission-a-suite-and-grant-roles.md)); to discard: the validation is `Pending` and past its release time; to resolve: the validation has received one of two reports and is a second reconciliation window past its release time |
| **Result**        | The reservation of the validation is released. For a resolution, the amount held in transit is back on the wallet it left, or issuance is paused for the chain that owes its report                                                                                                                                        |

## Actors and roles

| Actor             | Role in this flow                                                                                        | What they hold                                                        |
| ----------------- | -------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| Validation Keeper | Finds the validations to release and submits the calls. An automated operator, run by the T-REX Network. | `VALIDATION_KEEPER` on the `ModularCompliance`, granted by the Issuer |
| Issuer            | Grants the role to the keeper and is informed of resolutions. Does not transact in this flow.            | The authority over who holds the role                                 |
| Platform          | Indexes validations and their status for the keeper.                                                     | Read access                                                           |

## Sequence

```mermaid
sequenceDiagram
    participant Platform
    participant Keeper as Validation Keeper
    participant MC as ModularCompliance
    participant Token

    Platform->>MC: statusOf(validationId), validationOf(validationId) (read)
    Platform->>Keeper: candidates
    alt no report arrived
        Keeper->>MC: discardExpiredValidations(validationIds)
        MC->>MC: caller holds VALIDATION_KEEPER
        MC->>MC: each id issued, Pending, past releaseAt
        MC->>MC: release the pending amounts of the identities
        MC->>Token: releaseFromValidation(wallet, amountMax)
        MC-->>Platform: ValidationDiscarded(validationId), per id
    else one of two reports arrived
        Keeper->>MC: resolveStuckValidation(validationId)
        MC->>MC: caller holds VALIDATION_KEEPER
        MC->>MC: awaiting its other report, past the second window
        alt the burn was reported
            MC->>Token: returnHeldInTransit(wallet, validationId)
        else the mint was reported
            MC->>MC: pause issuance for the sender's chain
        end
        MC-->>Platform: ValidationResolved(validationId, returnedAmount)
    end
```

## Step by step

### Step 1. Find the validations to release

**Who:** Platform, on behalf of the Validation Keeper

**Call:** Off-chain, using read calls

**Before the call:** The Platform indexes `TransferValidationIssued` and keeps the identifiers of the validations that are not settled.

**What happens:** For each open validation, the Platform reads `ModularCompliance.statusOf(validationId)`.

| `statusOf` returns                               | Meaning                                          | Action                                                         |
| ------------------------------------------------ | ------------------------------------------------ | -------------------------------------------------------------- |
| `Pending`                                        | Not past its release time.                       | None. A settlement is still expected                           |
| `Expired`                                        | Past its release time, and no report arrived.    | Discard (step 2)                                               |
| `AwaitingMint`, `AwaitingBurn`                   | One of two reports arrived.                      | None until the second window has passed. Then resolve (step 3) |
| `DiscardedAwaitingMint`, `DiscardedAwaitingBurn` | Discarded, then one of two reports arrived late. | Same                                                           |

For a resolution, the Platform reads `ModularCompliance.validationOf(validationId)` and computes the earliest time: `releaseAt` plus the reconciliation window of the validation, which is `releaseAt` less `expiry`.

**What changes:** Nothing on-chain.

### Step 2. Discard expired validations

**Who:** Validation Keeper (`VALIDATION_KEEPER`)

**Call:** `ModularCompliance.discardExpiredValidations(validationIds)`

**Before the call:** Every identifier in the list reads `Expired`.

**What the contract checks:**

1. The caller holds `VALIDATION_KEEPER` in the asset's domain. Otherwise the call reverts with `AccessManagedUnauthorized`.
2. Each identifier was issued. Otherwise `UnknownValidation`.
3. Each validation is stored as `Pending`. Otherwise `ValidationNotDiscardable`, with the status found. A validation that received one report is refused whatever the clock says.
4. The current time is past the `releaseAt` of each validation. Otherwise `ValidationNotReleasable`, with the release time.

The batch is atomic. One refused identifier reverts the whole call.

**What changes:** For each validation: the status becomes `Discarded`. The compliance subtracts `amountMax` from the pending amounts of the two identities, unless both wallets belong to one identity, in which case nothing was reserved for them. The token gives back the reservation on the sending wallet and emits `ReleasedFromValidation`. The compliance emits `ValidationDiscarded(validationId)`. The next validation is computed as if this one had never been issued. Nothing is undone on the ownership side, because nothing had moved.

### Step 3. Resolve a stuck validation

**Who:** Validation Keeper (`VALIDATION_KEEPER`)

**Call:** `ModularCompliance.resolveStuckValidation(validationId)`

**Before the call:** The validation is a transfer between two satellites, one report arrived, and the second window has passed. The Issuer is informed: a resolution means a satellite did not report.

**What the contract checks:**

1. The caller holds `VALIDATION_KEEPER` in the asset's domain. Otherwise `AccessManagedUnauthorized`.
2. The identifier was issued. Otherwise `UnknownValidation`.
3. The validation is awaiting its other report. Otherwise `ValidationNotStuck`, with the status found.
4. The current time is past `releaseAt` plus the reconciliation window of the validation. Otherwise `ValidationNotYetResolvable`, with the earliest time.
5. When the burn was reported: the token is not paused. Otherwise `EnforcedPause`.

**What changes:** It depends on which report arrived.

| The report that arrived | What the resolution does                                                                                                                                                     |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The burn                | The amount held in transit goes back to the bridged position of the wallet it was burned from. The token emits `ReturnedInTransit(walletKey, validationId, wallet, amount)`. |
| The mint                | Nothing on the reference chain can be returned. The compliance pauses the issuance of validations for the sender's chain and emits `ValidationIssuancePaused(chainKey)`.     |

In both cases the compliance releases whatever the validation still reserves, sets the status to `Resolved`, and emits `ValidationResolved(validationId, returnedAmount)`. `returnedAmount` is zero when only the mint was reported. A report that arrives for a `Resolved` validation halts the token.

## State after the flow

After a discard:

* `ModularCompliance.statusOf(validationId)` is `Discarded`.
* `ModularCompliance.pendingOutOf` and `pendingInOf` of the two identities, and `Token.reservedOf(wallet)`, no longer count the validation.
* No balance and no position has changed.

After a resolution:

* `ModularCompliance.statusOf(validationId)` is `Resolved`.
* When the burn was reported: `Token.inTransitOf(validationId)` is zero and `Token.bridgedBalanceOf(wallet)` has increased by the amount returned. On the satellite, the tokens are burned. The register and the satellite now disagree for that wallet, and the Issuer reconciles them.
* When the mint was reported: `ModularCompliance.isIssuancePaused(chainKey)` is true for the sender's chain. On the receiving satellite, the recipient holds tokens the register does not record as its own.
* No position of any identity has changed.

## Failure modes

| Symptom                                                                       | Cause                                                                                                                                     | Fix                                                                                                                                       |
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Reverts with `AccessManagedUnauthorized`                                      | The caller does not hold `VALIDATION_KEEPER` in the asset's domain (check 1 of steps 2 and 3)                                             | Grant the role: [25](/guides/issuer/25.-commission-a-suite-and-grant-roles.md)                                                            |
| Reverts with `UnknownValidation`                                              | The identifier was never issued by this compliance (check 2 of steps 2 and 3)                                                             | Take identifiers from `TransferValidationIssued`                                                                                          |
| `discardExpiredValidations` reverts with `ValidationNotDiscardable`           | One validation of the batch is not `Pending`: it settled in the meantime, was already discarded, or received one report (step 2, check 3) | Read `statusOf` again. Remove the identifier and resubmit                                                                                 |
| `discardExpiredValidations` reverts with `ValidationNotReleasable`            | One validation of the batch is not past its release time (step 2, check 4)                                                                | Wait until the time the error gives                                                                                                       |
| `resolveStuckValidation` reverts with `ValidationNotStuck`                    | The validation is not awaiting a second report (step 3, check 3)                                                                          | For a validation with no report, discard. For a settled one, nothing to do                                                                |
| `resolveStuckValidation` reverts with `ValidationNotYetResolvable`            | The second window has not passed (step 3, check 4)                                                                                        | Wait until the time the error gives                                                                                                       |
| `resolveStuckValidation` reverts with `EnforcedPause`                         | The token is paused and an amount held in transit cannot be returned (step 3, check 5)                                                    | Lift the pause first: [16](/guides/agents/16.-pause-and-unpause-a-token.md)                                                               |
| A settlement arrives after its validation was discarded                       | The satellite executed in time and its report was slower than the window                                                                  | It is applied as a late reconciliation: [36](/guides/cross-chain/36.-handle-a-late-reconciliation-or-a-halt.md)                           |
| The token paused itself after a resolution                                    | The missing report arrived after the validation was resolved                                                                              | [36](/guides/cross-chain/36.-handle-a-late-reconciliation-or-a-halt.md)                                                                   |
| A wallet's available amount stays reduced long after a transfer was abandoned | The validation expired and nobody discarded it                                                                                            | Release is never automatic. Check that the keeper holds `VALIDATION_KEEPER` for the asset, and report the validation to the T-REX Network |

## Common misunderstandings

> **Wrong:** "An expired validation releases its reservation by itself."
>
> **Right:** Past its release time, a validation reads `Expired` and keeps its reservation until the Validation Keeper discards it. `Expired` is computed when the status is read. It is not a stored state and it triggers nothing.

> **Wrong:** "Discarding a validation cancels the transfer."
>
> **Right:** A discard releases the reservation on the register. It cannot stop the satellite, and it does not need to: past `expiry`, T-REX Lite refuses to execute. If the satellite did execute in time and its report arrives after the discard, the register applies it late.

> **Wrong:** "Anyone can clean up expired validations."
>
> **Right:** The keeper is a restricted role, which the Issuer grants. Discarding is what turns a slow settlement into a late one, and a late settlement that breaches a rule stops a chain from issuing. The T-REX Network runs the keeper, so an Issuer does not have to operate one.

> **Wrong:** "A transfer with one report can be discarded like any other."
>
> **Right:** One report proves that a satellite executed its half, irreversibly. Such a validation is never discarded. It is settled by the second report, or resolved after a second window, with consequences the Issuer has to look into.

## Integration notes

* The keeper runs on a schedule. Its input is the list of open validations, from `TransferValidationIssued` less `ValidationSettled`, `ValidationDiscarded` and `ValidationResolved`.
* Read `statusOf` for every identifier just before submitting. One identifier that settled in the meantime reverts the whole batch.
* Do not discard at the first second. A margin after `releaseAt` costs little, and it turns fewer slow settlements into late reconciliations.
* The keeper holds back while the token is paused, and during a known outage of a messaging provider. An Issuer that knows of such an outage tells the T-REX Network. Settlements that cannot be delivered are not abandoned transfers.
* Treat every resolution as an incident. Alert the Issuer on `ValidationResolved`: a satellite failed to report, and the register and that satellite disagree until the Issuer has reconciled them. What executed on a satellite is final, so the register is brought in line with it: [36](/guides/cross-chain/36.-handle-a-late-reconciliation-or-a-halt.md).
* A discard works while the token is paused. A resolution that returns an amount held in transit does not.

## Related flows

* [31 Transfer on a satellite](/guides/cross-chain/31.-transfer-on-a-satellite.md) : where a validation comes from
* [34 Transfer between two satellites](/guides/cross-chain/34.-transfer-between-two-satellites.md) : the transfers that can get stuck
* [36 Handle a late reconciliation or a halt](/guides/cross-chain/36.-handle-a-late-reconciliation-or-a-halt.md) : what follows a late report or a resolution
* [33 Open an asset on a satellite](/guides/cross-chain/33.-open-an-asset-on-a-satellite.md) : where the windows are set
* [25 Commission a suite and grant roles](/guides/issuer/25.-commission-a-suite-and-grant-roles.md) : granting `VALIDATION_KEEPER`


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.t-rex.network/guides/cross-chain/35.-discard-or-resolve-a-validation.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
