Skip links

Protocol Specification for Regulatory Actions: Message Formats, Validation Logic, and Cross-chain Coordination

TL;DR

  • Six regulatory actions (FREEZE, SEIZE, CONFISCATE, LIQUIDATE, RESTRICT, RECOVER) each possess unique state transition rules and reversibility conditions, standardized through OIP message formats
  • OSS four-stage validation pipeline filters all regulatory actions through Authority Verification → State Transition Check → Legal Basis Verification → Cross-chain Consistency
  • D-quencer priority system assigns CRITICAL (P0) priority to emergency regulatory actions, ensuring processing before standard transactions
  • Three-phase cross-chain broadcast protocol (PREPARE → COMMIT → FINALIZE) supports three execution strategies based on atomicity level: ALL_OR_NOTHING, GUARANTEED, and BEST_EFFORT
  • This specification transforms the philosophical foundation of enforceability into implementable technical standards, defining integration APIs for DeFi protocol developers

Introduction: From Philosophy to Protocol

In our previous exploration of enforceability as the third pillar of RCP, we philosophically examined why regulatory enforcement power is essential. We demonstrated that the fundamental contradiction of “how to implement centralized enforcement power in a decentralized system” could be resolved through the Selective Decentralization paradigm.

However, philosophical justification alone does not make systems operational. Protocol specification is required.

In our recent analysis of asset state management complexity, we introduced the Hierarchical Lock Status framework. We defined what states an asset can occupy and which transitions are valid. Now we must specify who can trigger these state transitions, how, and why.

This research fully specifies at the protocol level how six regulatory actions are encoded as OIP messages, validated by OSS, prioritized by D-quencer, and broadcast across chains.

“Protocol specification is the bridge between philosophy and implementation. Specification without principles yields directionless implementation; principles without specification remain unrealizable ideals.”

Taxonomy of Six Regulatory Actions

Action Characteristics Matrix

Building upon the Lock Status framework established in our asset state management research, each of the six regulatory actions possesses unique state transition rules. The following table summarizes the core characteristics of each action:

ActionPermitted Start StatesTarget StateReversibilityCross-chain Requirement
FREEZEAVAILABLE, RESERVEDRESTRICTEDtrueSynchronous broadcast
SEIZERESTRICTEDSEIZEDConditionalAtomic commit required
CONFISCATESEIZEDTERMINATEDfalseFinality proof required
LIQUIDATEAVAILABLE, RESTRICTED, SEIZEDTERMINATEDfalseMarket integration
RESTRICTAVAILABLERESTRICTEDtrueCondition propagation
RECOVERRESTRICTED, SEIZEDAVAILABLEN/AOwnership restoration

Enforcing the Escalation Chain

As argued in our enforceability analysis, the core of regulatory enforcement power lies in procedural legitimacy. Code must enforce legal procedure:

  • SEIZE presupposes FREEZE — assets cannot be seized without first being frozen
  • CONFISCATE presupposes SEIZE — assets cannot be confiscated without first being seized
  • RECOVER presupposes proof of fraudulent acquisition — legal basis for ownership restoration is required

This escalation chain is enforced during the State Transition Check stage of the OSS validation pipeline.

OIP Regulatory Action Message Format

Base Message Structure

In OIP v0.3, regulatory actions are defined as the REGULATORY_ACTION message type. Extending the base OIP message structure from our earlier protocol specification, regulatory-specific fields are added:

{
  "version": "0.3.0",
  "messageId": "uuid-v4",
  "messageType": "REGULATORY_ACTION",
  "timestamp": "ISO-8601",
  "sourceChainId": "oraclizer-l3",
  "signature": "ed25519-signature",
  
  "regulatoryPayload": {
    "actionType": "FREEZE|SEIZE|CONFISCATE|LIQUIDATE|RESTRICT|RECOVER",
    "targetAsset": {
      "assetId": "bytes32",
      "assetType": "ERC20|ERC721|ERC1155|BOND|STOCK",
      "currentStateHash": "bytes32"
    },
    "authority": {
      "authorityId": "bytes32",
      "authorityType": "NATIONAL|INTERNATIONAL|INDUSTRY|SELF_REGULATORY",
      "jurisdiction": ["ISO-3166-alpha-2"],
      "legalBasis": {
        "documentType": "COURT_ORDER|REGULATORY_DIRECTIVE|EMERGENCY_ORDER",
        "documentHash": "bytes32",
        "issuedAt": "ISO-8601",
        "validUntil": "ISO-8601|null"
      },
      "priorityLevel": "CRITICAL|HIGH|NORMAL"
    },
    "actionParameters": { },
    "evidence": {
      "documentHashes": ["bytes32"],
      "witnessSignatures": ["signature"],
      "externalReferences": ["uri"]
    }
  },
  
  "crossChainScope": {
    "targetChains": ["chainId"],
    "atomicityRequirement": "ALL_OR_NOTHING|GUARANTEED|BEST_EFFORT",
    "timeoutSeconds": 300,
    "fallbackPolicy": "RETRY|PARTIAL|ABORT"
  }
}

Action-Specific Parameter Details

Each regulatory action requires a unique parameter set in the actionParameters field:

FREEZE Parameters

