typescript
import { z } from "zod";
import { store } from "opentool/store";
import { wallet } from "opentool/wallet";
import {
resolveHyperliquidChainConfig,
withdrawFromHyperliquid,
} from "opentool/adapters/hyperliquid";
export const profile = {
description:
"Withdraw USDC from Hyperliquid back to an on-chain address via withdraw3.",
};
const decimalString = z
.string()
.min(1, "amount is required")
.refine((v) => /^\d+(?:\.\d+)?$/.test(v), "must be a decimal string");
export const schema = z.object({
amount: decimalString,
destination: z
.string()
.min(1, "destination is required")
.refine(
(v) => /^0x[a-fA-F0-9]{40}$/.test(v),
"destination must be a hex address"
),
environment: z.enum(["mainnet", "testnet"]).default("testnet"),
});
export async function POST(req: Request): Promise<Response> {
const body = await req.json().catch(() => ({}));
const { amount, destination, environment } = schema.parse(body);
const chainConfig = resolveHyperliquidChainConfig(environment);
const context = await wallet({
chain: chainConfig.chain,
});
const walletAddress = context.address;
const withdraw = await withdrawFromHyperliquid({
amount,
destination: destination as `0x${string}`,
environment,
wallet: context,
});
const eventEnvironment =
environment === "mainnet" ? "hyperliquid" : "hyperliquid-testnet";
await store({
source: "hyperliquid",
ref: `${withdraw.nonce}`,
status: "submitted",
walletAddress,
action: "withdraw",
notional: amount,
network: eventEnvironment,
market: {
market_type: "system",
venue: "hyperliquid",
environment: eventEnvironment,
canonical_symbol: `system:hyperliquid:${eventEnvironment}:withdraw`,
protocol_market_id: "withdraw",
raw_symbol: "USDC",
},
metadata: {
environment,
destination,
nonce: withdraw.nonce,
status: withdraw.status,
},
});
return Response.json({
ok: true,
environment,
walletAddress,
withdraw,
});
}