For the complete documentation index, see llms.txt. This page is also available as Markdown.

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); 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

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

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

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

The token paused itself after a resolution

The missing report arrived after the validation was resolved

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.

  • A discard works while the token is paused. A resolution that returns an amount held in transit does not.

Last updated