Transfer validation
A transfer on a satellite executes only against a validation issued on the reference chain. The functions that issue, track, settle and close validations are part of ModularCompliance. This page lists them. The model is in Authorization before execution and The dual-state lifecycle.
At a glance
Source
contracts/compliance/modular/TransferValidation.sol, inherited by ModularCompliance
Interfaces
ITransferValidation, ISettlementHandler
Works with
The Token, which reserves the amount and sends the message, and the TREXRegistry, which resolves wallets
Roles
AGENT to request for a holder, COMPLIANCE_MANAGER for the windows, VALIDATION_KEEPER to discard and resolve
Request a validation
function requestTransferValidation(
bytes calldata from,
bytes calldata to,
uint256 requestedMin,
uint256 requestedMax,
bytes calldata spender
) external returns (uint256 validationId);from, to and spender are ERC-7930 interoperable addresses in canonical form. spender is empty when the sender executes itself. The function checks the range, answers five questions in this order, then writes.
Is the range valid?
requestedMin is at most requestedMax
InvalidRequestedRange
Where does it go?
from is on a satellite. The transfer has two legs when to is on another satellite
SenderNotOnSatellite, NonCanonicalInteroperableAddress
May the caller ask?
from resolves to an identity. The caller is that identity, or holds AGENT
UnverifiedWallet, NotAuthorizedForWallet
By when?
A validity window is set. For each satellite involved, issuance is not paused and a reconciliation window is set. The reservation lasts for the larger of the two windows
ValidityWindowNotSet, ValidationIssuancePaused, ReconciliationWindowNotSet
How much?
to is eligible (isWalletVerified). The maximum is capped at what the sending wallet has available, then at what the RULE modules allow. The rules are not asked when both wallets belong to one identity
UnverifiedWallet, ZeroValue when nothing is available, EmptyValidationRange when the cap falls below the minimum
May this spender execute?
Every SPENDER module accepts a named spender
ValidationSpenderRefused
On success the compliance stores the validation, reserves amountMax on the sending wallet (Token.reserveForValidation), counts it in the pending amounts of both identities, emits TransferValidationIssued, and has the token send one COMPLIANCE_VALIDATION message to each satellite involved. Guide: 31 Transfer on a satellite.
A request is allowed while the token is paused.
Status of a validation
statusOf(validationId) returns one of ten values. It reverts with UnknownValidation for an id that was never issued.
Pending
Issued, amount reserved, no report received
AwaitingMint
Two legs: the burn was reported, the mint is owed. The amount is in transit on the token
AwaitingBurn
Two legs: the mint was reported, the burn is owed
Settled
Every expected report arrived. Reservation released, positions updated
Expired
Computed, never stored: Pending and past releaseAt
Discarded
The Validation Keeper released the reservation of an expired validation
DiscardedAwaitingMint, DiscardedAwaitingBurn
Discarded, then one leg arrived late. The other is owed
LateReconciled
Every report arrived after the discard and was applied
Resolved
The Validation Keeper gave up on a pair whose other leg never arrived. A leg that arrives afterwards halts the token
validationOf(validationId) returns the stored record.
hash
The ERC-712 struct hash of the ComplianceValidation that was sent
amountMin, amountMax
The final bounds, inclusive
expiry
The deadline of the satellite: no execution at or after it
releaseAt
expiry plus the reconciliation window: from then on the validation can be discarded
fromChainKey, toChainKey
The chain of each wallet
fromKey, toKey
The keccak256 of each wallet, which a report is matched against
fromWallet
The sending wallet, as bytes
twoLegs
True when the two wallets are on two different satellites
fromIdentity, toIdentity
The identities resolved at issuance
relocation
True when both wallets belong to one identity
status
The stored status
executedAmount
The amount of the first report, which the second must repeat
legWallet
For two legs, the wallet the first report carried
Other reads: lastValidationId(), defaultValidityWindow(), reconciliationWindowOf(chainKey), isIssuancePaused(chainKey).
Settlement
handleSettlement(originChainKey, notification) is called by the token when a SETTLEMENT_NOTIFICATION passes the delivery checks. It reverts with AddressNotATokenBoundToComplianceContract for another caller.
The validation is unknown, the leg was already applied, or the validation is Resolved
Nothing is applied. ReplayedSettlement is emitted and the function tells the token to pause itself
The wallets or the origin chain do not match the validation
Reverts with SettlementLegMismatch
The amount is outside [amountMin, amountMax]
Reverts with SettlementOutOfBounds
Second leg with another amount than the first
Reverts with SettlementAmountMismatch
First of two legs
Recorded with ValidationLegConfirmed. A burn puts the amount in transit on the token
Last expected report
Positions and token ledger updated, ValidationSettled
Report for a discarded validation
Applied, with LateReconciliation. Issuance is paused for the origin chain when the amount exceeds what the rules allow at that moment
Every report that reaches the compliance emits SettlementNotified.
Windows and issuance
setDefaultValidityWindow(duration)
COMPLIANCE_MANAGER
ZeroDuration. Emits DefaultValidityWindowSet
setReconciliationWindow(chainKey, duration)
COMPLIANCE_MANAGER
ZeroDuration. Emits ReconciliationWindowSet
setIssuancePaused(chainKey, paused)
COMPLIANCE_MANAGER
Nothing. Emits ValidationIssuancePaused or ValidationIssuanceUnpaused when the state changes
The Validation Keeper
discardExpiredValidations(validationIds)
VALIDATION_KEEPER
UnknownValidation, ValidationNotDiscardable unless the validation is Pending, ValidationNotReleasable before releaseAt. Releases the reservation and emits ValidationDiscarded
resolveStuckValidation(validationId)
VALIDATION_KEEPER
UnknownValidation, ValidationNotStuck unless one leg of two arrived, ValidationNotYetResolvable before the date the error carries. Returns an amount held in transit to the wallet it was burned from, and emits ValidationResolved
Guides: 35 Discard or resolve a validation, 36 Handle a late reconciliation or a halt.
Events
TransferValidationIssued(uint256 indexed validationId, bytes from, bytes to, bytes spender, uint256 amountMin, uint256 amountMax, uint64 expiry, uint64 reconciliationWindow)
A validation is issued
SettlementNotified(bytes32 indexed originChainKey, uint256 indexed validationId, bytes from, bytes to, uint256 amount)
A report from a satellite reaches the compliance
ValidationLegConfirmed(uint256 indexed validationId, bytes32 indexed chainKey, uint256 amount), ValidationSettled (same arguments)
The first of two reports is recorded. Every report arrived
LateReconciliation(uint256 indexed validationId, bytes32 indexed chainKey)
A report is applied after the discard
ReplayedSettlement(uint256 indexed validationId, bytes32 indexed chainKey)
A report is refused and the token halts
ValidationDiscarded(uint256 indexed validationId)
An expired validation is discarded
ValidationResolved(uint256 indexed validationId, uint256 returnedAmount)
A half-reported transfer is closed
ValidationIssuancePaused(bytes32 indexed chainKey), ValidationIssuanceUnpaused (same argument)
Issuance for a chain stops, resumes
DefaultValidityWindowSet(uint64 duration), ReconciliationWindowSet(bytes32 indexed chainKey, uint64 duration)
A window is set
Last updated