13. Transaction — 17 Primary Status State Machine

Stage 9 정식 명세 · color-coded · 시간 제약 · chain-specific quirk

17 Primary Status (color-coded)

ColorStageStatus (API code)
🟡 Yellow입력/검증SUBMITTED / PENDING_AML_SCREENING / PENDING_ENRICHMENT / PENDING_AUTHORIZATION / QUEUED
🟡 Yellow서명PENDING_SIGNATURE / SIGNED (Solana-only) / CANCELLING
🔵 Blue외부 처리 (3rd party)PENDING_3RD_PARTY_MANUAL_APPROVAL / PENDING_3RD_PARTY
🔵 BlueblockchainBROADCASTING / CONFIRMING
🟢 Green종결 (성공)COMPLETED
🔴 Red종결 (실패)CANCELLED / BLOCKED / REJECTED / FAILED

Outgoing Transaction Flow

stateDiagram-v2
  [*] --> SUBMITTED
  SUBMITTED --> PENDING_AML_SCREENING : AML/Travel Rule enabled
  PENDING_AML_SCREENING --> PENDING_ENRICHMENT
  SUBMITTED --> PENDING_ENRICHMENT : AML disabled
  PENDING_ENRICHMENT --> PENDING_AUTHORIZATION : dApp Protection done
  PENDING_AUTHORIZATION --> QUEUED : approvals collected
  PENDING_AUTHORIZATION --> FAILED : 2h timeout
  QUEUED --> PENDING_SIGNATURE
  PENDING_SIGNATURE --> SIGNED : Solana sign-only
  PENDING_SIGNATURE --> BROADCASTING
  PENDING_SIGNATURE --> CANCELLED : co-signer reject
  PENDING_SIGNATURE --> FAILED : 2h timeout
  BROADCASTING --> CONFIRMING : ~1 min typical
  CONFIRMING --> COMPLETED
  CONFIRMING --> FAILED
  CANCELLING --> CANCELLED
  CANCELLING --> COMPLETED : already broadcast
      
Figure 4. Outgoing transaction state machine — 17 status. AML/Travel Rule optional, 2h timeout on Pending Authorization + Pending Signature.

Incoming Transaction Flow

stateDiagram-v2
  [*] --> START
  START --> PENDING_AML_SCREENING : AML/KYC enabled
  START --> BROADCASTING : network conn / exchange / gas station
  START --> CONFIRMING : AML/KYC disabled
  PENDING_AML_SCREENING --> REJECTED : reject
  PENDING_AML_SCREENING --> PENDING_3RD_PARTY
  PENDING_3RD_PARTY --> CONFIRMING
  CONFIRMING --> COMPLETED
  CONFIRMING --> FAILED
  CONFIRMING --> REJECTED : user-initiated freeze via API
      
Figure 5. Incoming transaction state machine — rejected incoming 의 asset 은 Admin unfreeze 까지 사용 불가.

시간 제약

제약적용 상태동작
2 hoursPending Authorizationtimeout → fail
2 hoursPending Signaturetimeout → fail
30 secondsCancelling (typical)길어지면 Status page 확인
1 minuteBroadcasting (typical)길어지면 Status page 확인
2 hoursSolana 6번째 이상 tx Submitted만료 → terminated

Chain-Specific 처리 모델

  • EVM-compatible: 동일 vault account 의 EVM tx 들은 blockchain-standard 단위 직렬화 (Ethereum + Polygon → 순차, BTC + Solana → 병렬)
  • Solana: vault account 당 동시 5 tx queue (6번째 이상은 Submitted max 2h 대기)
  • EVM withdrawal: nonce 충돌 위험 → multiple withdrawal vault 권장 round-robin
  • Bitcoin withdrawal: unconfirmed input 25-tx chain limit (Bitcoin Core default) → multiple withdrawal vault

Special Branches

  • Approvers Unanimous-Veto Rule: "If at least one person chooses to reject, the transaction is rejected." Approvers N명 → reject 1표 = tx 즉시 rejected
  • Outgoing rejected: 자산 즉시 사용 가능 (re-tx 가능)
  • Incoming rejected: 자산이 Admin 가 unfreeze 할 때까지 사용 불가
  • Raw Signing (cached signature): 동일 raw data 재서명 시 Submitted → Completed (전체 ceremony 우회)
  • Solana Sign-Only: signed payload client 에 반환, Fireblocks 가 broadcast 안 함, NOT BROADCAST BY FIREBLOCKS 태그

DB Schema — Transaction State

CREATE TABLE transactions (
  id                  BINARY(16) PRIMARY KEY,
  workspace_id        BINARY(16) NOT NULL,
  source_vault_id     BINARY(16),
  destination_type    ENUM('vault', 'exchange', 'whitelisted', 'one-time', 'network'),
  asset_id            VARCHAR(32),
  amount_decimal      DECIMAL(38, 18),
  chain               VARCHAR(32),
  status              ENUM('SUBMITTED', 'PENDING_AML_SCREENING', ...) NOT NULL,
  status_changed_at   DATETIME(6) NOT NULL,
  created_at          DATETIME(6) NOT NULL,
  raw_payload_cbor    BLOB,                    -- ★ append-only (TEE-signed)
  tx_hash             VARCHAR(128),            -- ★ set-once (broadcast 시점)
  pending_auth_at     DATETIME(6),             -- 2h timeout 산정
  pending_sign_at     DATETIME(6),
  KEY (workspace_id, status)
);

-- 상태 전이는 별도 테이블 (append-only)
CREATE TABLE transaction_events (
  id              BINARY(16) PRIMARY KEY,
  transaction_id  BINARY(16) NOT NULL,
  from_status     VARCHAR(32),
  to_status       VARCHAR(32) NOT NULL,
  reason          VARCHAR(255),
  occurred_at     DATETIME(6) NOT NULL,
  KEY (transaction_id, occurred_at)
);