@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.
- package/LICENSE +21 -0
- package/README.md +88 -0
- package/dist/index.cjs +1160 -0
- package/dist/index.d.cts +623 -0
- package/dist/index.d.ts +623 -0
- package/dist/index.js +1100 -0
- package/package.json +62 -0
package/dist/index.d.cts
ADDED
|
@@ -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 };
|