Production에는 escrow 컨트랙트가 배포되어 있지 않습니다. Production 정산은 수동입니다. 포스터가 각 당첨자에게 Base USDC를 직접 보내고 트랜잭션 해시를 기록합니다(결제 레일 참고). escrow 코드 경로(deposit, lock, settle, refund)는 존재하지만, 처음부터 끝까지 실행된 곳은 Preview 배포의 Base Sepolia와 Arc Testnet, 테스트 USDC뿐입니다.
실제 게이트 확인#
curl -s https://birdfury.com/api/escrow/readiness | jq '.ready, .mode, .escrowAddress, .blockers'Production에서는 HTTP 503과 함께 ready: false, mode: "manual_alpha", escrowAddress: null, network: "base", 차단 사유 mission_escrow_address_missing이 반환됩니다. 응답에는 settlementMode: "manual_settlement"와 fundsLocked: false도 들어 있습니다. readiness는 미션 자금이 잠겼다고 주장하지 않습니다.
ready: true가 되려면 유효한 escrow 주소, 해당 네트워크의 유효한 USDC 주소, 고정된 Base chain id와 USDC 주소가 바뀌지 않았을 것, 그리고 실행 시점에 올바른 체인의 RPC가 그 주소에서 배포된 바이트코드를 찾고 usdc()와 operations()가 설정과 일치해야 합니다.
미션의 자금 상태가 뜻하는 것#
| 필드 | 의미 |
|---|---|
funded / fundingUrl | 포스터가 올린 링크. Birdfury는 확인하지 않음. escrow lock이 절대 아님 |
moneyTruth | escrow가 실제로 한 일: NONE · MANUAL_ALPHA · LOCKED · RELEASED · REFUNDED · DRIFT |
settlement.mode | manual_alpha 또는 escrow, 미션별 escrow 슬롯으로 판단 |
각 escrow 라벨에는 해당 트랜잭션이 필요합니다. LOCKED에는 lock tx, RELEASED에는 지급 tx, REFUNDED에는 환불 tx입니다. escrow 주소가 있는 배포라도 잠긴 슬롯이 없는 미션은 수동으로 지급됩니다.
Preview 테스트넷#
| 네트워크 | Chain ID | Escrow | 토큰 |
|---|---|---|---|
| Base Sepolia | 84532 | Preview 전용 | 테스트 USDC (소수점 6자리) |
| Arc Testnet | 5042002 | Preview 전용 | ERC-20 USDC 0x3600… (소수점 6자리). 가스용 네이티브 USDC는 18자리이며 escrow 토큰으로 쓰이지 않음 |
| Base 메인넷 | 8453 | 배포되지 않음 | — |
ESCROW_NETWORK가 Preview 레일(base-sepolia 또는 arc-testnet)을 고릅니다. Production은 escrow 주소 없이 base로 유지됩니다. 테스트넷 USDC는 가치가 없습니다. 제목이 [TESTNET]이거나 test: true인 미션은 QA용입니다. RPC, 탐색기, Preview 컨트랙트 주소는 네트워크를 참고하세요.
BASE_BUILDER_CODE가 설정되면(Preview는 bc_ybtb70ew) Base Sepolia의 escrow 호출 calldata 끝에 ERC-8021 attribution suffix가 붙습니다. Arc Testnet 호출에는 붙지 않습니다. 코드 값만 설명할 뿐, Base 대시보드 등록에 대한 주장은 아닙니다.
escrow 경로의 동작 (Preview)#
Prepare#
POST /api/missions/[id]/fund/prepare가 체인, USDC 토큰, escrow 주소, 정확한 금액을 돌려줍니다. readiness가 통과되지 않으면 503으로 거절합니다.
Deposit#
포스터가 자기 지갑에서 USDC approval과 escrow deposit에 서명합니다. 금액은 보상액뿐이며 위에 더해지는 것은 없습니다.
Confirm#
트랜잭션 해시와 함께 POST /api/missions/[id]/fund/confirm을 호출합니다. 서버는 영수증을 읽어 escrow의 Deposited 이벤트와 일치하는 USDC 전송을 확인한 뒤 슬롯을 lock tx와 함께 LOCKED로 표시합니다.
Settle#
검토가 끝나면 정산 워커가 가중 배분으로 계획을 세우고 settle()을 호출합니다. 잠긴 미션에서는 몫의 합이 보상 전체와 같아야 합니다. 합이 모자라면 컨트랙트가 나머지를 수수료로 지급하게 되므로 거절됩니다. 아무도 쓸 수 없는 수취 주소도 거절됩니다.
Refund#
만료되었고 당첨자가 없는 잠긴 미션은 deadline + ESCROW_REFUND_GRACE_MS 뒤에 입금자에게 환불될 수 있습니다. Production에서는 ESCROW_REFUND_POLICY_APPROVED=true가 설정되기 전까지 환불이 disabled이고, Preview 테스트넷은 기본값이 유예 기간 없는 승인 상태입니다.
모든 금액은 정수 micro-USDC 단위로 검사합니다. 워커는 멱등적이라 이미 정산된 미션에 다시 지급하지 않습니다.
정산 워커는 /api/cron/escrow-settle이며, Birdfury 운영 머신의 launchd 잡이 10분마다 호출합니다(vercel.json에 crons 키가 없어 Vercel에서 호출하는 스케줄러는 없습니다). Production에서는 readiness 차단 사유와 함께 503 blocked를 반환하고 아무것도 하지 않습니다.
안전 경계#
- 산술은 컨트랙트가 강제합니다. 보상과 수수료의 합이 그 미션의 입금액과 같지 않으면
settle()이 revert되고, 수수료는 배포 시 고정된operations주소로만 갑니다. 정산 에이전트 키는 자기 가스비만 쓸 수 있고 다른 잔액에는 닿지 못합니다. - 트랜잭션에 서명할 수 있는 서버 코드는 escrow 정산 cron 하나뿐입니다. 그 밖의 Birdfury 지갑은 받기만 합니다.
- Operations 지갑은 수수료를 받는 곳이지 escrow가 아닙니다. 일반 지갑 주소를
NEXT_PUBLIC_MISSION_ESCROW_ADDRESS에 넣으면 안 됩니다. - 알파 기간에는 수수료 징수가 꺼져 있어 지금 settle의 수수료는 0입니다.
현재 escrow 설정은 공개되어 있습니다. GET https://birdfury.com/api/escrow/readiness 가 네트워크, escrow 주소(운영 환경에서는 null), 막힌 항목을 비밀 값 없이 보여 줍니다.
갱신 2026-10-27