A guaranteed-settlement layer for FXRP redemptions on Flare β now with a destination-tag redemption lane.
π Built for the Flare Summer Signal hackathon. Project submission (BUIDL): dorahacks.io/buidl/46944.
Harbor sits between an FXRP redeemer and the agent that owes them XRP, ensuring that missed payments automatically trigger FAssets default recovery. Because it never custodies funds, recovered collateral is always paid directly to you.
| Live demo | harbor-web-olive.vercel.app |
| Backend API | api-production-6f3ec.up.railway.app |
| Walkthrough | βΆ Watch the new demo video on YouTube |
βΆ Watch the demo video β redeem β watch XRPL β settle, or prove non-payment β execute default β recover collateral.
Note: High-res video assets are also available in the repository at assets/loom/video.mp4 (90-second walkthrough) and assets/demo/harbor-demo.mp4 (2-minute product demo).
- Non-custodial by construction: Harbor acts as the nominated executor. You redeem directly on the
AssetManager, so default collateral is paid to you. - Two Redemption Lanes:
- Standard (
redeemAmount): Uses theReferencedPaymentNonexistenceproof andHarborRedeemer.executeDefault. - Redeem-by-tag (
redeemWithTag): For XRPL destinations requiring a tag. Uses the XRP-nativeXRPPaymentNonexistenceproof andHarborRedeemer.executeXrpDefault. (Tag0is valid; an empty input uses the standard lane. Automatically disabled ifredeemWithTagSupported()is false).
- Standard (
- No agent selection: FAssets handles agent assignment via FIFO.
- Permissionless self-recovery: Anyone can complete a stuck default directly from the UI using the pre-built FDC proof bytes.
- Heuristic reliability scoring: Ranks agents (
agent-reliability-mvp-v1) based on fulfillment, speed, availability, and FTSOv2 collateral ratios. Purely informational analytics. - Fast XRPL settlement observer: Validates
netUnderlyingUBA(valueUBA - feeUBA) and destination tags directly from the ledger. - Durable indexing: FAssets indexer recovers events across RPC gaps via a persisted cursor.
- Zero-config local dev: Everything boots in "mock mode" with local defaults.
Harbor is a Next.js 14 / Node.js 22 pnpm monorepo. The core system operates on the Flare Coston2 testnet.
flowchart LR
R["Redeemer"] -->|"redeemAmount / redeemWithTag"| AM["FXRP AssetManager"]
AM -->|"Redemption(WithTag)Requested"| IDX["Indexer"]
AG["Agent"] -->|"pays XRP (with tag if WITH_TAG)"| XRPL[("XRP Ledger")]
OBS["XRPL Observer"] -->|"validates payment"| XRPL
KEEP{"Keeper"} -->|"watch window"| OBS
KEEP -->|"expired: prove non-payment"| FDC[("Flare Data Connector")]
FDC -->|"Merkle proof"| KEEP
KEEP -->|"execute(Xrp)Default"| HR["HarborRedeemer"]
HR -->|"forward default"| AM
AM -->|"collateral"| R
sequenceDiagram
autonumber
actor R as Redeemer / UI
participant AM as FXRP AssetManager
participant AG as FAsset Agent
participant XRPL as XRP Ledger
participant OBS as XRPL Observer
participant KEEP as Keeper
participant API as Read API
participant FDC as Flare Data Connector
participant DA as DA Layer
participant HR as HarborRedeemer
R->>AM: redeemAmount / redeemWithTag (nominate HarborRedeemer)
AM-->>API: RedemptionRequested / RedemptionWithTagRequested
Note over KEEP: status REQUESTED β WATCHING
AG->>XRPL: pay net underlying XRP (with tag if WITH_TAG)
loop poll window
OBS->>XRPL: read agent address
end
OBS->>OBS: validate destination, reference, net amount, window, tag
alt Agent Pays (Settlement)
OBS-->>API: write settlement receipt
KEEP->>API: read valid observation
Note over KEEP: status β SETTLED
R->>API: GET /redemptions/:id
else Window Expires (Default Recovery)
Note over KEEP: window expired, no valid payment β WINDOW_EXPIRED
alt STANDARD Lane
KEEP->>FDC: submit ReferencedPaymentNonexistence request
else WITH_TAG Lane
KEEP->>FDC: submit XRPPaymentNonexistence request
end
Note over KEEP: status β REQUEST_PROOF
KEEP->>FDC: track voting round to finalization
KEEP->>DA: retrieve response + Merkle proof
Note over KEEP: status β PROOF_READY
alt Keeper Submits Default
alt STANDARD Lane
KEEP->>HR: executeDefault(proof, requestId)
HR->>AM: redemptionPaymentDefault(proof, requestId)
else WITH_TAG Lane
KEEP->>HR: executeXrpDefault(proof, requestId)
HR->>AM: xrpRedemptionPaymentDefault(proof, requestId)
end
else Permissionless Self-Recovery (Keeper Offline)
R->>API: GET /redemptions/:id (fdcProofs[])
R->>HR: executeDefault / executeXrpDefault (proof, id)
HR->>AM: redemptionPaymentDefault / xrpRedemptionPaymentDefault
end
AM-->>R: collateral to recorded redeemer
AM-->>HR: return executor fee
HR-->>KEEP: forward fee to caller
Note over KEEP: status β DEFAULT_SUBMITTED β RECOVERED
end
The keeper evaluates requests deterministically. Front-running it via self-recovery is harmless.
stateDiagram-v2
[*] --> REQUESTED
REQUESTED --> WATCHING: window open
WATCHING --> SETTLED: valid XRPL payment observed
REQUESTED --> WINDOW_EXPIRED: window already passed
WATCHING --> WINDOW_EXPIRED: window passed, no payment
WINDOW_EXPIRED --> REQUEST_PROOF: submit FDC request
REQUEST_PROOF --> PROOF_READY: voting round finalized
PROOF_READY --> DEFAULT_SUBMITTED: execute(Xrp)Default sent
DEFAULT_SUBMITTED --> RECOVERED: default confirmed on-chain
PROOF_READY --> RECOVERED: default confirmed (front-run harmless)
SETTLED --> [*]
RECOVERED --> [*]
REQUESTED --> FAILED
WATCHING --> FAILED
WINDOW_EXPIRED --> FAILED
REQUEST_PROOF --> FAILED
PROOF_READY --> FAILED
DEFAULT_SUBMITTED --> FAILED
FAILED --> [*]
Formula (agent-reliability-mvp-v1) clamped to [0, 100]:
fulfillment (β€ 45) = fulfillment_rate Β· 45 (22.5 if no history)
settlement_time (β€ 15) = based on average settlement seconds (fast β€ 1h, slow β₯ 24h)
availability (β€ 20) = from published availability + free lots
collateral (β€ 20) = from agent's collateral ratio (floor 120% ... full 200%)
default_penalty (β€ 20) = min(defaults Β· 5, 20) (subtracted)
Scores are heuristic and never influence the protocol's FIFO agent assignment. Identity data (name, icon) is parsed directly from AgentOwnerRegistry.
Public, GET-only API providing read-only access to reliability and settlement status:
GET /agents?asset=FXRPβ Heuristic agent leaderboardGET /redemptions/:idβ Evidence-based timeline
Drop-in Integrations:
integration/harbor-widget.html: Zero-dependency embeddable leaderboard.integration/HarborAgentReliability.tsx: Configurable React component.- See
integration/INTEGRATION.mdfor full field definitions.
Requires Node.js 22+, pnpm 10, and Foundry (for contract checks).
pnpm install
cp .env.example .env
pnpm --filter @harbor/api dev # Starts API on :3001
pnpm --filter @harbor/web dev # Starts Next.js console on :3000
pnpm check # Format, typecheck, forge testConfigured via .env defaults:
HARBOR_RUN_API(on)HARBOR_RUN_MIGRATIONS(on)HARBOR_RUN_INDEXER,HARBOR_RUN_XRPL_OBSERVER,HARBOR_RUN_AGENT_REFRESH,HARBOR_RUN_KEEPER(off)
- Foundry (Contracts):
pnpm check:contracts(3 suites) - API (node:test):
pnpm testin@harbor/api(16 suites) - Web (Vitest):
pnpm testin@harbor/web(19 suites) - E2E (Playwright):
pnpm test:e2ein@harbor/web(7 suites) - Live Coston2 E2E: Located in
test/e2e(harbor-e2e.ts,harbor-tag-e2e.ts)
HarborRedeemer requires Solidity 0.8.25 and exposes a secure receive() hook, executeDefault, and executeXrpDefault to forward the respective proofs and refund fees. It resolves AssetManagerFXRP on initialization.
# Broadcast deployment
RPC_URL_COSTON2=... DEPLOYER_PRIVATE_KEY=... KEEPER_EXECUTOR_ADDRESS=... pnpm deploy:harbor:coston2
# Regen ABIs
pnpm protocol:generate-harbor-abiDeployed Addresses:
- HarborRedeemer:
0x82f39361FFb1a438e4EBF8025efa06e4511b02b5 - FXRP AssetManager:
0xc1Ca88b937d0b528842F95d5731ffB586f4fbDFA - FXRP Token (FTestXRP):
0x0b6A3645c240605887a5532109323A3E12273dc7
Licensed under Apache 2.0.
