@0xmonaco/core 1.1.7 → 1.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -90,6 +90,8 @@ export declare class FeesAPIImpl extends BaseAPI implements FeesAPI {
90
90
  * tradingPairId: "123e4567-e89b-12d3-a456-426614174000",
91
91
  * });
92
92
  * console.log(`Current tier: ${tier.currentTierLevel}`);
93
+ * console.log(`Rate source: ${tier.rateSource}`);
94
+ * console.log(`Maker ${tier.makerFeeBps} bps, taker ${tier.takerFeeBps} bps`);
93
95
  * console.log(`14d volume: ${tier.weightedVolume14d}`);
94
96
  * for (const row of tier.feeSchedule) {
95
97
  * console.log(`Tier ${row.tierLevel}: taker ${row.takerFeeBps} bps`);
@@ -108,6 +108,8 @@ export class FeesAPIImpl extends BaseAPI {
108
108
  * tradingPairId: "123e4567-e89b-12d3-a456-426614174000",
109
109
  * });
110
110
  * console.log(`Current tier: ${tier.currentTierLevel}`);
111
+ * console.log(`Rate source: ${tier.rateSource}`);
112
+ * console.log(`Maker ${tier.makerFeeBps} bps, taker ${tier.takerFeeBps} bps`);
111
113
  * console.log(`14d volume: ${tier.weightedVolume14d}`);