{
  "freezeScope": "FULL|PARTIAL",
  "exemptOperations": ["VIEW", "DIVIDEND_CLAIM"],
  "expirationTime": "ISO-8601|null",
  "autoUnfreezeCondition": {
    "type": "TIME_BASED|CONDITION_BASED|MANUAL",
    "parameters": {}
  }
}

Key Design Decision: exemptOperations specifies operations permitted even during freeze. For example, dividend claim rights may be exercisable during a freeze. This is the protocol-level implementation of the “proportionality principle” discussed in our enforceability research.

SEIZE Parameters

{
  "custodyAddress": "ERC-3770-address",
  "ownershipRetained": true,
  "accessRestrictions": {
    "allowedOperations": [],
    "custodianPermissions": ["VIEW", "TRANSFER_TO_COURT"]
  },
  "releasePreconditions": {
    "type": "COURT_ORDER|APPEAL_PERIOD|SETTLEMENT",
    "parameters": {}
  }
}

Key Design Decision: ownershipRetained: true specifies that seizure is not ownership deprivation. Legally, ownership of seized assets remains with the original owner; only control is delegated to regulatory authority.

CONFISCATE Parameters

{
  "destinationAddress": "ERC-3770-address",
  "finalityProof": {
    "courtDecision": {
      "caseNumber": "string",
      "documentHash": "bytes32",
      "jurisdiction": "ISO-3166"
    },
    "appealDeadlinePassed": true,
    "appealWaiver": "bytes32|null"
  },
  "compensationMechanism": {
    "type": "NONE|PARTIAL|FULL",
    "parameters": {}
  }
}

Key Design Decision: finalityProof proves the legal finality of confiscation. If appealDeadlinePassed is false, OSS rejects this message. This mechanism resolves the tension between blockchain’s irreversibility and legal procedure’s reversibility.

LIQUIDATE Parameters

{
  "liquidationType": "MARKET_SALE|AUCTION|FIXED_PRICE",
  "minimumPrice": {
    "amount": "uint256",
    "currency": "ERC-3770-address",
    "priceOracle": "oracleId"
  },
  "proceedsDistribution": {
    "primaryCreditor": {
      "address": "ERC-3770-address",
      "percentage": 80
    },
    "remainderRecipient": {
      "address": "ERC-3770-address",
      "percentage": 20
    }
  },
  "deadline": "ISO-8601"
}

Key Design Decision: priceOracle references external price oracles for fair market price determination. This directly integrates with Oraclizer’s state synchronization capabilities, ensuring liquidation price fairness.

RESTRICT Parameters

{
  "restrictionType": "WHITELIST|BLACKLIST|THRESHOLD|GEOGRAPHIC",
  "allowedCounterparties": ["OCID"],
  "blockedCounterparties": ["OCID"],
  "transactionLimits": {
    "perTransaction": "uint256",
    "daily": "uint256",
    "monthly": "uint256"
  },
  "conditionExpression": "DSL-expression",
  "exceptions": [{
    "conditionType": "EMERGENCY|LEGAL_OBLIGATION",
    "authorizedBy": "authorityId"
  }]
}

Key Design Decision: conditionExpression expresses complex conditions in a domain-specific language (DSL). For example, expressions like "sender.kycLevel >= 2 AND receiver.jurisdiction NOT IN ['XX', 'YY']" are possible.

RECOVER Parameters

{
  "originalOwner": {
    "ocid": "OCID",
    "ownershipProof": {
      "type": "TRANSACTION_HISTORY|LEGAL_DOCUMENT|COURT_ORDER",
      "documentHash": "bytes32"
    }
  },
  "fraudulentHolder": {
    "ocid": "OCID",
    "fraudProof": {
      "type": "THEFT|SCAM|UNAUTHORIZED_TRANSFER",
      "documentHash": "bytes32"
    }
  },
  "recoveryBasis": {
    "legalFramework": "string",
    "courtOrder": "bytes32"
  },
  "compensationMechanism": {
    "toFraudulentHolder": false,
    "toIntermediaries": {
      "enabled": true,
      "parameters": {}
    }
  }
}

Key Design Decision: RECOVER, unlike other actions, has the character of “reverting” state. While this appears to conflict with blockchain’s immutability principle, it is justified under the Selective Immutability paradigm explored in our rollback mechanisms research.

OSS Validation Pipeline

Four-Stage Validation Process

The Oracle State Synchronizer (OSS) validates all incoming regulatory action messages through a four-stage pipeline:

Stage 1: Authority Verification

package oss

import (
    "context"
    "errors"
)

var (
    ErrAuthorityNotRegistered     = errors.New("AUTHORITY_NOT_REGISTERED")
    ErrJurisdictionMismatch       = errors.New("JURISDICTION_MISMATCH")
    ErrActionNotPermitted         = errors.New("ACTION_NOT_PERMITTED")
    ErrCriticalPriorityNotAllowed = errors.New("CRITICAL_PRIORITY_NOT_AUTHORIZED")
)

type AuthorityVerificationResult struct {
    Verified  bool
    Authority *RegisteredAuthority
}

