The loan
You lock tokens in Hock and receive HYPE straight away. Three things are fixed when you sign and never change: how much you borrowed, the fee, and the date. There's no interest that grows, no health factor, and no price feed involved.
Terms run 1, 3, 7, 14 days. The fee is deducted from the loan, so the HYPE you receive is the loan minus the fee, and what you repay is the loan itself.
At the date
Before the date you can repay at any time and your tokens come back to you in the same transaction. After the date passes unpaid, the loan ends: the vault that funded it keeps your tokens and you keep the HYPE. You owe nothing further.
Nothing happens between signing and the date. If the price halves, nobody can sell your tokens. If it triples, the gain is still yours as long as you repay in time.
Sale value
Hock never asks what a token is worth on a chart. It asks the token's own pool what your exact amount would sell for right now, after slippage, using the same quoter any swap uses. That number is the sale value shown on your loan.
This is what makes lending without an oracle safe enough to try. Pumping a thin pool doesn't help a borrower much, because the sale value of a large position in a thin pool is small, and the loan is a fraction of that.
Loan-to-value and caps
Loan-to-value is a share of sale value, set per token by each vault's curator and lower for thinner pools. Hock caps it at 50% for any token. The live figures are on the Markets page, read from the contracts.
Each vault also sets how much it will lend against a token in total. On top of that, the core contract refuses a loan if the vault's outstanding loans against a token would exceed what all of that token's collateral could be sold for at once, times its loan-to-value. That keeps defaulted collateral small enough to actually sell.
Fees
| Term | Fee, share of the loan |
|---|---|
| 1 day | 1% |
| 3 days | 2.5% |
| 7 days | 5% |
| 14 days | 9% |
Fees go to the vault that funded the loan, less a protocol share of 10% that goes to the Hock treasury. Repaying early doesn't refund any of the fee.
Vaults and curators
Lenders deposit HYPE into vaults. Each vault is run by a curator who sets which tokens it takes, its loan-to-value limits and terms, within Hock's limits. Depositors earn the fees; when a loan defaults, depositors share its collateral.
A conservative vault takes deep pools at short terms and earns less. An aggressive one takes thinner tokens at low loan-to-value and charges for the risk. Pick the book you'd want to be.
$HOCK
$HOCK is live on volta.family with a 1% fee on every trade: 35% of it buys back and burns $HOCK, 35% is the creator share that Hock deposits into its vault and lends out as HYPE, and 30% goes to Volta. Hock's 10% share of every loan fee goes to $HOCK stakers: each core's treasury is the HockStaking contract at 0xbe31F5305167EE3cF2801d384160161AaEBc9aD5, which streams what arrives to stakers over seven days, in HYPE. It has no owner, and staking has no lockup. More on $HOCK.
Price checks
A loan is priced from a live sell quote, scaled back if the pool's price sits above a recent snapshot of it. That stops anyone pumping a pool in the same transaction to borrow more. A snapshot counts once it is a minute old and stops counting after 30 minutes.
Snapshots are taken by every loan and by anyone who asks. If a token has been quiet for half an hour, the borrow page offers a price check: one small transaction, then a minute's wait, and the loan can be opened.
Risks
Lenders can lose money. Borrowers may let a loan mature unpaid when the collateral is worth less than the loan, and lenders then hold that collateral, which may be hard to sell. Fees are set to cover this but are not guaranteed. Hock is unaudited software on a new chain, and vault deposits are capped while it is.
Elysium itself is new. Withdrawals from it to HyperEVM take about seven days, and its sequencer can delay transactions by up to a minute, so don't leave a repayment to the last minute.
Contracts
All on Elysium, chain id 99901. Elysium can't verify contract source yet, so the ABIs are published here as plain JSON that viem, ethers or cast can read as they are.
HockStaking
Stake $HOCK, earn the protocol's share of every loan fee in HYPE
0xbe31F5305167EE3cF2801d384160161AaEBc9aD5For tokens that trade on Uniswap's v4 PoolManager (Volta, Ascend, Montra and most launchpads):
Hock Volta Vault
ERC-4626 vault of wrapped HYPE that funds loans
0x141E0d88eD0C52700eeF5eb8C64265f4aa25cd94HockVaultLauncher
Opens a funded, configured project vault in one transaction
0x8a1Fa5B86bd7ad9a5F5aB6DC1e5B4f4DC16CA9e6For tokens that trade on ElysPump's own PoolManager:
Hock ElysPump Vault
ERC-4626 vault of wrapped HYPE that funds loans
0xA20DC61e529F1F0f750343084171230C3f7fdea9HockVaultLauncher
Opens a funded, configured project vault in one transaction
0xB383833F2d075F1548376640fB5d5B748075E196V4Quoter (ElysPump)
Uniswap's V4Quoter, bound to ElysPump's PoolManager
0x07C56161D805abB793E06E2d254DCa0D2152Ab08For tokens that trade on Kleos's own PoolManager:
Hock Kleos Vault
ERC-4626 vault of wrapped HYPE that funds loans
0xFD43E7a558fB097a00349e0D66Bf4A265C1C0891HockVaultLauncher
Opens a funded, configured project vault in one transaction
0xE8d7f04F2FaA7b83b9C551a1112Aa61081A9AB36V4Quoter (Kleos)
Uniswap's V4Quoter, bound to Kleos's PoolManager
0xEee51DAB3fC95f319f0fA4B66E29A0bA3303ed10HockBoost
$HOCK boost for Hock Volta Vault depositors: stake shares, earn $HOCK. No owner.
0xcD9e4106089F571547D6E6187d1237bB0f136f9cTokens Hock lends against, each through its own v4 pool:
Hock prices and sells through Uniswap's v4 deployment on Elysium (the PoolManager at 0xE2e105d3F7209A9DA11f83cCf3E7398a753823F1 and the V4Quoter at 0x379F556c5a748BD7Cc9baF0F7A344094Ac1814f5) and through ElysPump's own PoolManager at 0x5fb015a49f5b35073f9Ce01C2aDb287A75F89600 and Kleos's at 0xb308B5df891c300a5f5a5b8fdBC30108bA842490, each with Uniswap's quoter deployed for it. Vaults hold wrapped HYPE at 0xbA876237B0F021A6c6D852d6c26E107997cC5E81.
Project vaults
Anyone can open a vault for a listed token, and a token's team is the natural one to. Put HYPE behind the token and its holders can borrow against their bags instead of selling them. The vault belongs to the wallet that opened it: it earns every loan fee (less Hock's 10%), and its owner can change the LTV and limit, stop lending, or withdraw unlent HYPE at any time.
Open a project vault does it in one transaction through the HockVaultLauncher for the token's deployment: it creates the vault, sets the four term fees and the token's policy, deposits the HYPE sent with the call as the sender's deposit, and hands the vault to the sender. Borrowers always get the best offer across every vault that lends against their token.
For AI agents
Agents can use Hock with their own wallets. Give yours this line, in Claude Code, OpenClaw, Codex or any runtime that reads a skill file:
Read https://hock.markets/skill.md and follow it.
The skill file has the contracts, every call with examples, and the rules an agent follows: it only borrows or repays when its owner asks, confirms the terms first, and tracks each due date so a loan never expires by accident.
MCP server
Hock runs a hosted MCP server, with no key or sign-up. Add it to any MCP client as a Streamable HTTP server:
https://hock.markets/mcp
In Claude Code: claude mcp add --transport http hock https://hock.markets/mcp
It reads from the chain: list_markets (LTV, HYPE available, fees), quote_loan (exact HYPE received, HYPE to repay, due date) and get_loans (a wallet's loans and due dates). The build_* tools return unsigned transactions for a price check, approval, borrow or repayment. The server never holds keys or signs anything: your agent signs with its own wallet, and only when you ask it to.
Hock Agent 001
Hock Agent 001 is an agent with its own wallet that borrows against $HOCK every hour and repays early, using only the public MCP server above. It runs on a ten-minute schedule and sends at most one transaction per run. This is its full source; fork it for your own agent.
/**
* Hock Agent 001: an autonomous borrower that uses Hock exactly the way any outside agent would,
* through the public MCP server (hock.markets/mcp). Every hour it locks $HOCK, borrows HYPE on a
* one-day loan, and repays it early, so its repayment record builds in public on-chain.
*
* It runs on the Worker's cron every ten minutes. Each run does at most one thing: a price check,
* an approval, a borrow or a repayment, then exits; the next run sees the result. Its key is the
* Cloudflare secret HOCK_AGENT_KEY and never leaves the Worker.
*/
import {
createPublicClient,
createWalletClient,
defineChain,
erc20Abi,
formatEther,
parseEther,
type Address,
type Hex,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { HOCK_TOKEN, RPC_URL, deploymentOf, rpcTransport } from "../lib/deployment";
import { handleMcp } from "../mcp/server";
export const AGENT_NAME = "Hock Agent 001";
/** $HOCK it locks for each loan. */
export const COLLATERAL = "500000";
export const TERM_DAYS = 1;
/** More than the whole $HOCK supply, so one approval covers every loan. */
const APPROVE_ONCE = "1000000000000";
/** Repay a loan once it has been open this long. */
// runs land a few seconds early on the ten-minute marks; 35 min means the 40-minute run repays
const HOLD_S = 35 * 60;
/** Open the next loan once this long has passed since the last one opened. */
const EVERY_S = 55 * 60;
/** Never hold a loan closer than this to its due date. */
const REPAY_WINDOW_S = 6 * 3600;
type Tx = { chainId: number; to: Address; data: Hex; value: string };
type Plan = { action: string; why: string; tx?: Tx };
/** One MCP tool call, as the agent made it: for the public activity feed. */
export type Step = { tool: string; args: Record<string, unknown>; result: string; ms: number };
let rpcId = 0;
/** Call one of Hock's MCP tools, through the same JSON-RPC interface outside agents use. */
async function tool<T = Record<string, unknown>>(
trace: Step[],
name: string,
args: Record<string, unknown>,
summary: (r: T) => string,
) {
const t0 = Date.now();
const res = await handleMcp(
new Request("https://hock.markets/mcp", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
jsonrpc: "2.0",
id: ++rpcId,
method: "tools/call",
params: { name, arguments: args },
}),
}),
);
const j = (await res.json()) as {
result?: { isError?: boolean; structuredContent?: T; content?: { text: string }[] };
};
if (!j.result || j.result.isError) {
const msg = j.result?.content?.[0]?.text ?? "no result";
trace.push({ tool: name, args, result: `error: ${msg}`, ms: Date.now() - t0 });
throw new Error(`${name}: ${msg}`);
}
const out = j.result.structuredContent as T;
trace.push({ tool: name, args, result: summary(out), ms: Date.now() - t0 });
return out;
}
const elysium = defineChain({
id: 99901,
name: "Elysium",
nativeCurrency: { name: "HYPE", symbol: "HYPE", decimals: 18 },
rpcUrls: { default: { http: [RPC_URL] } },
});
export const client = createPublicClient({
chain: elysium,
transport: rpcTransport({ retryCount: 3 }),
});
/** Decide the one thing to do now. */
export async function plan(me: Address, trace: Step[] = []): Promise<Plan> {
const { loans } = await tool<{
loans: {
loan_id: string;
state: string;
seconds_left: number;
repay_hype: number;
opened: string;
}[];
}>(trace, "get_loans", { wallet: me }, (r) => {
const n = r.loans.filter((l) => l.state === "active").length;
return `${r.loans.length} loans, ${n} open`;
});
const now = Date.now() / 1000;
const open = loans.find((l) => l.state === "active");
if (open) {
const age = now - Date.parse(open.opened) / 1000;
if (age < HOLD_S && open.seconds_left > REPAY_WINDOW_S)
return { action: "wait", why: `loan ${open.loan_id} open ${Math.round(age / 60)} min` };
const r = await tool<{ transaction: Tx }>(
trace,
"build_repay",
{ loan_id: open.loan_id },
() => `unsigned tx, value ${open.repay_hype} HYPE`,
);
return {
action: "repay",
why: `repaying ${open.repay_hype} HYPE on ${open.loan_id}`,
tx: r.transaction,
};
}
// no open loan: borrow again on the hour
const last = Math.max(0, ...loans.map((l) => Date.parse(l.opened) / 1000));
const since = now - last;
// the price check must be 1 to 30 minutes old at borrow time, so it goes in one run early
if (since < EVERY_S - 10 * 60)
return { action: "wait", why: `next loan in ${Math.round((EVERY_S - since) / 60)} min` };
const core = deploymentOf(HOCK_TOKEN).core;
const allowed = await client.readContract({
address: HOCK_TOKEN,
abi: erc20Abi,
functionName: "allowance",
args: [me, core],
});
if (allowed < parseEther(COLLATERAL)) {
// approve once for every future loan, not just this one
const r = await tool<{ transaction: Tx }>(
trace,
"build_approve",
{ token: HOCK_TOKEN, amount: APPROVE_ONCE },
() => "unsigned tx",
);
return { action: "approve", why: "approving $HOCK for its loans", tx: r.transaction };
}
const q = await tool<{
ok: boolean;
reason?: string;
message?: string;
receive_now_hype?: number;
}>(trace, "quote_loan", { token: HOCK_TOKEN, amount: COLLATERAL, term_days: TERM_DAYS }, (r) =>
r.ok ? `receive ${r.receive_now_hype} HYPE now` : `not now: ${r.reason}`,
);
// no snapshot to price against, or the price ran up past the last one: record a fresh one
if (!q.ok && (q.reason === "needs_price_check" || q.reason === "PriceMoving")) {
const r = await tool<{ transaction: Tx }>(
trace,
"build_price_check",
{ token: HOCK_TOKEN },
() => "unsigned tx",
);
return {
action: "price_check",
why:
q.reason === "PriceMoving"
? "the price ran up since the last check, so it records a new one"
: "the quote needs a fresh price snapshot",
tx: r.transaction,
};
}
if (!q.ok) return { action: "wait", why: q.message ?? q.reason ?? "no quote" };
if (since < EVERY_S)
return { action: "wait", why: `next loan in ${Math.round((EVERY_S - since) / 60)} min` };
const r = await tool<{ transaction: Tx | null; reason?: string }>(
trace,
"build_borrow",
{ token: HOCK_TOKEN, amount: COLLATERAL, term_days: TERM_DAYS },
(b) => (b.transaction ? "unsigned tx" : `not now: ${b.reason}`),
);
if (!r.transaction) return { action: "wait", why: r.reason ?? "borrow not available" };
return {
action: "borrow",
why: `borrowing ${q.receive_now_hype} HYPE against ${COLLATERAL} $HOCK on a ${TERM_DAYS}-day loan`,
tx: r.transaction,
};
}
/** One cron run: plan, then sign and send at most one transaction. */
export async function tick(key: string | undefined, dryRun = false) {
if (!key) return { action: "off", why: "HOCK_AGENT_KEY is not set" };
const account = privateKeyToAccount(key as Hex);
const me = account.address;
// a transaction from the last run still in flight: let it land first
const [latest, pending] = await Promise.all([
client.getTransactionCount({ address: me, blockTag: "latest" }),
client.getTransactionCount({ address: me, blockTag: "pending" }),
]);
if (pending > latest) return { action: "wait", why: "previous transaction still pending" };
const p = await plan(me);
const out: Record<string, unknown> = { agent: me, ...p, tx: undefined };
if (!p.tx || dryRun) {
console.log(`[${AGENT_NAME}] ${p.action}: ${p.why}`);
return { ...out, tx: p.tx };
}
const balance = await client.getBalance({ address: me });
if (balance < BigInt(p.tx.value) + parseEther("0.002"))
return { ...out, action: "wait", why: `needs more HYPE (has ${formatEther(balance)})` };
const wallet = createWalletClient({ account, chain: elysium, transport: rpcTransport() });
const hash = await wallet.sendTransaction({
to: p.tx.to,
data: p.tx.data,
value: BigInt(p.tx.value),
});
console.log(`[${AGENT_NAME}] ${p.action}: ${p.why} → ${hash}`);
return { ...out, hash };
}
Network
- Network
- Elysium
- Chain id
- 99901
- RPC
- https://rpc.elysium.kinetiq.xyz
- Explorer
- https://explorer.volta.family
- Gas
- HYPE