112
114
  * for (const row of tier.feeSchedule) {
113
115
  * console.log(`Tier ${row.tierLevel}: taker ${row.takerFeeBps} bps`);
@@ -22,9 +22,16 @@
22
22
  * const margin = await sdk.sweeper.register({ depositTarget: "margin" });
23
23
  * ```
24
24
  */
25
- import type { GetSweeperChainsResponse, GetSweeperDepositStatusResponse, ListSweeperDepositsParams, ListSweeperDepositsResponse, RegisterSweeperRequest, RegisterSweeperResponse, SweeperAPI } from "@0xmonaco/types";
25
+ import type { DepositFromHypercoreRequest, DepositFromHypercoreResponse, DepositFromHyperEvmRequest, DepositFromHyperEvmResponse, GetSweeperChainsResponse, GetSweeperDepositStatusResponse, HypercoreDepositInfo, HyperEvmDepositInfo, ListSweeperDepositsParams, ListSweeperDepositsResponse, RegisterSweeperRequest, RegisterSweeperResponse, SweeperAPI } from "@0xmonaco/types";
26
+ import { type WalletClient } from "viem";
26
27
  import { BaseAPI } from "../base";
27
28
  export declare class SweeperAPIImpl extends BaseAPI implements SweeperAPI {
29
+ private walletClient;
30
+ /**
31
+ * Set the wallet client that signs {@link depositFromHypercore}. Its
32
+ * account must be the one holding the Hyperliquid balance.
33
+ */
34
+ setWalletClient(walletClient: WalletClient): void;
28
35
  /**
29
36
  * Registers an (application, user, deposit target) deposit address for
30
37
  * monitoring. Requires authentication; the user is the session's own
@@ -90,4 +97,105 @@ export declare class SweeperAPIImpl extends BaseAPI implements SweeperAPI {
90
97
  * on the last page)
91
98
  */
92
99
  listDeposits(params?: ListSweeperDepositsParams): Promise<ListSweeperDepositsResponse>;
100
+ /**
101
+ * Deposits USDC the wallet holds on Hyperliquid (HyperCore). The wallet
102
+ * signs Circle's CCTP withdrawal (`sendToEvmWithData`) to the caller's
103
+ * deposit address on Sei, and the SDK posts it to Hyperliquid, which
104
+ * executes it — no gas and no HyperEVM transaction from the user. Circle
105
+ * forwards the mint to Sei, keeping up to `maxFee` of it, and the sweeper
106
+ * credits the rest like any deposit made straight to the address; it lists
107
+ * in {@link listDeposits} by its Sei mint.
108
+ *
109
+ * The Hyperliquid balance is chosen from the account itself (see
110
+ * {@link getHypercoreDepositInfo}): spot for a unified or portfolio-margin
111
+ * account, otherwise whichever of perp (first) or spot covers `amount`.
112
+ * Before registering or signing, this refuses an amount below the minimum
113
+ * — a credit under the hub's floor would sit on the address unswept, and a
114
+ * withdrawal that does not exceed Circle's fee reverts on HyperEVM — and an
115
+ * amount no single balance covers. It then registers the deposit address
116
+ * (so it is monitored) and signs.
117
+ *
118
+ * @param body - The raw USDC amount (6 decimals), and optionally the
119
+ * deposit target
120
+ * @returns Promise resolving once Hyperliquid accepts the withdrawal
121
+ * @throws {InvalidConfigError} On an invalid amount, a missing wallet, a
122
+ * wallet off Sei, or an amount below the minimum or above what one
123
+ * balance holds
124
+ * @throws {InvalidStateError} When the hub does not sweep USDC
125
+ * @throws {APIError} When a Hyperliquid read fails (nothing signed), or
126
+ * when Hyperliquid refuses the action (nothing moved). A lost or
127
+ * unreadable reply to the withdrawal itself is an unknown outcome and
128
+ * carries `retryable: false`: calling again signs a second withdrawal, so
129
+ * check {@link listDeposits} or the Hyperliquid balance first.
130
+ */
131
+ depositFromHypercore(body: DepositFromHypercoreRequest): Promise<DepositFromHypercoreResponse>;
132
+ /**
133
+ * What the wallet can deposit from Hyperliquid: its account mode and USDC
134
+ * balances, the most one deposit can draw, Circle's fee, and the smallest
135
+ * amount {@link depositFromHypercore} accepts. Reads only public data
136
+ * (Hyperliquid's info endpoint, Circle's CoreDepositWallet on HyperEVM and
137
+ * {@link getChains}), so it needs a wallet client on Sei but no session.
138
+ *
139
+ * @returns Promise resolving to the account's deposit info, amounts in raw
140
+ * USDC base units (6 decimals)
141
+ * @throws {InvalidConfigError} On a missing wallet or a wallet off Sei
142
+ * @throws {InvalidStateError} When the hub does not sweep USDC
143
+ * @throws {APIError} When a Hyperliquid read fails
144
+ */
145
+ getHypercoreDepositInfo(): Promise<HypercoreDepositInfo>;
146
+ /**
147
+ * Deposits Circle USDC the wallet holds on HyperEVM. The wallet approves
148
+ * (when its allowance is short) and burns it through Circle's
149
+ * TokenMessengerV2 on HyperEVM, naming the caller's Sei deposit address as
150
+ * the recipient and asking Circle to forward the mint. Circle mints on Sei,
151
+ * keeping up to `maxFee`, and the sweeper credits the rest like any deposit
152
+ * made straight to the address; it lists in {@link listDeposits} by its Sei
153
+ * mint. The wallet pays HYPE for gas; no Monaco contract is involved on
154
+ * HyperEVM.
155
+ *
156
+ * A browser or embedded wallet is switched to HyperEVM (added first when it
157
+ * does not know the chain) and back to Sei afterwards. Before registering
158
+ * or sending, this refuses an amount below the minimum or above the
159
+ * wallet's HyperEVM USDC, and a wallet with no HYPE.
160
+ *
161
+ * @param body - The raw USDC amount (6 decimals), and optionally the
162
+ * deposit target
163
+ * @returns Promise resolving once the burn lands on HyperEVM
164
+ * @throws {InvalidConfigError} On an invalid amount, a missing wallet, a
165
+ * wallet off Sei, or an amount below the minimum or above the balance
166
+ * @throws {InvalidStateError} When the hub does not sweep USDC or the
167
+ * wallet has no HYPE
168
+ * @throws {APIError} When Circle's fee quote fails (nothing sent), or when
169
+ * the burn was sent but its receipt cannot be read: an unknown outcome
170
+ * carrying `retryable: false` and the burn's hash, since calling again
171
+ * burns a second time
172
+ * @throws {ContractError} When the approval or the burn reverts (nothing
173
+ * burned)
174
+ */
175
+ depositFromHyperEvm(body: DepositFromHyperEvmRequest): Promise<DepositFromHyperEvmResponse>;
176
+ /**
177
+ * What the wallet can deposit from HyperEVM: its Circle USDC and HYPE
178
+ * there, Circle's forwarding fee, and the smallest amount
179
+ * {@link depositFromHyperEvm} accepts. Reads only public data (the
180
+ * HyperEVM RPC, Circle's fee API and {@link getChains}), so it needs a
181
+ * wallet client on Sei but no session.
182
+ *
183
+ * @returns Promise resolving to the wallet's HyperEVM deposit info
184
+ * @throws {InvalidConfigError} On a missing wallet or a wallet off Sei
185
+ * @throws {InvalidStateError} When the hub does not sweep USDC
186
+ * @throws {APIError} When Circle's fee quote fails
187
+ */
188
+ getHyperEvmDepositInfo(): Promise<HyperEvmDepositInfo>;
189
+ /** The reads both Hyperliquid (HyperCore) methods start from, made in parallel. */
190
+ private readHypercoreDepositInfo;
191
+ /** The reads both HyperEVM methods start from, made in parallel. */
192
+ private readHyperEvmDepositInfo;
193
+ /** The wallet client, which must be on Sei, and the Hyperliquid network that pairs with its chain. */
194
+ private walletOnSei;
195
+ /**
196
+ * The Sei hub's minimum for Circle's USDC, raw. By address, never by symbol:
197
+ * Sei also carries Noble's bridged USDC.n, and both routes mint Circle's
198
+ * native USDC.
199
+ */
200
+ private hubUsdcFloor;
93
201
  }
@@ -22,9 +22,13 @@
22
22
  * const margin = await sdk.sweeper.register({ depositTarget: "margin" });
23
23
  * ```
24
24
  */
25
- import { InvalidConfigError } from "../../errors";
25
+ import { formatUnits } from "viem";
26
+ import { sei, seiTestnet } from "viem/chains";
27
+ import { InvalidConfigError, InvalidStateError } from "../../errors";
26
28
  import { BaseAPI } from "../base";
27
29
  import { perpRoutes } from "../perp/routes";
30
+ import { availableToDraw, chooseSource, hypercoreNetworkFor, postToExchange, readHypercoreAccount, readMaxFee, signWithdrawalToSei, USDC_DECIMALS, } from "./hypercore";
31
+ import { burnToSei, quoteForwardFee, readHyperEvmAccount } from "./hyperevm";
28
32
  /**
29
33
  * Ergonomic target to its wire form.
30
34
  *
@@ -52,6 +56,14 @@ function toWireDepositTarget(target) {
52
56
  throw new InvalidConfigError(`Invalid deposit target: ${JSON.stringify(target)}. Expected "spot" or "margin".`);
53
57
  }
54
58
  export class SweeperAPIImpl extends BaseAPI {
59
+ walletClient;
60
+ /**
61
+ * Set the wallet client that signs {@link depositFromHypercore}. Its
62
+ * account must be the one holding the Hyperliquid balance.
63
+ */
64
+ setWalletClient(walletClient) {
65
+ this.walletClient = walletClient;
66
+ }
55
67
  /**
56
68
  * Registers an (application, user, deposit target) deposit address for
57
69
  * monitoring. Requires authentication; the user is the session's own
@@ -143,6 +155,218 @@ export class SweeperAPIImpl extends BaseAPI {
143
155
  method: "GET",
144
156
  });
145
157
  }
158
+ /**
159
+ * Deposits USDC the wallet holds on Hyperliquid (HyperCore). The wallet
160
+ * signs Circle's CCTP withdrawal (`sendToEvmWithData`) to the caller's
161
+ * deposit address on Sei, and the SDK posts it to Hyperliquid, which
162
+ * executes it — no gas and no HyperEVM transaction from the user. Circle
163
+ * forwards the mint to Sei, keeping up to `maxFee` of it, and the sweeper
164
+ * credits the rest like any deposit made straight to the address; it lists
165
+ * in {@link listDeposits} by its Sei mint.
166
+ *
167
+ * The Hyperliquid balance is chosen from the account itself (see
168
+ * {@link getHypercoreDepositInfo}): spot for a unified or portfolio-margin
169
+ * account, otherwise whichever of perp (first) or spot covers `amount`.
170
+ * Before registering or signing, this refuses an amount below the minimum
171
+ * — a credit under the hub's floor would sit on the address unswept, and a
172
+ * withdrawal that does not exceed Circle's fee reverts on HyperEVM — and an
173
+ * amount no single balance covers. It then registers the deposit address
174
+ * (so it is monitored) and signs.
175
+ *
176
+ * @param body - The raw USDC amount (6 decimals), and optionally the
177
+ * deposit target
178
+ * @returns Promise resolving once Hyperliquid accepts the withdrawal
179
+ * @throws {InvalidConfigError} On an invalid amount, a missing wallet, a
180
+ * wallet off Sei, or an amount below the minimum or above what one
181
+ * balance holds
182
+ * @throws {InvalidStateError} When the hub does not sweep USDC
183
+ * @throws {APIError} When a Hyperliquid read fails (nothing signed), or
184
+ * when Hyperliquid refuses the action (nothing moved). A lost or
185
+ * unreadable reply to the withdrawal itself is an unknown outcome and
186
+ * carries `retryable: false`: calling again signs a second withdrawal, so
187
+ * check {@link listDeposits} or the Hyperliquid balance first.
188
+ */
189
+ async depositFromHypercore(body) {
190
+ const { amount, depositTarget } = body;
191
+ if (typeof amount !== "bigint" || amount <= 0n) {
192
+ throw new InvalidConfigError(`Invalid amount: ${String(amount)}. Expected a positive bigint of raw USDC base units (6 decimals).`, "amount", amount);
193
+ }
194
+ const { walletClient, network, account, info } = await this.readHypercoreDepositInfo("depositFromHypercore");
195
+ if (amount < info.minimumAmount) {
196
+ throw new InvalidConfigError(`Deposit at least ${formatUnits(info.minimumAmount, USDC_DECIMALS)} USDC from Hyperliquid: Circle keeps up to ${formatUnits(info.maxFee, USDC_DECIMALS)} USDC to forward it to Sei, and the hub only sweeps a credit above its minimum.`, "amount", amount);
197
+ }
198
+ const source = chooseSource(account, amount);
199
+ if (!source) {
200
+ throw new InvalidConfigError(`Hyperliquid holds too little USDC: one deposit can draw at most ${formatUnits(info.available, USDC_DECIMALS)} USDC (perp ${formatUnits(info.perpBalance, USDC_DECIMALS)}, spot ${formatUnits(info.spotBalance, USDC_DECIMALS)}).`, "amount", amount);
201
+ }
202
+ const { sweeperAddress } = await this.register(depositTarget === undefined ? {} : { depositTarget });
203
+ const nonce = Date.now();
204
+ const withdrawal = await signWithdrawalToSei({
205
+ walletClient,
206
+ network,
207
+ amount: formatUnits(amount, USDC_DECIMALS),
208
+ sourceDex: source === "perp" ? "" : "spot",
209
+ recipient: sweeperAddress,
210
+ nonce,
211
+ });
212
+ await postToExchange(network, withdrawal);
213
+ return { sweeperAddress, amount, source, maxFee: info.maxFee, minimumCredit: amount - info.maxFee, nonce };
214
+ }
215
+ /**
216
+ * What the wallet can deposit from Hyperliquid: its account mode and USDC
217
+ * balances, the most one deposit can draw, Circle's fee, and the smallest
218
+ * amount {@link depositFromHypercore} accepts. Reads only public data
219
+ * (Hyperliquid's info endpoint, Circle's CoreDepositWallet on HyperEVM and
220
+ * {@link getChains}), so it needs a wallet client on Sei but no session.
221
+ *
222
+ * @returns Promise resolving to the account's deposit info, amounts in raw
223
+ * USDC base units (6 decimals)
224
+ * @throws {InvalidConfigError} On a missing wallet or a wallet off Sei
225
+ * @throws {InvalidStateError} When the hub does not sweep USDC
226
+ * @throws {APIError} When a Hyperliquid read fails
227
+ */
228
+ async getHypercoreDepositInfo() {
229
+ return (await this.readHypercoreDepositInfo("getHypercoreDepositInfo")).info;
230
+ }
231
+ /**
232
+ * Deposits Circle USDC the wallet holds on HyperEVM. The wallet approves
233
+ * (when its allowance is short) and burns it through Circle's
234
+ * TokenMessengerV2 on HyperEVM, naming the caller's Sei deposit address as
235
+ * the recipient and asking Circle to forward the mint. Circle mints on Sei,
236
+ * keeping up to `maxFee`, and the sweeper credits the rest like any deposit
237
+ * made straight to the address; it lists in {@link listDeposits} by its Sei
238
+ * mint. The wallet pays HYPE for gas; no Monaco contract is involved on
239
+ * HyperEVM.
240
+ *
241
+ * A browser or embedded wallet is switched to HyperEVM (added first when it
242
+ * does not know the chain) and back to Sei afterwards. Before registering
243
+ * or sending, this refuses an amount below the minimum or above the
244
+ * wallet's HyperEVM USDC, and a wallet with no HYPE.
245
+ *
246
+ * @param body - The raw USDC amount (6 decimals), and optionally the
247
+ * deposit target
248
+ * @returns Promise resolving once the burn lands on HyperEVM
249
+ * @throws {InvalidConfigError} On an invalid amount, a missing wallet, a
250
+ * wallet off Sei, or an amount below the minimum or above the balance
251
+ * @throws {InvalidStateError} When the hub does not sweep USDC or the
252
+ * wallet has no HYPE
253
+ * @throws {APIError} When Circle's fee quote fails (nothing sent), or when
254
+ * the burn was sent but its receipt cannot be read: an unknown outcome
255
+ * carrying `retryable: false` and the burn's hash, since calling again
256
+ * burns a second time
257
+ * @throws {ContractError} When the approval or the burn reverts (nothing
258
+ * burned)
259
+ */
260
+ async depositFromHyperEvm(body) {
261
+ const { amount, depositTarget } = body;
262
+ if (typeof amount !== "bigint" || amount <= 0n) {
263
+ throw new InvalidConfigError(`Invalid amount: ${String(amount)}. Expected a positive bigint of raw USDC base units (6 decimals).`, "amount", amount);
264
+ }
265
+ const { walletClient, network, account, info } = await this.readHyperEvmDepositInfo("depositFromHyperEvm");
266
+ if (amount < info.minimumAmount) {
267
+ throw new InvalidConfigError(`Deposit at least ${formatUnits(info.minimumAmount, USDC_DECIMALS)} USDC from HyperEVM: Circle keeps up to ${formatUnits(info.maxFee, USDC_DECIMALS)} USDC to forward it to Sei, and the hub only sweeps a credit above its minimum.`, "amount", amount);
268
+ }
269
+ if (amount > info.usdcBalance) {
270
+ throw new InvalidConfigError(`The wallet holds ${formatUnits(info.usdcBalance, USDC_DECIMALS)} USDC on HyperEVM.`, "amount", amount);
271
+ }
272
+ if (info.gasBalance === 0n) {
273
+ throw new InvalidStateError("The wallet holds no HYPE on HyperEVM to pay gas for the approval and burn.");
274
+ }
275
+ const { sweeperAddress } = await this.register(depositTarget === undefined ? {} : { depositTarget });
276
+ const { approveTxHash, burnTxHash } = await burnToSei({
277
+ walletClient,
278
+ network,
279
+ amount,
280
+ recipient: sweeperAddress,
281
+ maxFee: info.maxFee,
282
+ allowance: account.allowance,
283
+ });
284
+ return {
285
+ sweeperAddress,
286
+ amount,
287
+ maxFee: info.maxFee,
288
+ minimumCredit: amount - info.maxFee,
289
+ ...(approveTxHash === undefined ? {} : { approveTxHash }),
290
+ burnTxHash,
291
+ };
292
+ }
293
+ /**
294
+ * What the wallet can deposit from HyperEVM: its Circle USDC and HYPE
295
+ * there, Circle's forwarding fee, and the smallest amount
296
+ * {@link depositFromHyperEvm} accepts. Reads only public data (the
297
+ * HyperEVM RPC, Circle's fee API and {@link getChains}), so it needs a
298
+ * wallet client on Sei but no session.
299
+ *
300
+ * @returns Promise resolving to the wallet's HyperEVM deposit info
301
+ * @throws {InvalidConfigError} On a missing wallet or a wallet off Sei
302
+ * @throws {InvalidStateError} When the hub does not sweep USDC
303
+ * @throws {APIError} When Circle's fee quote fails
304
+ */
305
+ async getHyperEvmDepositInfo() {
306
+ return (await this.readHyperEvmDepositInfo("getHyperEvmDepositInfo")).info;
307
+ }
308
+ /** The reads both Hyperliquid (HyperCore) methods start from, made in parallel. */
309
+ async readHypercoreDepositInfo(caller) {
310
+ const { walletClient, network, address } = this.walletOnSei(caller);
311
+ const [floor, maxFee, account] = await Promise.all([this.hubUsdcFloor(network), readMaxFee(network), readHypercoreAccount(network, address)]);
312
+ const info = {
313
+ account: address,
314
+ accountMode: account.mode,
315
+ perpBalance: account.perp,
316
+ spotBalance: account.spot,
317
+ available: availableToDraw(account),
318
+ maxFee,
319
+ minimumAmount: minimumAmount(maxFee, floor),
320
+ };
321
+ return { walletClient, network, account, info };
322
+ }
323
+ /** The reads both HyperEVM methods start from, made in parallel. */
324
+ async readHyperEvmDepositInfo(caller) {
325
+ const { walletClient, network, address } = this.walletOnSei(caller);
326
+ const [floor, maxFee, account] = await Promise.all([this.hubUsdcFloor(network), quoteForwardFee(network), readHyperEvmAccount(network, address)]);
327
+ const info = {
328
+ account: address,
329
+ usdcBalance: account.usdc,
330
+ gasBalance: account.gas,
331
+ maxFee,
332
+ minimumAmount: minimumAmount(maxFee, floor),
333
+ };
334
+ return { walletClient, network, account, info };
335
+ }
336
+ /** The wallet client, which must be on Sei, and the Hyperliquid network that pairs with its chain. */
337
+ walletOnSei(caller) {
338
+ const walletClient = this.walletClient;
339
+ if (!walletClient?.account) {
340
+ throw new InvalidConfigError("Wallet client not set. Connect the wallet that holds the USDC first.", "walletClient");
341
+ }
342
+ const network = hypercoreNetworkFor(walletClient.chain?.id);
343
+ if (!network) {
344
+ throw new InvalidConfigError(`${caller} needs the wallet on Sei (chain ${sei.id} or ${seiTestnet.id}); got chain ${walletClient.chain?.id}.`, "walletClient");
345
+ }
346
+ return { walletClient, network, address: walletClient.account.address };
347
+ }
348
+ /**
349
+ * The Sei hub's minimum for Circle's USDC, raw. By address, never by symbol:
350
+ * Sei also carries Noble's bridged USDC.n, and both routes mint Circle's
351
+ * native USDC.
352
+ */
353
+ async hubUsdcFloor(network) {
354
+ const { chains } = await this.getChains();
355
+ const seiUsdc = network.seiUsdc.toLowerCase();
356
+ const usdc = chains.find((chain) => chain.hub)?.assets.find((asset) => asset.tokenAddress.toLowerCase() === seiUsdc);
357
+ if (!usdc) {
358
+ throw new InvalidStateError("The Sei hub does not sweep USDC, so a deposit from Hyperliquid would not be credited.");
359
+ }
360
+ return BigInt(usdc.minSweepRaw);
361
+ }
362
+ }
363
+ /**
364
+ * The smallest amount a forwarded deposit accepts: Circle's fee plus the
365
+ * hub's floor. Circle reverts a transfer that does not exceed its fee, and the
366
+ * hub leaves a credit below its floor on the address until a later deposit.
367
+ */
368
+ function minimumAmount(maxFee, floor) {
369
+ return maxFee + (floor > 0n ? floor : 1n);
146
370
  }
