@recoilpay/intent-core 0.1.0

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.
@@ -0,0 +1,623 @@
1
+ import { PublicClient, Hex, Address, Abi, WalletClient } from 'viem';
2
+
3
+ /**
4
+ * Types mirroring the OIF aggregator HTTP API (camelCase JSON).
5
+ * Source of truth: oif-aggregator crates/types. Only the surface Phase 0 needs is
6
+ * modelled here; quote/order types arrive in Phase 2.
7
+ */
8
+ /** A token a solver can source/deliver, from GET /api/v1/solvers. */
9
+ interface SolverAsset {
10
+ address: string;
11
+ chainId: number;
12
+ symbol: string;
13
+ name: string;
14
+ decimals: number;
15
+ }
16
+ interface SolverSupportedAssets {
17
+ type: 'assets' | 'routes';
18
+ assets?: SolverAsset[];
19
+ source?: 'autoDiscovered' | 'config';
20
+ }
21
+ interface Solver {
22
+ solverId: string;
23
+ adapterId: string;
24
+ name: string;
25
+ description?: string;
26
+ endpoint: string;
27
+ status: string;
28
+ supportedAssets: SolverSupportedAssets;
29
+ createdAt?: string;
30
+ lastSeen?: string;
31
+ }
32
+ interface SolversResponse {
33
+ solvers: Solver[];
34
+ totalSolvers: number;
35
+ }
36
+ /** An ERC-7930 interop-encoded address. */
37
+ type InteropAddress$1 = `0x${string}`;
38
+ interface Input {
39
+ user: InteropAddress$1;
40
+ asset: InteropAddress$1;
41
+ amount?: string;
42
+ lock?: AssetLockReference;
43
+ }
44
+ interface Output {
45
+ receiver: InteropAddress$1;
46
+ asset: InteropAddress$1;
47
+ amount?: string;
48
+ calldata?: string;
49
+ }
50
+ interface AssetLockReference {
51
+ kind: 'the-compact' | 'rhinestone';
52
+ params?: unknown;
53
+ }
54
+ interface OriginSubmission {
55
+ mode: 'user' | 'protocol';
56
+ schemes?: ('erc4337' | 'permit2' | 'erc20-permit' | 'eip3009')[];
57
+ }
58
+ interface SolverOptions {
59
+ timeout?: number;
60
+ solverTimeout?: number;
61
+ minQuotes?: number;
62
+ solverSelection?: 'all' | 'sampled' | 'priority';
63
+ includeSolvers?: string[];
64
+ excludeSolvers?: string[];
65
+ sampleSize?: number;
66
+ priorityThreshold?: number;
67
+ }
68
+ interface IntentRequest {
69
+ intentType: 'oif-swap';
70
+ inputs: Input[];
71
+ outputs: Output[];
72
+ swapType?: 'exact-input' | 'exact-output';
73
+ preference?: 'price' | 'speed' | 'inputPriority' | 'trustMinimization';
74
+ partialFill?: boolean;
75
+ minValidUntil?: number;
76
+ failureHandling?: ('refund-automatic' | 'refund-claim' | 'needs-new-signature')[];
77
+ originSubmission?: OriginSubmission;
78
+ metadata?: unknown;
79
+ }
80
+ interface QuoteRequest {
81
+ user: InteropAddress$1;
82
+ intent: IntentRequest;
83
+ supportedTypes: string[];
84
+ metadata?: unknown;
85
+ solverOptions?: SolverOptions;
86
+ }
87
+ /** EIP-712 typed data carried by an order, ready to feed to a wallet signer. */
88
+ interface OrderPayload {
89
+ signatureType: 'eip712';
90
+ domain: {
91
+ name: string;
92
+ version?: string;
93
+ chainId: string | number;
94
+ verifyingContract: string;
95
+ };
96
+ primaryType: string;
97
+ message: Record<string, unknown>;
98
+ types: Record<string, Array<{
99
+ name: string;
100
+ type: string;
101
+ }>>;
102
+ }
103
+ interface Order {
104
+ type: string;
105
+ payload: OrderPayload;
106
+ metadata?: Record<string, unknown>;
107
+ }
108
+ interface QuotePreview {
109
+ inputs: Input[];
110
+ outputs: Output[];
111
+ }
112
+ /** A single quote from POST /api/v1/quotes. */
113
+ interface Quote {
114
+ quoteId: string;
115
+ solverId: string;
116
+ order: Order;
117
+ partialFill: boolean;
118
+ preview: QuotePreview;
119
+ integrityChecksum: string;
120
+ provider?: string;
121
+ eta?: number;
122
+ failureHandling?: 'refund-automatic' | 'refund-claim' | 'needs-new-signature';
123
+ validUntil?: number;
124
+ metadata?: unknown;
125
+ }
126
+ interface AggregationMetadata {
127
+ totalDurationMs: number;
128
+ solverTimeoutMs: number;
129
+ globalTimeoutMs: number;
130
+ earlyTermination: boolean;
131
+ totalSolversAvailable: number;
132
+ solversQueried: number;
133
+ solversRespondedSuccess: number;
134
+ solversRespondedError: number;
135
+ solversTimedOut: number;
136
+ minQuotesRequired: number;
137
+ solverSelectionMode: string;
138
+ }
139
+ interface QuotesResponse {
140
+ quotes: Quote[];
141
+ totalQuotes: number;
142
+ metadata?: AggregationMetadata;
143
+ }
144
+ interface OrderRequest {
145
+ /** The full Quote object returned by /quotes. */
146
+ quoteResponse: Quote;
147
+ /** Hex signature produced by ./sign (scheme-prefixed). */
148
+ signature: string;
149
+ originSubmission?: OriginSubmission;
150
+ metadata?: unknown;
151
+ }
152
+ /**
153
+ * Order lifecycle: created → pending → executing → executed → settled →
154
+ * settling → finalized. Terminal: finalized | failed | refunded.
155
+ */
156
+ type OrderStatus = 'created' | 'pending' | 'executing' | 'executed' | 'settled' | 'settling' | 'finalized' | 'refunded' | {
157
+ failed: [string, string];
158
+ };
159
+ interface AssetAmount {
160
+ asset: InteropAddress$1;
161
+ amount?: string;
162
+ }
163
+ interface Settlement {
164
+ type: 'escrow' | 'resourceLock';
165
+ data: unknown;
166
+ }
167
+ interface OrderResponse {
168
+ orderId: string;
169
+ status: OrderStatus;
170
+ createdAt: string;
171
+ updatedAt: string;
172
+ inputAmounts: AssetAmount[];
173
+ outputAmounts: AssetAmount[];
174
+ orderType: string;
175
+ settlement: Settlement;
176
+ quoteId?: string;
177
+ fillTransaction?: unknown;
178
+ }
179
+
180
+ /** Production aggregator. Override with `apiUrl` for staging or a local instance. */
181
+ declare const DEFAULT_API_URL = "https://api.recoilpay.com";
182
+ interface ApiOptions {
183
+ /** Aggregator base URL, without the `/api/v1` suffix. Defaults to production. */
184
+ apiUrl?: string;
185
+ /** Custom fetch (tests, server runtimes, proxies). Defaults to the global fetch. */
186
+ fetch?: typeof fetch;
187
+ }
188
+ /** One chain from GET /api/v1/chains. Keys are snake_case on this endpoint. */
189
+ interface ChainInfo {
190
+ chain_id: number;
191
+ name: string;
192
+ rpc_url: string;
193
+ input_settler: string;
194
+ output_settler: string;
195
+ oracle: string;
196
+ permit2: string;
197
+ tokens: {
198
+ symbol: string;
199
+ address: string;
200
+ decimals: number;
201
+ }[];
202
+ }
203
+ declare class ApiError extends Error {
204
+ readonly status: number;
205
+ readonly code: string;
206
+ constructor(status: number, code: string, message: string);
207
+ }
208
+ type ApiClient = ReturnType<typeof createApiClient>;
209
+ /** Thin typed client for the RecoilPay aggregator's public API. */
210
+ declare function createApiClient(options?: ApiOptions): {
211
+ /** The fetch this client uses (shared with the session's parser). */
212
+ fetch: typeof fetch;
213
+ /** Supported chains with RPC endpoints and settlement contracts. */
214
+ getChains(): Promise<ChainInfo[]>;
215
+ getSolvers: () => Promise<SolversResponse>;
216
+ /**
217
+ * Every asset an active solver can fill, de-duplicated — the set that
218
+ * decides whether an intent is executable right now.
219
+ */
220
+ getSupportedAssets(): Promise<SolverAsset[]>;
221
+ getQuotes: (req: QuoteRequest) => Promise<QuotesResponse>;
222
+ submitOrder: (req: OrderRequest) => Promise<OrderResponse>;
223
+ getOrder: (id: string) => Promise<OrderResponse>;
224
+ };
225
+
226
+ /** Intent parser types. See docs/intent-parser-design.md. */
227
+ type IntentAction = 'swap' | 'send';
228
+ type AmountKind = 'token' | 'usd';
229
+ /** Output of the grammar parser, before resolution. Raw alias strings as typed. */
230
+ interface RawIntent {
231
+ action: IntentAction;
232
+ amount: string | null;
233
+ amountKind: AmountKind;
234
+ tokenIn: string | null;
235
+ chainIn: string | null;
236
+ tokenOut: string | null;
237
+ chainOut: string | null;
238
+ recipient: string | null;
239
+ }
240
+ type ParseResult = {
241
+ ok: true;
242
+ intents: RawIntent[];
243
+ } | {
244
+ ok: false;
245
+ offTemplate: true;
246
+ message: string;
247
+ };
248
+ type IssueField = 'amount' | 'tokenIn' | 'chainIn' | 'tokenOut' | 'chainOut' | 'recipient';
249
+ type IssueKind = 'missing' | 'unknown' | 'unsupported';
250
+ interface ValidationIssue {
251
+ field: IssueField;
252
+ kind: IssueKind;
253
+ message: string;
254
+ }
255
+ /** Fully resolved intent ready for the OIF order builder. */
256
+ interface ResolvedIntent {
257
+ action: IntentAction;
258
+ user: string;
259
+ srcChainId: number;
260
+ dstChainId: number;
261
+ inputToken: string;
262
+ inputDecimals: number;
263
+ inputAmount: bigint;
264
+ outputToken: string;
265
+ recipient: string;
266
+ }
267
+
268
+ declare const TEMPLATE_HINT = "Try: swap <amount> <token> on <chain> for <token> on <chain> [to <address>] (e.g. swap 10 USDC on Base for ETH on Arbitrum) OR send <amount> <token> on <chain> to <address>";
269
+ /** Hugging Face's OpenAI-compatible router: the endpoint @huggingface/inference's chatCompletion uses. */
270
+ declare const HF_ROUTER_URL = "https://router.huggingface.co/v1/chat/completions";
271
+ declare const DEFAULT_INTENT_MODEL = "Qwen/Qwen2.5-Coder-32B-Instruct";
272
+ interface ParserOptions {
273
+ /**
274
+ * Your Hugging Face access token. Parsing runs in the browser, so this
275
+ * token is visible to anyone who loads your page: use a dedicated,
276
+ * inference-only token you can rotate. Without one, parsing is off.
277
+ */
278
+ hfAccessToken?: string | null;
279
+ /** Model on the Hugging Face router. */
280
+ model?: string;
281
+ fetch?: typeof fetch;
282
+ }
283
+ /**
284
+ * Pulls intents out of a model reply. Models wrap JSON in code fences or add
285
+ * a sentence around it, so this takes a fenced block, or else the span from
286
+ * the first `{`/`[` to the last `}`/`]`.
287
+ */
288
+ declare function extractIntents(content: string): ParseResult;
289
+ /**
290
+ * Plain English → intents, by asking a model on Hugging Face from the
291
+ * browser. Never throws: every failure is `{ ok: false, message }` with text
292
+ * that can be shown to the user as-is.
293
+ */
294
+ declare function parseIntent(text: string, options?: ParserOptions): Promise<ParseResult>;
295
+
296
+ declare const SOLANA_CHAIN_IDS: ReadonlySet<number>;
297
+ declare function chainName(chainId: number, info?: ChainInfo): string;
298
+ declare function explorerUrl(chainId: number): string | undefined;
299
+ /**
300
+ * The read-only chain access the flow needs: Permit2 allowance checks and
301
+ * waiting for the approval transaction. Anything viem-shaped fits.
302
+ */
303
+ type ChainReader = Pick<PublicClient, 'readContract' | 'waitForTransactionReceipt'>;
304
+ /** Readers built from the aggregator's own RPC endpoints, cached per chain. */
305
+ declare function rpcReaders(chains: () => Promise<ChainInfo[]>): (chainId: number) => Promise<ChainReader | null>;
306
+
307
+ /**
308
+ * Quote signing for the OIF escrow (Permit2) and EIP-3009 order types.
309
+ *
310
+ * Adapted from the aggregator demo's `quoteSigner.ts`, but signs with the
311
+ * CONNECTED WALLET (wagmi/viem `signTypedDataAsync`) instead of a raw private
312
+ * key. The quote response already carries the full EIP-712 typed data in
313
+ * `quote.order.payload` (domain / types / message / primaryType) — verified
314
+ * against the live aggregator for both order types — so we feed that straight
315
+ * to the wallet and apply the demo's per-scheme post-processing:
316
+ * - `oif-escrow-v0` (Permit2 `PermitBatchWitnessTransferFrom`): prefix `0x00`.
317
+ * - `oif-3009-v0` (EIP-3009 `ReceiveWithAuthorization` / `TransferWithAuthorization`):
318
+ * prefix `0x01`, and for multi-input orders wrap the signature(s) in an ABI
319
+ * `bytes[]` array.
320
+ *
321
+ * `oif-resource-lock-v0` (TheCompact) and `oif-generic-v0` are not implemented
322
+ * and throw clearly.
323
+ */
324
+
325
+ /**
326
+ * Wallet typed-data signer. Pass wagmi's `signTypedDataAsync` (from
327
+ * `useSignTypedData`) or viem wallet client's `signTypedData`.
328
+ */
329
+ type TypedDataSigner = (args: {
330
+ domain: Record<string, unknown>;
331
+ types: Record<string, ReadonlyArray<{
332
+ name: string;
333
+ type: string;
334
+ }>>;
335
+ primaryType: string;
336
+ message: Record<string, unknown>;
337
+ }) => Promise<Hex>;
338
+ /**
339
+ * Sign a quote with the connected wallet.
340
+ *
341
+ * @param quote - the full Quote object returned by /quotes
342
+ * @param signTypedDataAsync - wagmi/viem typed-data signer (uses connected wallet)
343
+ * @returns the scheme-prefixed signature to put in the order's `signature` field
344
+ */
345
+ declare function signQuote(quote: Quote, signTypedDataAsync: TypedDataSigner): Promise<Hex>;
346
+
347
+ /**
348
+ * Everything the intent flow needs from a wallet. Deliberately small so any
349
+ * stack can supply it: wagmi, a viem WalletClient (see `viemWallet`), an
350
+ * embedded wallet SDK, or a test double.
351
+ */
352
+ interface IntentWallet {
353
+ /** The connected EVM account. */
354
+ address: Address;
355
+ /** Solana account, when the host also has one connected (Solana-destination swaps). */
356
+ solanaAddress?: string | null;
357
+ getChainId(): Promise<number>;
358
+ switchChain(chainId: number): Promise<void>;
359
+ /** EIP-712 signing. `types` never includes `EIP712Domain`. */
360
+ signTypedData: TypedDataSigner;
361
+ writeContract(req: {
362
+ chainId: number;
363
+ address: Address;
364
+ abi: Abi;
365
+ functionName: string;
366
+ args: readonly unknown[];
367
+ }): Promise<Hex>;
368
+ sendTransaction(req: {
369
+ chainId: number;
370
+ to: Address;
371
+ value: bigint;
372
+ }): Promise<Hex>;
373
+ }
374
+ /** Adapt a viem `WalletClient` (with an account attached) to `IntentWallet`. */
375
+ declare function viemWallet(client: WalletClient): IntentWallet;
376
+ /** viem rejects an `EIP712Domain` entry inside `types`; wallets add it themselves. */
377
+ declare function stripDomainType(types: Record<string, ReadonlyArray<{
378
+ name: string;
379
+ type: string;
380
+ }>>): Record<string, ReadonlyArray<{
381
+ name: string;
382
+ type: string;
383
+ }>>;
384
+
385
+ /**
386
+ * Where the flow is.
387
+ *
388
+ * idle nothing submitted yet
389
+ * parsing turning the text into intents
390
+ * offTemplate couldn't parse; show `hint`
391
+ * invalid parsed, but `issues` need fixing
392
+ * needsWallet ready to quote once a wallet is set
393
+ * quoting resolving and racing solvers
394
+ * quoted `preview` is ready to confirm
395
+ * switchingChain / approving / signing / submitting confirm() progress
396
+ * tracking order submitted, polling its status
397
+ * done every intent reached a terminal status
398
+ * error something threw; see `error`
399
+ */
400
+ type IntentPhase = 'idle' | 'parsing' | 'offTemplate' | 'invalid' | 'needsWallet' | 'quoting' | 'quoted' | 'switchingChain' | 'approving' | 'signing' | 'submitting' | 'tracking' | 'done' | 'error';
401
+ /** What a confirm card shows: human amounts and names, no addresses or base units. */
402
+ interface IntentPreview {
403
+ action: 'swap' | 'send';
404
+ payAmount: string;
405
+ paySymbol: string;
406
+ srcChainName: string;
407
+ receiveAmount: string;
408
+ receiveSymbol: string;
409
+ dstChainName: string;
410
+ recipient?: string;
411
+ etaSeconds?: number;
412
+ /** Quotes received (1 for a direct wallet transfer). */
413
+ solverCount: number;
414
+ solversQueried?: number;
415
+ raceMs?: number;
416
+ }
417
+ interface IntentState {
418
+ phase: IntentPhase;
419
+ assetsLoading: boolean;
420
+ assetsError: boolean;
421
+ /** The supported-asset set has loaded, so input can be accepted. */
422
+ ready: boolean;
423
+ hint: string | null;
424
+ issues: ValidationIssue[];
425
+ resolved: ResolvedIntent | null;
426
+ /**
427
+ * `solver` goes through the aggregator. `direct` is a same-chain send,
428
+ * which solvers don't quote, so the wallet transfers it itself.
429
+ */
430
+ route: 'solver' | 'direct' | null;
431
+ quote: Quote | null;
432
+ preview: IntentPreview | null;
433
+ /** Escrow route only: an ERC-20 approval to Permit2 comes first. */
434
+ needsApproval: boolean;
435
+ orderId: string | null;
436
+ status: OrderStatus | null;
437
+ order: OrderResponse | null;
438
+ /** Destination-chain explorer link: the fill tx when known, else the recipient. */
439
+ explorerUrl: string | null;
440
+ error: string | null;
441
+ isConnected: boolean;
442
+ /** Multi-intent sentences run one after another. */
443
+ queueIndex: number;
444
+ queueTotal: number;
445
+ }
446
+ interface IntentSession {
447
+ getState(): IntentState;
448
+ /** Called after every state change. Returns an unsubscribe function. */
449
+ subscribe(listener: (state: IntentState) => void): () => void;
450
+ run(text: string): Promise<void>;
451
+ confirm(): Promise<void>;
452
+ reset(): void;
453
+ /** Connect, switch or disconnect. A pending intent continues automatically. */
454
+ setWallet(wallet: IntentWallet | null): void;
455
+ refreshAssets(): Promise<void>;
456
+ /** Set or replace the Hugging Face token used for parsing (null turns parsing off). */
457
+ setHfAccessToken(token: string | null): void;
458
+ /** Stop polling and drop listeners. */
459
+ destroy(): void;
460
+ }
461
+ interface SessionOptions extends ApiOptions {
462
+ /**
463
+ * Your Hugging Face access token, for turning plain English into intents.
464
+ * Parsing runs in the browser, so the token is visible to anyone who loads
465
+ * the page: use a dedicated, inference-only token. Without one, `run()`
466
+ * answers with the example-sentence hint.
467
+ */
468
+ hfAccessToken?: string | null;
469
+ /** Hugging Face model for parsing. */
470
+ hfModel?: ParserOptions['model'];
471
+ /** Share one client across sessions; otherwise built from `apiUrl`/`fetch`. */
472
+ api?: ApiClient;
473
+ wallet?: IntentWallet | null;
474
+ /** Order-status polling interval. Default 2500ms. */
475
+ pollIntervalMs?: number;
476
+ /** Chain reads (Permit2 allowance, receipts). Defaults to the aggregator's RPCs. */
477
+ chainReader?: (chainId: number) => Promise<ChainReader | null>;
478
+ }
479
+ declare function isTerminalStatus(status: OrderStatus): boolean;
480
+ /**
481
+ * The whole intent flow as a framework-free store: parse → validate →
482
+ * resolve → quote → (approve) → sign → submit → track. UI layers subscribe
483
+ * and render `getState()`; they never talk to the aggregator themselves.
484
+ */
485
+ declare function createIntentSession(options?: SessionOptions): IntentSession;
486
+
487
+ /**
488
+ * The registry has two layers (see docs/intent-parser-design.md §2):
489
+ * - alias layer (static): maps loose user words → canonical token symbol / chain.
490
+ * - support layer (dynamic): what solvers can actually fill, from the live aggregator.
491
+ *
492
+ * A word can be a token OR a chain depending on slot (e.g. "eth", "sol"); the grammar
493
+ * decides which table to consult by position, so the same word appears in both.
494
+ */
495
+ interface CanonicalChain {
496
+ id: number;
497
+ name: string;
498
+ }
499
+ declare const TOKEN_ALIASES: Record<string, string>;
500
+ declare const CHAIN_ALIASES: Record<string, CanonicalChain>;
501
+ declare function normalizeToken(raw: string | null): string | null;
502
+ declare function normalizeChain(raw: string | null): CanonicalChain | null;
503
+ interface SupportedSet {
504
+ chains: Set<number>;
505
+ assets: Map<string, SolverAsset>;
506
+ }
507
+ declare const EMPTY_SUPPORTED: SupportedSet;
508
+ declare function buildSupportedSet(assets: SolverAsset[]): SupportedSet;
509
+ declare function findAsset(set: SupportedSet, chainId: number, symbol: string): SolverAsset | undefined;
510
+
511
+ /**
512
+ * Validate a parsed intent against the live support set. Collects EVERY problem
513
+ * (missing / unknown / unsupported) so the user can fix them in one edit.
514
+ * Empty array = ready to resolve.
515
+ */
516
+ declare function validateIntent(raw: RawIntent, supported: SupportedSet): ValidationIssue[];
517
+
518
+ /**
519
+ * Resolve a validated RawIntent into a ResolvedIntent (canonical addresses, chain
520
+ * ids, base-unit amount). PRECONDITION: validateIntent(raw, supported) returned [].
521
+ * Throws if that precondition is violated.
522
+ */
523
+ declare function resolveIntent(raw: RawIntent, supported: SupportedSet, user: string, solanaUser?: string | null): ResolvedIntent;
524
+
525
+ /**
526
+ * Build the POST /api/v1/quotes body from a resolved intent.
527
+ *
528
+ * Shape matches the verified oif-aggregator contract: every address is
529
+ * ERC-7930 interop-encoded (see ./interop), and the input amount is a base-unit
530
+ * decimal string.
531
+ *
532
+ * We request the **Permit2 escrow** path only (`schemes:['permit2']`,
533
+ * `supportedTypes:['oif-escrow-v0']`). The solver's custody logic checks the
534
+ * requested schemes and, when EIP-3009 is offered, prefers it — but the live
535
+ * 3009 open path reverts on-chain (`SignatureAndInputsNotEqual`). Permit2 escrow
536
+ * is the proven-working route (it needs a one-time Permit2 approval, handled in
537
+ * useIntent's confirm flow).
538
+ *
539
+ * For a `send`, `recipient` differs from `user`; for a `swap` they're equal.
540
+ * Either way the input is sourced on `srcChainId` from `user`, and the output
541
+ * is delivered on `dstChainId` to `recipient`.
542
+ */
543
+ declare function buildQuoteRequest(resolved: ResolvedIntent): QuoteRequest;
544
+ /**
545
+ * Build the POST /api/v1/orders body from a quote and its scheme-prefixed
546
+ * signature (produced by ./sign `signQuote`).
547
+ *
548
+ * The submission scheme (`permit2` / `eip3009`) is negotiated at quote time via
549
+ * the quote request's `originSubmission` — the demo does NOT repeat it on the
550
+ * order, so neither do we. The order just pairs the full quote back with its
551
+ * signature.
552
+ */
553
+ declare function buildOrderRequest(quote: Quote, signature: string): OrderRequest;
554
+
555
+ /**
556
+ * ERC-7930 InteropAddress encoding.
557
+ *
558
+ * Ported faithfully from the OIF aggregator demo
559
+ * (`oif-aggregator/demo/src/utils/interopAddress.ts`). The aggregator expects
560
+ * addresses as ERC-7930 "interop" hex strings (NOT plain `0x` addresses): get the
561
+ * encoding wrong and `/quotes` returns zero quotes or an error.
562
+ *
563
+ * Format per EIP-7930:
564
+ * 0x [version(2 bytes)] [chainType(2 bytes)] [chainRefLen(1)] [chainRef] [addrLen(1)] [address]
565
+ *
566
+ * version = 0x0001
567
+ * chainType = 0x0000 (EIP-155)
568
+ * chainRef = chainId as minimal big-endian bytes
569
+ * address = the 20-byte EVM address
570
+ */
571
+ /** An ERC-7930 interop address, hex-encoded. */
572
+ type InteropAddress = `0x${string}`;
573
+ declare function interopAddress(chainId: number, address: string): InteropAddress;
574
+
575
+ /**
576
+ * Permit2 allowance helpers.
577
+ *
578
+ * The OIF escrow (Permit2) flow needs the input ERC-20 to be approved for the
579
+ * canonical Permit2 spender before an escrow order can pull funds. These helpers
580
+ * are framework-light: reads take a viem `PublicClient`, and the approve is
581
+ * exposed both as a plain contract-write request (use with wagmi `useWriteContract`
582
+ * or any signer) and as a convenience that sends via a viem `WalletClient`.
583
+ */
584
+
585
+ /** Canonical Permit2 contract address (same on every chain). */
586
+ declare const PERMIT2_ADDRESS: Address;
587
+ /** Max uint256 — used for an unlimited approval. */
588
+ declare const MAX_UINT256: bigint;
589
+ /**
590
+ * Read the current Permit2 allowance the owner has granted on `token`
591
+ * (i.e. allowance(owner, PERMIT2_ADDRESS)).
592
+ */
593
+ declare function getPermit2Allowance(publicClient: PublicClient, token: Address, owner: Address): Promise<bigint>;
594
+ /** True if the owner's Permit2 allowance on `token` covers `required`. */
595
+ declare function hasPermit2Allowance(publicClient: PublicClient, token: Address, owner: Address, required: bigint): Promise<boolean>;
596
+ /**
597
+ * Build the contract-write request to approve Permit2 to spend `token`.
598
+ * Defaults to an unlimited (max uint256) approval. Returns a plain object you
599
+ * can hand to wagmi `writeContract`/`useWriteContract` or viem `writeContract`.
600
+ */
601
+ declare function buildPermit2ApproveRequest(token: Address, amount?: bigint): {
602
+ address: `0x${string}`;
603
+ abi: readonly [{
604
+ readonly name: "approve";
605
+ readonly type: "function";
606
+ readonly stateMutability: "nonpayable";
607
+ readonly inputs: readonly [{
608
+ readonly name: "spender";
609
+ readonly type: "address";
610
+ }, {
611
+ readonly name: "amount";
612
+ readonly type: "uint256";
613
+ }];
614
+ readonly outputs: readonly [{
615
+ readonly name: "";
616
+ readonly type: "bool";
617
+ }];
618
+ }];
619
+ functionName: "approve";
620
+ args: readonly [`0x${string}`, bigint];
621
+ };
622
+
623
+ export { type AggregationMetadata, type AmountKind, type ApiClient, ApiError, type ApiOptions, type AssetAmount, type AssetLockReference, CHAIN_ALIASES, type CanonicalChain, type ChainInfo, type ChainReader, DEFAULT_API_URL, DEFAULT_INTENT_MODEL, EMPTY_SUPPORTED, HF_ROUTER_URL, type Input, type IntentAction, type IntentPhase, type IntentPreview, type IntentRequest, type IntentSession, type IntentState, type IntentWallet, type InteropAddress, type IssueField, type IssueKind, MAX_UINT256, type Order, type OrderPayload, type OrderRequest, type OrderResponse, type OrderStatus, type OriginSubmission, type Output, PERMIT2_ADDRESS, type ParseResult, type ParserOptions, type Quote, type QuotePreview, type QuoteRequest, type QuotesResponse, type RawIntent, type ResolvedIntent, SOLANA_CHAIN_IDS, type SessionOptions, type Settlement, type Solver, type SolverAsset, type SolverOptions, type SolverSupportedAssets, type SolversResponse, type SupportedSet, TEMPLATE_HINT, TOKEN_ALIASES, type TypedDataSigner, type ValidationIssue, buildOrderRequest, buildPermit2ApproveRequest, buildQuoteRequest, buildSupportedSet, chainName, createApiClient, createIntentSession, explorerUrl, extractIntents, findAsset, getPermit2Allowance, hasPermit2Allowance, interopAddress, isTerminalStatus, normalizeChain, normalizeToken, parseIntent, resolveIntent, rpcReaders, signQuote, stripDomainType, validateIntent, viemWallet };