func (v *Validator) VerifyAuthority(ctx context.Context, msg *RegulatoryActionMessage) (*AuthorityVerificationResult, error) {
    authority := msg.RegulatoryPayload.Authority
    
    // 1. Verify authorityId exists in Registry
    registeredAuthority, err := v.authorityRegistry.Get(ctx, authority.AuthorityID)
    if err != nil || registeredAuthority == nil {
        return nil, ErrAuthorityNotRegistered
    }
    
    // 2. Check jurisdiction scope
    targetJurisdiction, err := v.getAssetJurisdiction(ctx, msg.RegulatoryPayload.TargetAsset)
    if err != nil {
        return nil, err
    }
    
    if !containsJurisdiction(authority.Jurisdiction, targetJurisdiction) {
        return nil, ErrJurisdictionMismatch
    }
    
    // 3. Verify permissions for action type
    actionPermissions := registeredAuthority.Permissions[authority.AuthorityType]
    if !containsAction(actionPermissions, msg.RegulatoryPayload.ActionType) {
        return nil, ErrActionNotPermitted
    }
    
    // 4. Validate priority level
    if authority.PriorityLevel == PriorityCritical && !registeredAuthority.CanIssueCritical {
        return nil, ErrCriticalPriorityNotAllowed
    }
    
    return &AuthorityVerificationResult{
        Verified:  true,
        Authority: registeredAuthority,
    }, nil
}

Stage 2: State Transition Check

package oss

var (
    ErrStateHashMismatch           = errors.New("STATE_HASH_MISMATCH")
    ErrFreezeRequiredBeforeSeize   = errors.New("FREEZE_REQUIRED_BEFORE_SEIZE")
    ErrSeizeRequiredBeforeConfiscate = errors.New("SEIZE_REQUIRED_BEFORE_CONFISCATE")
    ErrInvalidStateTransition      = errors.New("INVALID_STATE_TRANSITION")
)

// ValidTransitions defines permitted starting states for each action
var ValidTransitions = map[ActionType][]LockStatus{
    ActionFreeze:     {StatusAvailable, StatusReserved},
    ActionSeize:      {StatusRestricted},  // Must be frozen first
    ActionConfiscate: {StatusSeized},       // Must be seized first
    ActionLiquidate:  {StatusAvailable, StatusRestricted, StatusSeized},
    ActionRestrict:   {StatusAvailable},
    ActionRecover:    {StatusRestricted, StatusSeized},
}

type StateTransitionResult struct {
    Valid        bool
    CurrentState *AssetState
}

func (v *Validator) CheckStateTransition(ctx context.Context, msg *RegulatoryActionMessage) (*StateTransitionResult, error) {
    actionType := msg.RegulatoryPayload.ActionType
    targetAsset := msg.RegulatoryPayload.TargetAsset
    
    // Query current state from RWA Registry
    currentState, err := v.rwaRegistry.GetState(ctx, targetAsset.AssetID)
    if err != nil {
        return nil, err
    }
    
    // Verify state hash match (concurrency control)
    if currentState.StateHash != targetAsset.CurrentStateHash {
        return nil, ErrStateHashMismatch
    }
    
    // Check valid transition
    validFromStates, exists := ValidTransitions[actionType]
    if !exists {
        return nil, ErrInvalidStateTransition
    }
    
    if !containsStatus(validFromStates, currentState.LockStatus) {
        // Check escalation requirements
        if actionType == ActionSeize && currentState.LockStatus == StatusAvailable {
            return nil, ErrFreezeRequiredBeforeSeize
        }
        if actionType == ActionConfiscate && currentState.LockStatus != StatusSeized {
            return nil, ErrSeizeRequiredBeforeConfiscate
        }
        return nil, ErrInvalidStateTransition
    }
    
    return &StateTransitionResult{
        Valid:        true,
        CurrentState: currentState,
    }, nil
}

Stage 3: Legal Basis Verification

package oss

import "time"

var (
    ErrInvalidLegalDocumentStructure = errors.New("INVALID_LEGAL_DOCUMENT_STRUCTURE")
    ErrEvidenceHashInvalid           = errors.New("EVIDENCE_HASH_INVALID")
    ErrLegalBasisExpired             = errors.New("LEGAL_BASIS_EXPIRED_OR_NOT_YET_VALID")
    ErrAppealPeriodNotExpired        = errors.New("APPEAL_PERIOD_NOT_EXPIRED")
)

type LegalBasisResult struct {
    Verified bool
}

func (v *Validator) VerifyLegalBasis(ctx context.Context, msg *RegulatoryActionMessage) (*LegalBasisResult, error) {
    legalBasis := msg.RegulatoryPayload.Authority.LegalBasis
    evidence := msg.RegulatoryPayload.Evidence
    
    // 1. Validate legal document structure
    if !v.isValidLegalDocument(legalBasis) {
        return nil, ErrInvalidLegalDocumentStructure
    }
    
    // 2. Verify evidence hash integrity
    for _, hash := range evidence.DocumentHashes {
        verified, err := v.verifyDocumentHash(ctx, hash)
        if err != nil || !verified {
            return nil, ErrEvidenceHashInvalid
        }
    }
    
    // 3. Check temporal validity
    now := time.Now()
    issuedAt := legalBasis.IssuedAt
    
    if now.Before(issuedAt) {
        return nil, ErrLegalBasisExpired
    }
    
    if legalBasis.ValidUntil != nil && now.After(*legalBasis.ValidUntil) {
        return nil, ErrLegalBasisExpired
    }
    
    // 4. Additional finality proof verification for confiscation
    if msg.RegulatoryPayload.ActionType == ActionConfiscate {
        finalityProof := msg.RegulatoryPayload.ActionParameters.(*ConfiscateParams).FinalityProof
        if !finalityProof.AppealDeadlinePassed {
            return nil, ErrAppealPeriodNotExpired
        }
    }
    
    return &LegalBasisResult{Verified: true}, nil
}

