How it actually works
Deposits: CREATE2 proxies
Deposits work by sending USDC to a personal address on HyperCore. What arrives there is credited to you; there is no deposit call to make and nothing to approve. Deposit through the app - USDC sent to the same address on HyperEVM is not credited.
Your own deposit address
Every account has its own deposit address, computed from the wallet address with CREATE2 by the DepositProxyFactory:
proxy = last 20 bytes of keccak256(0xff ‖ factory ‖ keccak256(user) ‖ keccak256(initCode))
initCode = DepositProxy creation code ‖ abi.encode(exchange, user)
Every input is public, so anyone can recompute the address, and it can receive funds before the contract behind it exists. The contract is deployed when the first deposit is swept, either by our relayer’s deployer key or by the account itself; nobody else can deploy it.
The app registers the address with our sweeper before it sends anything, and checks its own computation against the server’s answer and against the chain; if they disagree, nothing is sent. Every route lands on the HyperCore side of the address: Hyperliquid transfers, Circle CCTP mints, and Across fills, which arrive through Circle’s HyperCore deposit contract on HyperEVM. A plain token transfer to the proxy on HyperEVM is not watched and is not credited.
The sweeper picks it up
Our sweeper reacts to signs that funds have arrived: indexed CCTP and HyperEVM deposits, HyperCore transfers into registered addresses, and checks requested by the app. When a deposit address holds at least 10 USDC, it drives two Exchange calls, both public:
- Request. The Exchange reads the proxy’s HyperCore balances. If the USDC sits in perp collateral, it asks the proxy to move it to spot and stops; the next request sweeps it. Otherwise it records a pending sweep (the amount and the proxy’s balance at that moment) and asks the proxy to send that amount to the Exchange’s HyperCore account through CoreWriter.
- Finalize. In a later block (at least 1 after the request), the Exchange credits the amount if, and only if, both checks hold: the proxy’s balance is now below the recorded one (only the Exchange can move it, so the send executed), and the Exchange’s own HyperCore balance exceeds everything it already owes by at least the amount (the funds arrived and are not credited to anyone yet).
Checking only the proxy’s balance would credit a send that failed after a fee was charged, or credit twice when a send is late and a new deposit arrives in the same window; requiring the funds to arrive closes both holes. If the checks do not pass yet, finalization is retried in the next blocks. A sweep that has not landed within 5 blocks is marked unconfirmed and its amount recorded; a reconciler can later credit it, but only up to the recorded amount, only from funds that verifiably arrived and are attributed to nobody, and only within 30 days. Every reconciliation is an on-chain event. That is why the app sends one deposit at a time and waits for it to be credited.
Funds usually reach your balance within a few blocks of arriving at your address; a first deposit also waits for its proxy to be deployed, and pays HyperCore’s one-time account activation of 1 USDC.
Why proxies, not direct deposits
Two reasons. The first is convenience: a deposit call would need an approval and a deposit, each with gas. A proxy turns a deposit into a plain transfer to an address, which works the same whether it comes from Hyperliquid, a Circle bridge or Across.
The second is isolation: each account’s proxy is a separate address, so nobody’s funds can mix with yours, and the sweeper handles each account on its own. A problem with one account’s deposit does not touch the others.
What you’re trusting
The proxies are deployed by one factory, with CREATE2 and fixed code. Anyone can derive the expected address for any wallet and check that the contract there matches: the factory, the salt and the code hash are public. If the code ever changed, the derived address would change with it; your old address would hold no contract rather than silently point at different code. The factory and the proxy cannot be upgraded.
Only the Exchange can make a proxy send USDC, and only to the Exchange. Anything sent to the wrong place can be recovered by you alone: tokens or HYPE that reach the proxy contract on HyperEVM with withdrawStuckToken and withdrawStuckNative, called from your wallet on the proxy, and tokens other than USDC that reach its HyperCore account with rescueProxyCoreToken, called from your wallet on the Exchange.