OverviewRecipes

Hyperliquid Transactions Quickstart

By the end of this tutorial, you’ll have an application integrated with Alchemy Wallets, enabling email and social login for user authentication, and the ability to sign and send transactions seamlessly.


Getting Started Instructions

1. Create a Custom Chain Config

Extend viem’s custom chains to create a chain definition for HyperEVM, as it’s not a defined chain on viem yet. This is the chain config you’ll be passing into your viem client in step #3.

1import { defineChain } from "viem";
2
3export const hype = defineChain({
4 id: 999,
5 name: "Hype",
6 nativeCurrency: {
7 decimals: 18,
8 name: "Hype",
9 symbol: "HYPE",
10 },
11 rpcUrls: {
12 default: {
13 http: ["https://hyperliquid-mainnet.g.alchemy.com/v2/{API_KEY}"],
14 webSocket: ["wss://hyperliquid-mainnet.g.alchemy.com/v2/{API_KEY}"],
15 },
16 },
17 blockExplorers: {
18 default: { name: "Explorer", url: "https://hyperevmscan.io/" },
19 },
20});

Wrap it in defineAlchemyChain to create an alchemy chain to pass into your config.tsx

1import { defineAlchemyChain } from "@account-kit/infra";
2
3const chain = defineAlchemyChain({
4 chain: hype,
5 rpcBaseUrl: `https://hyperliquid-mainnet.g.alchemy.com/v2/API_KEY`,
6});
7
8...
9//config.tsx
10export const config = createConfig(
11 {
12 transport: alchemy({ apiKey: "API_KEY" }),
13 chain: chain,
14 ssr: true, // more about ssr: https://www.alchemy.com/docs/wallets/react/ssr
15 storage: cookieStorage, // more about persisting state with cookies: https://www.alchemy.com/docs/wallets/react/ssr#persisting-the-account-state
16 enablePopupOauth: true, // must be set to "true" if you plan on using popup rather than redirect in the social login flow
17 policyId: "policy_id",
18 },
19 uiConfig
20);

2. Set Up Web2 Login with an Alchemy Signer

Working with React?

Follow this Quickstart guide to set up a new project.

The most important step is getting your API Key (NEXT_PUBLIC_ALCHEMY_API_KEY) and ensuring your project is configured correctly on your dashboard. Make sure you have Hyperliquid enabled as a network on your app.

Hyperliquid account config Hyperliquid dashboard support

Next, navigate to your page.tsx, and get the embedded EOA address using useSigner(). This new embedded EOA, also known as a signer, will be where users assets live and will sign transactions.

1const signer = useSigner();

Note: in order access your embedded EOA created by Alchemy Signer, you need to have finished authentication. To check your authentication status, you can check useSignerStatus(). For example:

1...
2 if (signerStatus.isConnected && signer) {
3 const address = signer.getAddress();
4 console.log("Connected signer address:", address);
5 }
6 ...

Not Working With React?

That’s okay! We have lower level methods we can leverage here to access your signer!

You can follow this Quickstart, to use our ‘@account-kit/signer’ package directly to create and use our wallets.

Create a Signer Instance

1import { AlchemyWebSigner } from "@account-kit/signer";
2
3export const signer = new AlchemyWebSigner({
4 client: {
5 connection: {
6 apiKey: "API_KEY",
7 },
8 iframeConfig: {
9 iframeContainerId: "alchemy-signer-iframe-container",
10 },
11 },
12});

Authenticate a User

Next, you’ll need to authenticate your user before you can use the signer. In this example, we use email auth, but we support a number of other auth methods. Check out our guides to complete authentication.

1import { signer } from "./signer";
2
3const result = await signer.authenticate({
4 type: "email",
5 email: "[email protected]",
6});
7...

Once you finish authenticating, you can access your signer!

3. Send Transactions

Got your signer/embedded EOA address? Now you are ready to send transactions! Alchemy signer supports signing messages as raw hashes. You can use methods including signMessage, signTypedData, and signTransaction.

1const signedTx = await signer.signTransaction(txRequest);

Then you can use a generic wallet client to send transactions! For example, if you are using viem, then you can use the toViemAccount method which will allow you to use the signer with a WalletClient.

1import { createWalletClient, http, custom, parseEther } from "viem";
2
3export const walletClient = createWalletClient({
4 transport: http("https://hyperliquid-mainnet.g.alchemy.com/v2/{API_KEY}"),
5 chain: Hype,
6 account: signer.toViemAccount(),
7});
8
9const txRequest = await walletClient.prepareTransactionRequest({
10 account: acct,
11 to: "0x0000000000000000000000000000000000000000",
12 value: parseEther("0.0001"),
13});
14
15// Sign transaction
16
17const txHash = await walletClient.sendRawTransaction({
18 serializedTransaction: signedTx,
19});