Stage 4: Cross-chain Consistency Check

package oss

import "sync"

var (
    ErrCrossChainStateConflict   = errors.New("CROSS_CHAIN_STATE_CONFLICT")
    ErrAtomicCommitNotSupported  = errors.New("ATOMIC_COMMIT_NOT_SUPPORTED")
)

type CrossChainConsistencyResult struct {
    Consistent  bool
    ChainStates map[string]*AssetState
}

func (v *Validator) CheckCrossChainConsistency(ctx context.Context, msg *RegulatoryActionMessage) (*CrossChainConsistencyResult, error) {
    crossChainScope := msg.CrossChainScope
    targetAsset := msg.RegulatoryPayload.TargetAsset
    
    // 1. Query current states from target chains (parallel)
    chainStates := make(map[string]*AssetState)
    var mu sync.Mutex
    var wg sync.WaitGroup
    errChan := make(chan error, len(crossChainScope.TargetChains))
    
    for _, chainID := range crossChainScope.TargetChains {
        wg.Add(1)
        go func(cid string) {
            defer wg.Done()
            state, err := v.crossChainBridge.QueryState(ctx, cid, targetAsset.AssetID)
            if err != nil {
                errChan <- err
                return
            }
            mu.Lock()
            chainStates[cid] = state
            mu.Unlock()
        }(chainID)
    }
    wg.Wait()
    close(errChan)
    
    if err := <-errChan; err != nil {
        return nil, err
    }
    
    // 2. Detect state conflicts
    stateConflicts := v.detectStateConflicts(chainStates)
    if len(stateConflicts) > 0 {
        resolved, err := v.attemptConflictResolution(ctx, stateConflicts)
        if err != nil || !resolved {
            return nil, ErrCrossChainStateConflict
        }
    }
    
    // 3. Verify atomicity feasibility
    if crossChainScope.AtomicityRequirement == AtomicityAllOrNothing {
        for _, chainID := range crossChainScope.TargetChains {
            bridgeStatus, err := v.crossChainBridge.GetStatus(ctx, chainID)
            if err != nil {
                return nil, err
            }
            if !bridgeStatus.SupportsAtomicCommit {
                return nil, fmt.Errorf("%w: chainId=%s", ErrAtomicCommitNotSupported, chainID)
            }
        }
    }
    
    // 4. Verify execution feasibility within timeout
    estimatedTime := v.estimateExecutionTime(crossChainScope.TargetChains)
    if estimatedTime > time.Duration(crossChainScope.TimeoutSeconds)*time.Second*8/10 {
        v.logger.Warn("Execution time may exceed timeout", 
            "estimatedTime", estimatedTime,
            "timeout", crossChainScope.TimeoutSeconds)
    }
    
    return &CrossChainConsistencyResult{
        Consistent:  true,
        ChainStates: chainStates,
    }, nil
}

D-quencer Priority Handling

Multi-Priority Queue Architecture

D-quencer processes regular transactions and regulatory actions distinctly. On top of the D-quencer consensus algorithm detailed in our core component documentation, a regulatory-specific priority system is added:

package dquencer

import (
    "container/heap"
    "sync"
)

type PriorityLevel int

const (
    PriorityCritical   PriorityLevel = iota // P0: Emergency regulatory actions
    PriorityRegulatory                       // P1: Standard regulatory actions
    PriorityHigh                             // P2: High priority regular transactions
    PriorityNormal                           // P3: Normal transactions
    PriorityLow                              // P4: Low priority
)

type RegulatoryPriorityQueue struct {
    queues              map[PriorityLevel]*MaxHeap
    regulatoryTxPerBlock int
    mu                  sync.RWMutex
}

func NewRegulatoryPriorityQueue() *RegulatoryPriorityQueue {
    rpq := &RegulatoryPriorityQueue{
        queues:              make(map[PriorityLevel]*MaxHeap),
        regulatoryTxPerBlock: 20,
    }
    
    for _, level := range []PriorityLevel{
        PriorityCritical, PriorityRegulatory, PriorityHigh, PriorityNormal, PriorityLow,
    } {
        rpq.queues[level] = NewMaxHeap()
    }
    
    return rpq
}

func (rpq *RegulatoryPriorityQueue) Enqueue(tx *Transaction) {
    rpq.mu.Lock()
    defer rpq.mu.Unlock()
    
    priority := rpq.determinePriority(tx)
    heap.Push(rpq.queues[priority], &HeapItem{
        Value:    tx,
        Priority: tx.Timestamp.UnixNano(),
    })
}

func (rpq *RegulatoryPriorityQueue) determinePriority(tx *Transaction) PriorityLevel {
    if tx.MessageType != MessageTypeRegulatoryAction {
        if tx.PriorityHint != nil {
            return *tx.PriorityHint
        }
        return PriorityNormal
    }
    
    authority := tx.RegulatoryPayload.Authority
    
    // CRITICAL: Emergency freeze, seize, confiscate
    if authority.PriorityLevel == "CRITICAL" ||
        authority.LegalBasis.DocumentType == DocumentTypeEmergencyOrder {
        return PriorityCritical
    }
    
    // REGULATORY: Standard regulatory actions
    return PriorityRegulatory
}