147
371
  /** The deposit listing's page size bounds, matching the endpoint's schema. */
148
372
  const MAX_DEPOSIT_PAGE_SIZE = 100;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * USDC withdrawals from Hyperliquid (HyperCore) to a Sei deposit address.
3
+ *
4
+ * Circle's CCTP integration on HyperCore withdraws USDC to any CCTP domain
5
+ * through one user-signed HyperCore action, `sendToEvmWithData`
6
+ * (developers.circle.com/cctp/howtos/withdraw-usdc-from-hypercore-to-evm).
7
+ * Hyperliquid executes the signed action itself, so the user pays no gas: its
8
+ * system call into Circle's CoreDepositWallet on HyperEVM burns the USDC with
9
+ * Sei as the destination domain and the deposit address as the mint
10
+ * recipient. Empty `data` selects Circle's default forwarding hook, so Circle
11
+ * mints on Sei and keeps its fee out of the mint. The hub then sweeps the
12
+ * deposit address like any deposit made straight to it.
13
+ */
14
+ import type { HypercoreAccountMode, HypercoreBalanceSource } from "@0xmonaco/types";
15
+ import { type Address, type Chain, type Hex, type WalletClient } from "viem";
16
+ /** Sei's CCTP domain: where the withdrawal mints. Not its EVM chain id. */
17
+ export declare const SEI_CCTP_DOMAIN = 16;
18
+ /** USDC's decimals on HyperEVM and Sei. */
19
+ export declare const USDC_DECIMALS = 6;
20
+ /** Where one Hyperliquid network's withdrawals are signed for and sent. */
21
+ export interface HypercoreNetwork {
22
+ hyperliquidChain: "Mainnet" | "Testnet";
23
+ exchangeUrl: string;
24
+ infoUrl: string;
25
+ hyperEvm: Chain;
26
+ /** Circle's CoreDepositWallet (developers.circle.com/cctp/references/hypercore-contract-addresses) */
27
+ coreDepositWallet: Address;
28
+ /** Circle's native USDC on the paired Sei chain: what the withdrawal mints */
29
+ seiUsdc: Address;
30
+ /** Circle's native USDC on HyperEVM */
31
+ hyperEvmUsdc: Address;
32
+ /** Circle's TokenMessengerV2 on HyperEVM */
33
+ tokenMessenger: Address;
34
+ /** Circle's attestation API (Iris), which quotes CCTP fees */
35
+ irisUrl: string;
36
+ }
37
+ /**
38
+ * The Hyperliquid network that pairs with the SDK's Sei chain: mainnet with
39
+ * Sei mainnet, testnet with Sei testnet (where every non-mainnet Monaco
40
+ * network settles). `undefined` for any other chain.
41
+ */
42
+ export declare function hypercoreNetworkFor(chainId: number | undefined): HypercoreNetwork | undefined;
43
+ /**
44
+ * The most Circle deducts from a forwarded withdrawal to Sei: the CCTP
45
+ * `maxFee` its CoreDepositWallet burns with (its forwarding fee for the
46
+ * domain plus CCTP's own cap). Owner-configurable per domain, so it is read
47
+ * rather than assumed. The wallet also reverts a withdrawal that does not
48
+ * exceed it.
49
+ */
50
+ export declare function readMaxFee(network: HypercoreNetwork): Promise<bigint>;
51
+ /** A Hyperliquid account's USDC, in raw 6-decimal base units. */
52
+ export interface HypercoreAccount {
53
+ mode: HypercoreAccountMode;
54
+ perp: bigint;
55
+ spot: bigint;
56
+ }
57
+ /** Read the account's mode and USDC balances from Hyperliquid's public info endpoint. */
58
+ export declare function readHypercoreAccount(network: HypercoreNetwork, user: Address): Promise<HypercoreAccount>;
59
+ /**
60
+ * The balance one withdrawal of `amount` draws on, or `undefined` when none
61
+ * covers it with room for Hyperliquid's fee. A unified or portfolio-margin
62
+ * account draws on spot: Hyperliquid refuses any other source for it
63
+ * ("Unified account only supports sending assets through spot"). Otherwise
64
+ * perp comes first, where a deposit to Hyperliquid lands by default.
65
+ */
66
+ export declare function chooseSource(account: HypercoreAccount, amount: bigint): HypercoreBalanceSource | undefined;
67
+ /** The most one withdrawal can draw: it draws on a single balance and leaves room for Hyperliquid's fee. */
68
+ export declare function availableToDraw(account: HypercoreAccount): bigint;
69
+ /** One signed `sendToEvmWithData` action, as Hyperliquid's exchange endpoint takes it. */
70
+ export interface SignedWithdrawal {
71
+ action: Record<string, unknown>;
72
+ nonce: number;
73
+ signature: {
74
+ r: Hex;
75
+ s: Hex;
76
+ v: number;
77
+ };
78
+ }
79
+ /**
80
+ * Sign a USDC withdrawal from Hyperliquid to `recipient` on Sei.
81
+ *
82
+ * The signature's domain uses the wallet's own chain (Sei), which Hyperliquid
83
+ * accepts from any chain and wallets that hold the domain to the active
84
+ * network sign without switching.
85
+ */
86
+ export declare function signWithdrawalToSei(params: {
87
+ walletClient: WalletClient;
88
+ network: HypercoreNetwork;
89
+ amount: string;
90
+ sourceDex: "" | "spot";
91
+ recipient: string;
92
+ nonce: number;
93
+ }): Promise<SignedWithdrawal>;
94
+ /**
95
+ * Post a signed withdrawal to Hyperliquid's exchange endpoint.
96
+ *
97
+ * Hyperliquid answers a decided rejection with HTTP 200 and
98
+ * `{"status":"err","response":"<reason>"}`, so the status alone does not tell
99
+ * success from refusal. A refusal is a decided outcome (nothing moved); a
100
+ * dropped connection, a 5xx, or a reply that is neither `ok` nor `err` leaves
101
+ * the outcome unknown. The signed body is never attached to an error.
102
+ */
103
+ export declare function postToExchange(network: HypercoreNetwork, withdrawal: SignedWithdrawal): Promise<void>;