18. Withdrawal Lifecycle
Outgoing tx 의 customer-visible projection · operation vocabulary · chain-specific quirkWithdrawal = Outgoing Transaction
Withdrawal 은 transaction state machine 의 customer-visible projection. 13. Transaction State 의 17 status outgoing flow 와 동등 — 본 페이지는 운영 동사 + chain quirk 중심.
Transaction Operations by Status (★ Stage 9)
transaction-lifecycle.md, p.2:
| Operation | 가능 status | 비고 |
|---|---|---|
| Cancel | Broadcasting 이전 | 이후엔 chain 에 broadcast 됨 |
| Retry | FAILED 후 | 동일 tx 재시도 |
| Boost / drop (EVM) | Broadcasting | EVM gas parameter 변경 — Replace-By-Fee (RBF) |
| Boost UTXO tx | Broadcasting | Bitcoin RBF |
| Rescreen / bypass AML | PENDING_AML_SCREENING / AML rejected | 재검사 또는 강제 진행 |
| Dismiss | — | transaction card UI dismissal |
| Edit transaction notes | any | — |
Chain-specific Withdrawal Quirk (★ Stage 9)
- EVM: nonce 가 순차 — 단일 vault stuck 시 전체 queue 정체 → multiple withdrawal vault round-robin
- Bitcoin: unconfirmed input 25-tx chain limit (Bitcoin Core default) → multiple withdrawal vault
- Solana: vault account 당 동시 5 tx queue (6번째 이상 Submitted max 2h 대기 후 terminated)
- Algorand: ~50min signing window
- Tezos: ~30min + mempool 1 tx/account
- Polkadot: tx valid 2 hours
Chain-specific 시간 제약과 Approval 경합
Owner / Admin Quorum approval 흐름과 시간 경합 가능
Algorand / Tezos / Polkadot 등의 짧은 signing window 가 Q+O / AG 흐름의 7-day expiry 보다 훨씬 짧음 — approval 받는 동안 chain-side validity expire 가능. DB 측은 chain_validity_expires_at 추적 + alert 필요.
Solana Sign-Only (★ Stage 9)
- Signed payload client 에 반환 (Fireblocks 가 broadcast 안 함)
NOT BROADCAST BY FIREBLOCKS태그 자동- on-chain 감지 시 Completed / Failed transition
- Timeout 미달성 시 자동 invalidate
Raw Signing Special Path
- 일반: Pending Signature → (broadcast 없음) → Completed
- Cached signature (동일 raw data 재서명): Submitted → Completed (전체 ceremony 우회)
DB Schema
CREATE TABLE withdrawals (
-- transaction 의 sub-aggregate. 1:1 with transaction 의 outgoing 경우.
id BINARY(16) PRIMARY KEY,
transaction_id BINARY(16) NOT NULL UNIQUE,
withdrawal_vault_id BINARY(16) NOT NULL,
destination_type ENUM('vault', 'exchange', 'whitelisted', 'one-time', 'network'),
destination_address VARCHAR(128) NOT NULL,
tag_or_memo VARCHAR(64),
chain VARCHAR(32) NOT NULL,
chain_validity_expires_at DATETIME(6), -- chain-specific validity (Algorand/Tezos/Polkadot)
retry_count INT NOT NULL DEFAULT 0,
last_boost_at DATETIME(6), -- RBF
cancel_attempted_at DATETIME(6),
cancel_completed_at DATETIME(6), -- ★ set-once
KEY (withdrawal_vault_id),
KEY (chain_validity_expires_at)
);
-- ★ append-only — broadcast 시도마다 row
CREATE TABLE broadcast_attempts (
id BINARY(16) PRIMARY KEY,
withdrawal_id BINARY(16) NOT NULL,
attempt_number INT NOT NULL,
raw_payload_cbor BLOB NOT NULL, -- ★ set-once
signed_payload BLOB NOT NULL, -- ★ set-once
broadcasted_to_node VARCHAR(128) NOT NULL,
broadcasted_at DATETIME(6) NOT NULL,
tx_hash VARCHAR(128), -- ★ set-once (node 가 echo)
outcome ENUM('submitted', 'rejected', 'dropped', 'replaced') NOT NULL,
UNIQUE KEY (withdrawal_id, attempt_number)
);
-- ★ append-only — confirmation 누적
CREATE TABLE confirmations (
id BINARY(16) PRIMARY KEY,
broadcast_attempt_id BINARY(16) NOT NULL,
block_hash VARCHAR(128) NOT NULL,
block_height BIGINT NOT NULL,
confirmed_at DATETIME(6) NOT NULL,
reorged_at DATETIME(6), -- reorg 시 set (row 자체는 유지)
KEY (broadcast_attempt_id, block_height)
);
-- 운영 액션 이력
CREATE TABLE withdrawal_operations (
-- append-only
id BINARY(16) PRIMARY KEY,
withdrawal_id BINARY(16) NOT NULL,
operation ENUM('cancel', 'retry', 'boost', 'drop',
'rescreen-aml', 'bypass-aml', 'dismiss', 'edit-notes') NOT NULL,
actor_user_id BINARY(16) NOT NULL,
parameters_cbor BLOB, -- e.g., new gas params
occurred_at DATETIME(6) NOT NULL,
KEY (withdrawal_id, occurred_at)
);
Round-Robin Withdrawal Vault 운영
EVM / Bitcoin 의 chain-specific 한 sequential bottleneck 회피:
workspace
└── withdrawal vaults (N 개)
├── vault A → EVM nonce A0, A1, A2, ...
├── vault B → EVM nonce B0, B1, B2, ...
├── vault C → EVM nonce C0, C1, C2, ...
→ application layer 의 vault selector 가 round-robin 으로 vault 할당
→ 한 vault 의 stuck tx 가 전체 queue 막지 않음