func (rpq *RegulatoryPriorityQueue) DequeueForBlock(blockCapacity int) []*Transaction {
    rpq.mu.Lock()
    defer rpq.mu.Unlock()
    
    selected := make([]*Transaction, 0, blockCapacity)
    regulatoryCount := 0
    
    // CRITICAL always processed first
    for rpq.queues[PriorityCritical].Len() > 0 && len(selected) < blockCapacity {
        item := heap.Pop(rpq.queues[PriorityCritical]).(*HeapItem)
        selected = append(selected, item.Value.(*Transaction))
        regulatoryCount++
    }
    
    // REGULATORY subject to per-block limit
    for rpq.queues[PriorityRegulatory].Len() > 0 &&
        regulatoryCount < rpq.regulatoryTxPerBlock &&
        len(selected) < blockCapacity {
        item := heap.Pop(rpq.queues[PriorityRegulatory]).(*HeapItem)
        selected = append(selected, item.Value.(*Transaction))
        regulatoryCount++
    }
    
    // Fill remaining capacity with regular transactions
    for _, priority := range []PriorityLevel{PriorityHigh, PriorityNormal, PriorityLow} {
        for rpq.queues[priority].Len() > 0 && len(selected) < blockCapacity {
            item := heap.Pop(rpq.queues[priority]).(*HeapItem)
            selected = append(selected, item.Value.(*Transaction))
        }
    }
    
    return selected
}

Scheduled Regulatory Action Processing

Some regulatory actions require scheduled execution rather than immediate execution. For example, when a court order takes effect on a specific date:

package dquencer

import (
    "context"
    "time"
)

type ScheduledActionStatus string

const (
    StatusPending  ScheduledActionStatus = "PENDING"
    StatusExecuted ScheduledActionStatus = "EXECUTED"
    StatusFailed   ScheduledActionStatus = "FAILED"
)

type ScheduledAction struct {
    ID            string
    Message       *RegulatoryActionMessage
    ExecutionTime time.Time
    Status        ScheduledActionStatus
    RetryPolicy   RetryPolicy
    RetryCount    int
}

type RetryPolicy struct {
    MaxRetries    int
    RetryInterval time.Duration
}

type ScheduledRegulatoryActionManager struct {
    dquencer         *DQuencer
    scheduledActions *TimerQueue
    notifier         Notifier
}

func NewScheduledManager(dq *DQuencer, notifier Notifier) *ScheduledRegulatoryActionManager {
    return &ScheduledRegulatoryActionManager{
        dquencer:         dq,
        scheduledActions: NewTimerQueue(),
        notifier:         notifier,
    }
}

func (m *ScheduledRegulatoryActionManager) ScheduleAction(
    msg *RegulatoryActionMessage,
    executionTime time.Time,
) (string, error) {
    
    scheduledAction := &ScheduledAction{
        ID:            generateID(),
        Message:       msg,
        ExecutionTime: executionTime,
        Status:        StatusPending,
        RetryPolicy: RetryPolicy{
            MaxRetries:    3,
            RetryInterval: time.Minute,
        },
    }
    
    m.scheduledActions.Insert(scheduledAction, executionTime)
    m.setExecutionTimer(scheduledAction)
    
    return scheduledAction.ID, nil
}

func (m *ScheduledRegulatoryActionManager) executeScheduledAction(
    ctx context.Context,
    action *ScheduledAction,
) {
    // Re-validate preconditions at execution time
    if err := m.validatePreconditions(ctx, action.Message); err != nil {
        if action.RetryCount < action.RetryPolicy.MaxRetries {
            action.RetryCount++
            time.AfterFunc(action.RetryPolicy.RetryInterval, func() {
                m.executeScheduledAction(ctx, action)
            })
            return
        }
        action.Status = StatusFailed
        m.notifier.NotifyExecutionFailure(action, err)
        return
    }
    
    // Inject into D-quencer with CRITICAL priority
    m.dquencer.InjectRegulatoryAction(action.Message, PriorityCritical)
    action.Status = StatusExecuted
}

Cross-chain Broadcast Protocol

Three-Phase Atomic Execution

Cross-chain regulatory actions render partial execution legally meaningless. For example, when seizing assets distributed across four chains, success on only three chains fails to achieve the regulatory objective.

Phase 1: PREPARE

package crosschain

import (
    "context"
    "sync"
    "time"
)

type PrepareStatus string

const (
    PrepareReady  PrepareStatus = "READY"
    PrepareFailed PrepareStatus = "PREPARE_FAILED"
)

type PrepareResult struct {
    ChainID       string
    Status        PrepareStatus
    ReservationID string
    Error         string
}