4. Sponsoring User Operations

Alchemy gas sponsorship lets your users send transactions on HyperEVM without holding the native token for gas. To start, make sure you have a gas policy ID configured in your app dashboard and pass it to your client initialization. Learn more in Sponsor Gas.

As an example, we will be sponsoring a HyperEVM user operation opening a limit order, using the HyperCore Writer contract. Start by creating some helpers to encode the call.

1// hyperliquidOrder.ts
2import {
3 encodeAbiParameters,
4 encodeFunctionData,
5 hexToBytes,
6 toHex,
7} from "viem";
8
9export const CORE_WRITER_ADDRESS =
10 "0x3333333333333333333333333333333333333333" as const;
11
12export const HYPERLIQUID_CALLDATA = (() => {
13 const asset = 0; // BTC
14 const isBuy = true;
15 const limitPx = 100000000000n; // $100,000
16 const sz = 100000n; // 0.001 BTC
17 const reduceOnly = false;
18 const encodedTif = 2; // GTC
19 const cloid = 0n;
20
21 const payloadHex = encodeAbiParameters(
22 [
23 { type: "uint32" },
24 { type: "bool" },
25 { type: "uint64" },
26 { type: "uint64" },
27 { type: "bool" },
28 { type: "uint8" },
29 { type: "uint128" },
30 ],
31 [asset, isBuy, limitPx, sz, reduceOnly, encodedTif, cloid],
32 );
33
34 // encoding version (1 byte) + action ID (3 bytes)
35 const prefix = new Uint8Array([0x01, 0x00, 0x00, 0x01]);
36
37 const payload = hexToBytes(payloadHex);
38 const actionBytes = new Uint8Array(prefix.length + payload.length);
39 actionBytes.set(prefix, 0);
40 actionBytes.set(payload, prefix.length);
41
42 const coreWriterAbi = [
43 {
44 type: "function",
45 name: "sendRawAction",
46 stateMutability: "nonpayable",
47 inputs: [{ name: "data", type: "bytes", internalType: "bytes" }],
48 outputs: [],
49 },
50 ] as const;
51
52 return encodeFunctionData({
53 abi: coreWriterAbi,
54 functionName: "sendRawAction",
55 args: [toHex(actionBytes)],
56 });
57})();

Using React

If you are working in React, you can use the useSendUserOperation hook. Make sure you have set up your config from step 1.

1import React from "react";
2import {
3 useSendUserOperation,
4 useSmartAccountClient,
5} from "@account-kit/react";
6import { CORE_WRITER_ADDRESS, HYPERLIQUID_CALLDATA } from "./hyperliquidOrder";
7
8function ComponentWithSendUserOperation() {
9 const { client } = useSmartAccountClient({});
10
11 const { sendUserOperation, isSendingUserOperation } = useSendUserOperation({
12 client,
13 waitForTxn: true,
14 onSuccess: ({ hash, request }) => {
15 // [optional] handle success
16 },
17 onError: (error) => {
18 // [optional] handle error
19 },
20 });
21
22 return (
23 <div>
24 <button
25 onClick={() =>
26 sendUserOperation({
27 uo: {
28 target: CORE_WRITER_ADDRESS,
29 data: HYPERLIQUID_CALLDATA,
30 value: 0n,
31 },
32 })
33 }
34 disabled={isSendingUserOperation}
35 >
36 {isSendingUserOperation ? "Sending..." : "Send UO"}
37 </button>
38 </div>
39 );
40}
41
42export default ComponentWithSendUserOperation;

Without React

If you are not using React, you can send the same user operation directly with the Modular Account V2 client in account-kit, passing in the chain config from step 1 and signer from step 2.

1import { createModularAccountV2Client } from "@account-kit/smart-contracts";
2import { CORE_WRITER_ADDRESS, HYPERLIQUID_CALLDATA } from "./hyperliquidOrder";
3
4const modularAccountV2Client = await createModularAccountV2Client({
5 chain, // chain config from step 1
6 signer, // signer from step 2
7 transport,
8 policyId: "policy_id",
9});
10
11await modularAccountV2Client.sendUserOperation({
12 uo: {
13 target: CORE_WRITER_ADDRESS,
14 data: HYPERLIQUID_CALLDATA,
15 value: 0n,
16 },
17});

Resources