HWAN

Architecture

HWAN is one Anchor program, a keeper that reads public prices, and this website. The program holds every dollar; the keeper and the website hold none.

Components

PartRuns whereHolds moneyJob
kimchi programSolanayes, in per-market USDC vaultsoffers, positions, reports, disputes, votes, settlement, claims
Keeperany machinenoreads Upbit, Bithumb and the ECB fix, computes S, calls report
Arbiters1 to 5 keys per marketnorecompute S from the same public data when a report is disputed
Website and /api/indexweb servernoshows the live index and the app; read-only

Accounts

All accounts are program-derived addresses (PDAs).

AccountSeedsContents
Config["config"]authority, arbiter set and threshold, fee routing
Market["market", expiry]expiry, floor, cap, fee_bps, bond, dispute and arbitration windows, oi_cap, open interest, status, report, dispute, votes, settlement
Vault["vault", market]USDC token account holding every locked margin, fee and bond of the market
Offer["offer", market, maker, nonce]side, price, units left
Position["position", market, owner]long units, short units

USDC is fixed in the program (EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v).

Instructions

The program lives on Solana mainnet at 42pubqJMjRpR6EK3bNZML9MFFVgqS7FXMQURsj7Vg7iK.

InstructionSignerEffect
initializeupgrade authoritycreates Config with the arbiter set
set_arbitersauthorityreplaces the arbiter set for markets opened afterwards
open_market(args)authorityopens one weekly series with expiry, floor, cap, fee_bps, bond, dispute_window, arbitration_window, oi_cap
post_offermakerlocks margin for one side at one price
take_offertakerlocks the opposite margin, both pay the fee, a long and a short come into being
cancel_offermakerreturns unfilled margin
report(value)anyone, with bondposts S after expiry
dispute(value)anyone, with bondchallenges inside the dispute window with a different value
vote(value)arbiterposts a recomputed S while a dispute is open
settleanyonefixes S once the windows allow it
claimanyone, pays the ownerpays long (S − floor) and short (cap − S) per unit to the owner's USDC account
claim_bondanyone, pays the ownerreturns what the outcome owes the reporter or challenger
sweep_feesanyonemoves collected fees to the fixed protocol accounts set at initialize
set_burn_routeauthoritynames the token and its Raydium CP-Swap pool for the buyback, once
buyback_burnanyonespends the burn share of swept fees on the token and burns what it buys
enable_tokensanyone, pays the mint rentcreates the series' LONG and SHORT position-token mints (Token-2022, name and symbol in the mint)
mint_pair(units)ownerlocks units × band plus one side's fee, mints units of LONG and of SHORT
redeem_pair(units)ownerburns units of each side and pays units × band back, at any time
redeem(side, units)ownerafter settlement, burns one side and pays long (S − floor) or short (cap − S) per unit; half the band each in a void series
close_token_mints(expiry)authoritycloses a series' two mints once every token is burned and returns their rent
close_marketauthoritycloses an empty market and returns rent (refused while any position token is outstanding)

Schedules enforced on chain

  • ExpiryNotOnSchedule: expiry must be 16:00 UTC (Korea), 17:00 UTC (India) or 20:00 UTC (Argentina) on a weekday.
  • BandInvalid: −10000 ≤ floor < cap ≤ 10000 ibp, and cap − floor ≤ 3000 ibp (a band up to 30 pp wide, anywhere within ±100 pp).
  • FeeTooHigh: at most 100 bps.
  • WindowTooShort: dispute window at least one hour, arbitration window at least 24 hours.