func (b *Broadcaster) PreparePhase(
    ctx context.Context,
    msg *RegulatoryActionMessage,
) ([]PrepareResult, error) {
    
    crossChainScope := msg.CrossChainScope
    regulatoryPayload := msg.RegulatoryPayload
    
    results := make([]PrepareResult, 0, len(crossChainScope.TargetChains))
    var mu sync.Mutex
    var wg sync.WaitGroup
    
    for _, chainID := range crossChainScope.TargetChains {
        wg.Add(1)
        go func(cid string) {
            defer wg.Done()
            
            result, err := b.bridge.SendPrepare(ctx, cid, &PrepareRequest{
                ActionType:  regulatoryPayload.ActionType,
                TargetAsset: regulatoryPayload.TargetAsset,
                StateReservation: StateReservation{
                    LockUntil:     time.Now().Add(time.Duration(crossChainScope.TimeoutSeconds) * time.Second),
                    ReservationID: generateReservationID(),
                },
            })
            
            mu.Lock()
            defer mu.Unlock()
            
            if err != nil {
                results = append(results, PrepareResult{
                    ChainID: cid,
                    Status:  PrepareFailed,
                    Error:   err.Error(),
                })
                return
            }
            
            results = append(results, PrepareResult{
                ChainID:       cid,
                Status:        PrepareReady,
                ReservationID: result.ReservationID,
            })
        }(chainID)
    }
    wg.Wait()
    
    // Check quorum
    readyCount := 0
    for _, r := range results {
        if r.Status == PrepareReady {
            readyCount++
        }
    }
    
    if crossChainScope.AtomicityRequirement == AtomicityAllOrNothing &&
        readyCount != len(crossChainScope.TargetChains) {
        // Cancel all reservations
        if err := b.rollbackPrepare(ctx, results); err != nil {
            b.logger.Error("Failed to rollback prepare", "error", err)
        }
        return nil, ErrPreparePhasesFailed
    }
    
    return results, nil
}

Phase 2: COMMIT

package crosschain

type CommitStatus string

const (
    CommitCommitted CommitStatus = "COMMITTED"
    CommitFailed    CommitStatus = "COMMIT_FAILED"
)

type CommitResult struct {
    ChainID     string
    Status      CommitStatus
    TxHash      string
    BlockNumber uint64
    Error       string
}

func (b *Broadcaster) CommitPhase(
    ctx context.Context,
    prepareResults []PrepareResult,
    msg *RegulatoryActionMessage,
) ([]CommitResult, error) {
    
    crossChainScope := msg.CrossChainScope
    regulatoryPayload := msg.RegulatoryPayload
    
    // Filter only READY chains
    readyChains := make([]PrepareResult, 0)
    for _, r := range prepareResults {
        if r.Status == PrepareReady {
            readyChains = append(readyChains, r)
        }
    }
    
    commitResults := make([]CommitResult, 0, len(readyChains))
    var mu sync.Mutex
    var wg sync.WaitGroup
    
    for _, prepared := range readyChains {
        wg.Add(1)
        go func(p PrepareResult) {
            defer wg.Done()
            
            signature, _ := b.signCommitMessage(p.ChainID, regulatoryPayload)
            result, err := b.bridge.SendCommit(ctx, p.ChainID, &CommitRequest{
                ReservationID:     p.ReservationID,
                RegulatoryPayload: regulatoryPayload,
                Signature:         signature,
            })
            
            mu.Lock()
            defer mu.Unlock()
            
            if err != nil {
                commitResults = append(commitResults, CommitResult{
                    ChainID: p.ChainID,
                    Status:  CommitFailed,
                    Error:   err.Error(),
                })
                return
            }
            
            commitResults = append(commitResults, CommitResult{
                ChainID:     p.ChainID,
                Status:      CommitCommitted,
                TxHash:      result.TxHash,
                BlockNumber: result.BlockNumber,
            })
        }(prepared)
    }
    wg.Wait()
    
    // Handle failures based on atomicity level
    failedCommits := make([]CommitResult, 0)
    for _, r := range commitResults {
        if r.Status == CommitFailed {
            failedCommits = append(failedCommits, r)
        }
    }
    
    if len(failedCommits) > 0 {
        return b.handleCommitFailures(ctx, commitResults, crossChainScope.AtomicityRequirement, msg)
    }
    
    return commitResults, nil
}

func (b *Broadcaster) handleCommitFailures(
    ctx context.Context,
    commitResults []CommitResult,
    atomicityLevel AtomicityRequirement,
    msg *RegulatoryActionMessage,
) ([]CommitResult, error) {
    
    switch atomicityLevel {
    case AtomicityBestEffort:
        // Keep successful chains, log failures
        succeeded := make([]CommitResult, 0)
        failed := make([]CommitResult, 0)
        for _, r := range commitResults {
            if r.Status == CommitCommitted {
                succeeded = append(succeeded, r)
            } else {
                failed = append(failed, r)
            }
        }
        b.logger.Warn("Partial commit success", 
            "succeeded", len(succeeded), 
            "failed", len(failed))
        return commitResults, nil
        
    case AtomicityGuaranteed:
        // Retry failed chains with exponential backoff
        for _, failed := range commitResults {
            if failed.Status == CommitFailed {
                go b.retryWithBackoff(ctx, failed.ChainID, msg, RetryConfig{
                    MaxRetries:   5,
                    InitialDelay: time.Second,
                    MaxDelay:     30 * time.Second,
                })
            }
        }
        return commitResults, nil
        
    case AtomicityAllOrNothing:
        // Rollback all chains
        successfulCommits := make([]CommitResult, 0)
        for _, r := range commitResults {
            if r.Status == CommitCommitted {
                successfulCommits = append(successfulCommits, r)
            }
        }
        if err := b.rollbackCommits(ctx, successfulCommits); err != nil {
            b.logger.Error("Rollback failed", "error", err)
        }
        return nil, ErrAtomicCommitFailed
    }
    
    return commitResults, nil
}

Phase 3: FINALIZE

package crosschain

type FinalizeResult struct {
    Status         string
    AggregateProof *CrossChainProof
    RegistryUpdate *RegistryUpdateResult
}

func (b *Broadcaster) FinalizePhase(
    ctx context.Context,
    commitResults []CommitResult,
    msg *RegulatoryActionMessage,
) (*FinalizeResult, error) {
    
    // 1. Generate aggregate cross-chain proof
    aggregateProof, err := b.generateCrossChainProof(commitResults)
    if err != nil {
        return nil, fmt.Errorf("failed to generate proof: %w", err)
    }
    
    // 2. Update RWA Registry on origin chain (Oraclizer L3)
    chainIDs := make([]string, 0, len(commitResults))
    txHashes := make([]string, 0, len(commitResults))
    for _, r := range commitResults {
        chainIDs = append(chainIDs, r.ChainID)
        txHashes = append(txHashes, r.TxHash)
    }
    
    registryUpdate, err := b.rwaRegistry.UpdateState(ctx, &RegistryUpdateRequest{
        AssetID:  msg.RegulatoryPayload.TargetAsset.AssetID,
        NewState: deriveNewState(msg.RegulatoryPayload.ActionType),
        CrossChainProof: aggregateProof,
        ExecutionDetails: ExecutionDetails{
            Timestamp:         time.Now(),
            AffectedChains:    chainIDs,
            TransactionHashes: txHashes,
        },
    })
    if err != nil {
        return nil, fmt.Errorf("failed to update registry: %w", err)
    }
    
    // 3. Emit event
    b.eventEmitter.Emit("REGULATORY_ACTION_FINALIZED", map[string]interface{}{
        "actionId":           msg.MessageID,
        "actionType":         msg.RegulatoryPayload.ActionType,
        "targetAsset":        msg.RegulatoryPayload.TargetAsset.AssetID,
        "authority":          msg.RegulatoryPayload.Authority.AuthorityID,
        "affectedChains":     len(commitResults),
        "aggregateProofHash": aggregateProof.Hash,
        "registryUpdateTx":   registryUpdate.TxHash,
    })
    
    return &FinalizeResult{
        Status:         "FINALIZED",
        AggregateProof: aggregateProof,
        RegistryUpdate: registryUpdate,
    }, nil
}

DeFi Protocol Integration Guide

Regulatory Event Subscription API

DeFi protocol developers can subscribe to Oraclizer’s regulatory events for automatic response:

package integration

import (
    "context"
    "log"
    
    oraclizer "github.com/oraclizer/sdk-go"
)

func SetupRegulatoryEventSubscription(ctx context.Context) error {
    // Initialize Oraclizer client
    client, err := oraclizer.NewClient(oraclizer.Config{
        Endpoint: "wss://api.oraclizer.io/v1",
        APIKey:   os.Getenv("ORACLIZER_API_KEY"),
    })
    if err != nil {
        return err
    }
    
    // Get list of managed assets
    managedAssets, err := getProtocolManagedAssets(ctx)
    if err != nil {
        return err
    }
    
    // Subscribe to regulatory events
    subscription, err := client.Subscribe(ctx, oraclizer.SubscriptionConfig{
        EventType: "REGULATORY_ACTION",
        AssetFilter: managedAssets,
        ActionTypes: []string{"FREEZE", "SEIZE", "CONFISCATE", "LIQUIDATE"},
    })
    if err != nil {
        return err
    }
    
    // Handle events
    go func() {
        for event := range subscription.Events() {
            handleRegulatoryAction(ctx, event)
        }
    }()
    
    return nil
}

func handleRegulatoryAction(ctx context.Context, event *oraclizer.RegulatoryEvent) {
    actionType := event.ActionType
    targetAsset := event.TargetAsset
    
    switch actionType {
    case "FREEZE":
        // Exclude from collateral valuation
        if err := collateralManager.MarkAsFrozen(ctx, targetAsset.AssetID); err != nil {
            log.Printf("Failed to mark asset as frozen: %v", err)
        }
        // Suspend liquidation for positions based on this collateral
        if err := liquidationEngine.SuspendForAsset(ctx, targetAsset.AssetID); err != nil {
            log.Printf("Failed to suspend liquidation: %v", err)
        }
        
    case "SEIZE":
        // Fully exclude from collateral
        if err := collateralManager.RemoveFromCollateral(ctx, targetAsset.AssetID); err != nil {
            log.Printf("Failed to remove from collateral: %v", err)
        }
        // Initiate forced liquidation for related positions
        if err := liquidationEngine.InitiateForAsset(ctx, targetAsset.AssetID); err != nil {
            log.Printf("Failed to initiate liquidation: %v", err)
        }
        
    case "LIQUIDATE":
        // Coordinate protocol liquidation with regulatory liquidation
        if err := coordinateWithRegulatoryLiquidation(ctx, event); err != nil {
            log.Printf("Failed to coordinate liquidation: %v", err)
        }
    }
}

Regulatory Status Query API

package integration

// Query current regulatory status of an asset
func QueryAssetRegulatoryStatus(ctx context.Context, client *oraclizer.Client, assetID string) (*RegulatoryStatus, error) {
    status, err := client.GetAssetRegulatoryStatus(ctx, oraclizer.StatusQuery{
        AssetID:         assetID,
        IncludeHistory:  true,
        CrossChainScope: []string{"ethereum", "arbitrum", "optimism"},
    })
    if err != nil {
        return nil, err
    }
    
    return status, nil
}

// Response structure example:
// {
//     AssetID: "0x742d35...",
//     CurrentStatus: {
//         LockStatus: "RESTRICTED",
//         ActiveActions: [{
//             ActionType: "FREEZE",
//             Authority:  "SEC",
//             Since:      "2024-03-15T10:30:00Z",
//             ExpiresAt:  nil,
//             ExemptOperations: ["VIEW"],
//         }],
//         LastUpdated: "2024-03-15T10:30:05Z",
//     },
//     CrossChainStatus: {
//         "ethereum": {Synced: true, Status: "RESTRICTED"},
//         "arbitrum": {Synced: true, Status: "RESTRICTED"},
//         "optimism": {Synced: true, Status: "RESTRICTED"},
//     },
//     History: [/* Past regulatory action history */],
// }

Important Note: Even if a DeFi protocol does not respond to regulatory events, transfers of the asset are already blocked at the Oraclizer L3 level. However, protocol-level measures (such as excluding from collateral valuation) must be explicitly implemented by each protocol.


Conclusion: Complete Protocolization of Enforceability

In this research, we transformed the philosophical foundation of enforceability into actually implementable protocol specification.

For each of the six regulatory actions:

  • OIP message formats were defined to clearly encode regulatory intent
  • The OSS validation pipeline systematically validates authority, state, legal basis, and cross-chain consistency
  • The D-quencer priority system guarantees emergency regulatory actions are processed without delay
  • Three-phase cross-chain broadcast technically prevents the legal meaninglessness of partial execution

Combined with our asset state management framework, Oraclizer now possesses both state definition (what) and state modification (how).

“When code enforces law, that code must reflect the procedural legitimacy of law. No CONFISCATE without SEIZE, no SEIZE without FREEZE. This is due process implemented as protocol.”

In upcoming research, we will explore cross-chain address systems and ERC-3770 integration, examining how standardized chain-specific address formats enable seamless regulatory action execution across heterogeneous blockchain environments.


References

  1. Ethereum Improvement Proposals. (2021). EIP-3770: Chain-specific addresses. https://eips.ethereum.org/EIPS/eip-3770
  2. Financial Stability Board. (2023). Global Regulatory Framework for Crypto-asset Activities. https://www.fsb.org/2023/07/high-level-recommendations-for-the-regulation-supervision-and-oversight-of-crypto-asset-activities-and-markets-final-report/
  3. FATF. (2021). Updated Guidance for a Risk-Based Approach for Virtual Assets and Virtual Asset Service Providers. https://www.fatf-gafi.org/en/publications/Fatfrecommendations/Guidance-rba-virtual-assets-2021.html

Read Next

Our State Synchronization Research Is Now on Ethereum’s Research Forum
Two Oraclizer research posts now stand on ethresear.ch, Ethereum's research forum: machine-checked proofs that atomic cross-domain synchronization behaves correctly, and a follow-up giving synchronization strength a formal, checkable basis. In between, a public exchange with an Ethereum researcher sharpened the work into what it is now.
Oraclizer Core ⋅ Jul 21, 2026
The Cross-Domain State Preservation Functor: What It Closed, and What It Was For
The cross-domain state preservation proofs are closed: homomorphism, functor completion, a natural-transformation tower, and unconditional bounded convergence. Ten theory files, zero sorry. The harder work was catching what passes a prover while saying nothing: a trivial functor, a bypassed threshold, a witness outside the roster. Each retreat left the rest firmer.
Oraclizer Core ⋅ Jul 16, 2026
Sync Degree Enforcement: What Turns a Classification Into a Constraint
A classification does nothing on its own. Sync degree enforcement closes the gap between labeling an asset S2 and having the system refuse to touch it any other way. It rests on a two-directional result: over-provisioning preserves every guarantee, under-provisioning admits a state that defeats all of them. So the check leaves runtime and enters Rust's type system.
Oraclizer Core ⋅ Jul 16, 2026
ERC-8319: A Citable Regulatory Constitution for Tokenized Assets
The ERC-8319 Regulatory Compliance Protocol has entered Ethereum's standards repository. It defines the legal effect of six enforcement actions on tokenized assets, from a reversible freeze to an irreversible confiscation, and compiles 31 requirements from 15 regulators under five principles. A convention, not an interface, under one permanent, citable identifier.
Oraclizer Core ⋅ Jul 14, 2026
ERC-8319 Regulatory Compliance Protocol Enters the Ethereum Standards Process
Oraclizer's Regulatory Compliance Protocol has been submitted to ethereum/ERCs as ERC-8319 and is under editor review. Not a glossary but a reference bundling a taxonomy of six enforcement actions, their legal effect, the dynamics between them, and 31 regulator requirements, layered non-invasively on existing standards. Co-authored with Horizen Labs and Dan Spuller.
Oraclizer Core ⋅ Jul 11, 2026