@pafi-dev/trading 0.1.10 → 0.2.1
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/README.md +132 -378
- package/dist/index.cjs +408 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +269 -46
- package/dist/index.d.ts +269 -46
- package/dist/index.js +378 -36
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,85 +1,82 @@
|
|
|
1
|
-
import { Address, PublicClient } from 'viem';
|
|
2
|
-
import { PartialUserOperation, PoolKey } from '@pafi-dev/core';
|
|
1
|
+
import { Address, PublicClient, Hex } from 'viem';
|
|
2
|
+
import { PartialUserOperation, PoolKey, QuoteResult, PathKey, BestQuote } from '@pafi-dev/core';
|
|
3
3
|
export { PAFI_SUBGRAPH_URL, fetchPafiPools } from '@pafi-dev/core';
|
|
4
4
|
|
|
5
5
|
interface ApiQuoteRequest {
|
|
6
6
|
chainId: number;
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
/** Token user is spending. */
|
|
8
|
+
inputTokenAddress: Address;
|
|
9
|
+
/** Token user wants to receive. */
|
|
10
|
+
outputTokenAddress: Address;
|
|
11
|
+
/** Input token amount (raw decimals — input token's native scale). */
|
|
9
12
|
amount: bigint;
|
|
10
13
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
14
|
+
* Pools to include in routing. Combined with COMMON_POOLS. When
|
|
15
|
+
* either side is a PT, the caller should fetch pools for ALL PTs
|
|
16
|
+
* involved (single PT → USDT → PT' multi-hop) and merge before
|
|
17
|
+
* passing here.
|
|
13
18
|
*/
|
|
14
19
|
pools?: PoolKey[];
|
|
15
20
|
}
|
|
16
21
|
type ApiQuoteError = "QUOTE_UNAVAILABLE" | "AMOUNT_TOO_SMALL_FOR_GAS";
|
|
17
22
|
interface ApiQuoteResponse {
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
/** Echoes `request.amount` for FE bookkeeping. */
|
|
24
|
+
inputAmount: bigint;
|
|
25
|
+
/** Quoted output amount before slippage. 0n when `quoteError` set. */
|
|
26
|
+
estimatedOutputAmount: bigint;
|
|
27
|
+
/** V4 Quoter's gas estimate for the route. */
|
|
20
28
|
gasEstimate: bigint;
|
|
21
29
|
quoteError?: ApiQuoteError;
|
|
22
30
|
}
|
|
23
31
|
interface ApiSwapRequest {
|
|
24
32
|
chainId: number;
|
|
25
33
|
userAddress: Address;
|
|
26
|
-
|
|
27
|
-
|
|
34
|
+
inputTokenAddress: Address;
|
|
35
|
+
outputTokenAddress: Address;
|
|
36
|
+
/** Input token amount to swap (raw token decimals). */
|
|
28
37
|
amount: bigint;
|
|
29
38
|
/** ERC-4337 account nonce for the user's EOA (from EntryPoint). */
|
|
30
39
|
aaNonce: bigint;
|
|
31
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* Slippage tolerance in basis points (1 bps = 0.01%). Default: 50
|
|
42
|
+
* for single-hop, 100 for multi-hop (handler picks based on
|
|
43
|
+
* `bestRoute.path.length`).
|
|
44
|
+
*/
|
|
32
45
|
slippageBps?: number;
|
|
33
|
-
/**
|
|
46
|
+
/** Pools to consider. Caller pre-fetches per-PT pools and merges. */
|
|
34
47
|
pools?: PoolKey[];
|
|
35
48
|
/**
|
|
36
|
-
*
|
|
37
|
-
* sponsoring ERC-4337 gas
|
|
49
|
+
* Operator fee in INPUT token units, paid to PAFI fee recipient as
|
|
50
|
+
* reimbursement for sponsoring ERC-4337 gas.
|
|
38
51
|
*
|
|
39
|
-
* - `undefined` (default): handler auto-quotes via
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* - `0n`: strip the `PT.transfer(pafiFeeRecipient, ...)` call (used
|
|
44
|
-
* for the unsponsored fallback path — user pays ETH gas
|
|
45
|
-
* themselves so there's nothing to reimburse).
|
|
46
|
-
* - explicit `bigint`: override for issuer markup / subsidy
|
|
47
|
-
* scenarios. Sponsor-relayer's `FeeValidator` enforces a 5%
|
|
48
|
-
* tolerance vs its own quote — too low → `INSUFFICIENT_FEE`.
|
|
52
|
+
* - `undefined` (default): handler auto-quotes via
|
|
53
|
+
* `quoteOperatorFeeInputToken` (Chainlink USD/ETH + token oracle).
|
|
54
|
+
* - `0n`: strip the fee transfer (unsponsored fallback path).
|
|
55
|
+
* - explicit `bigint`: override for issuer markup/subsidy scenarios.
|
|
49
56
|
*/
|
|
50
|
-
|
|
57
|
+
gasFeeAmount?: bigint;
|
|
51
58
|
}
|
|
52
59
|
interface ApiSwapResponse {
|
|
53
|
-
/** Unsigned UserOp — attach paymaster
|
|
60
|
+
/** Unsigned UserOp — attach paymaster + user signature, submit to Bundler. */
|
|
54
61
|
userOp: PartialUserOperation;
|
|
55
62
|
/**
|
|
56
|
-
* Fee-stripped fallback variant. Emitted only when `
|
|
57
|
-
*
|
|
58
|
-
* encouraging callers to redundantly resubmit.
|
|
59
|
-
*
|
|
60
|
-
* Submit this when the paymaster refuses sponsorship (the user pays
|
|
61
|
-
* ERC-4337 gas in ETH directly): there's no point charging the
|
|
62
|
-
* operator fee in PT for sponsorship that didn't happen. Pair with
|
|
63
|
-
* `sendWithPaymasterFallback({ txParams, txParamsFallback })`.
|
|
63
|
+
* Fee-stripped fallback variant. Emitted only when `gasFeeAmount > 0`.
|
|
64
|
+
* Submit when the paymaster refuses sponsorship (user pays ETH gas).
|
|
64
65
|
*/
|
|
65
66
|
userOpFallback?: PartialUserOperation;
|
|
66
|
-
/** Raw
|
|
67
|
-
|
|
68
|
-
/** Minimum
|
|
67
|
+
/** Raw output amount before slippage. For display. */
|
|
68
|
+
estimatedOutputAmount: bigint;
|
|
69
|
+
/** Minimum output accepted — encoded in the UserOp calldata. */
|
|
69
70
|
minAmountOut: bigint;
|
|
71
|
+
/** Number of hops in the chosen route. Used by FE to display "PT → USDT → PT1" etc. */
|
|
72
|
+
hops: number;
|
|
70
73
|
/** Swap deadline (unix seconds). Re-request if user doesn't submit in time. */
|
|
71
74
|
deadline: bigint;
|
|
72
75
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* `0n` when the caller forced no-fee mode.
|
|
76
|
+
* Operator fee amount embedded — echoes auto-quote result or override.
|
|
77
|
+
* Denominated in the INPUT token. `0n` when caller forced no-fee mode.
|
|
76
78
|
*/
|
|
77
79
|
feeAmountUsed: bigint;
|
|
78
|
-
/**
|
|
79
|
-
* Recipient address used for the PT fee transfer (always
|
|
80
|
-
* `getContractAddresses(chainId).pafiFeeRecipient`). Echoed for
|
|
81
|
-
* client-side verification before signing.
|
|
82
|
-
*/
|
|
83
80
|
feeRecipient: Address;
|
|
84
81
|
}
|
|
85
82
|
interface ApiPerpDepositRequest {
|
|
@@ -256,4 +253,230 @@ declare class TradingHandlers {
|
|
|
256
253
|
handlePerpDeposit(request: ApiPerpDepositRequest): Promise<ApiPerpDepositResponse>;
|
|
257
254
|
}
|
|
258
255
|
|
|
259
|
-
|
|
256
|
+
declare function checkAllowance(client: PublicClient, token: Address, owner: Address, spender: Address): Promise<bigint>;
|
|
257
|
+
/**
|
|
258
|
+
* Encode an ERC-20 approve(spender, amount) call.
|
|
259
|
+
*/
|
|
260
|
+
declare function buildErc20ApprovalCalldata(spender: Address, amount: bigint): Hex;
|
|
261
|
+
/**
|
|
262
|
+
* Encode a Permit2 approve(token, spender, amount, expiration) call.
|
|
263
|
+
*/
|
|
264
|
+
declare function buildPermit2ApprovalCalldata(token: Address, spender: Address, amount: bigint, expiration: number): Hex;
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Build the calldata inputs[0] (the V4_SWAP command payload) for
|
|
268
|
+
* UniversalRouter.execute.
|
|
269
|
+
*
|
|
270
|
+
* Actions encoded: SWAP_EXACT_IN → SETTLE_ALL → TAKE_ALL
|
|
271
|
+
*
|
|
272
|
+
* Encoding matches the Uniswap V4 SDK's V4Planner — each action's params
|
|
273
|
+
* are individually ABI-encoded, then wrapped together with the action bytes.
|
|
274
|
+
*/
|
|
275
|
+
declare function buildV4SwapInput(currencyIn: Address, path: PathKey[], amountIn: bigint, minAmountOut: bigint, outputCurrency: Address): Hex;
|
|
276
|
+
/**
|
|
277
|
+
* Build the full commands + inputs args for UniversalRouter.execute.
|
|
278
|
+
*/
|
|
279
|
+
declare function buildUniversalRouterExecuteArgs(currencyIn: Address, path: PathKey[], amountIn: bigint, minAmountOut: bigint, outputCurrency: Address): {
|
|
280
|
+
commands: Hex;
|
|
281
|
+
inputs: Hex[];
|
|
282
|
+
};
|
|
283
|
+
/**
|
|
284
|
+
* Build UniversalRouter execute args from a quote result.
|
|
285
|
+
*
|
|
286
|
+
* Takes the output of `findBestQuote` / `quoteBestRoute` and produces
|
|
287
|
+
* ready-to-use `{ commands, inputs }` for `UniversalRouter.execute`.
|
|
288
|
+
*
|
|
289
|
+
* @param quote - Quote result containing the path
|
|
290
|
+
* @param currencyIn - Input token address
|
|
291
|
+
* @param currencyOut - Output token address
|
|
292
|
+
* @param amountIn - Exact input amount (same value passed to the quoter)
|
|
293
|
+
* @param minAmountOut - Minimum acceptable output (caller applies slippage)
|
|
294
|
+
*
|
|
295
|
+
* @deprecated Since v1.4 — the Issuer App no longer handles swaps.
|
|
296
|
+
* For the new PT→USDT batch call on PAFI Web (Scenario 4 with
|
|
297
|
+
* EIP-7702 gas deduction), use `buildSwapWithGasDeduction()` (v1.5).
|
|
298
|
+
* This helper is kept for legacy v0.2.x consumers; will be removed in v2.0.
|
|
299
|
+
*/
|
|
300
|
+
declare function buildSwapFromQuote(params: {
|
|
301
|
+
quote: QuoteResult;
|
|
302
|
+
currencyIn: Address;
|
|
303
|
+
currencyOut: Address;
|
|
304
|
+
amountIn: bigint;
|
|
305
|
+
minAmountOut: bigint;
|
|
306
|
+
}): {
|
|
307
|
+
commands: Hex;
|
|
308
|
+
inputs: Hex[];
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
interface SwapSimulationResult {
|
|
312
|
+
success: boolean;
|
|
313
|
+
gasEstimate: bigint;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Simulate a UniversalRouter.execute swap call via eth_call (no gas spent).
|
|
317
|
+
*
|
|
318
|
+
* Runs the full V4 swap flow — token transfer via Permit2, swap via
|
|
319
|
+
* PoolManager, output settlement — without submitting a transaction.
|
|
320
|
+
* If the simulation reverts, throws a `SimulationError` with the reason.
|
|
321
|
+
*
|
|
322
|
+
* @param client - viem PublicClient
|
|
323
|
+
* @param routerAddress - UniversalRouter contract address
|
|
324
|
+
* @param commands - Packed command bytes (from buildSwapFromQuote)
|
|
325
|
+
* @param inputs - Encoded inputs per command (from buildSwapFromQuote)
|
|
326
|
+
* @param deadline - Unix timestamp after which the tx expires
|
|
327
|
+
* @param from - Address that will execute the swap
|
|
328
|
+
*/
|
|
329
|
+
declare function simulateSwap(client: PublicClient, routerAddress: Address, commands: Hex, inputs: Hex[], deadline: bigint, from: Address): Promise<SwapSimulationResult>;
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* v1.6 — Generalized swap UserOp builder. Direction-agnostic: works
|
|
333
|
+
* for **any** ERC-20 → ERC-20 pair routable through PAFI's V4 pools:
|
|
334
|
+
*
|
|
335
|
+
* - PT → USDT (cashout)
|
|
336
|
+
* - USDT → PT (buy PT with USDT)
|
|
337
|
+
* - PT0 → PT1 (same-issuer multi-token swap; routes via USDT)
|
|
338
|
+
*
|
|
339
|
+
* UserOp shape (atomic batch via EIP-7702 BatchExecutor):
|
|
340
|
+
*
|
|
341
|
+
* 1. `inputToken.approve(Permit2, amountIn + gasFeeAmount)` — single
|
|
342
|
+
* approve covers both swap input + operator gas reimbursement.
|
|
343
|
+
* 2. `Permit2.approve(inputToken, router, amountIn, deadline)` — Permit2
|
|
344
|
+
* authorization to UniversalRouter.
|
|
345
|
+
* 3. `UniversalRouter.execute(commands, inputs, deadline)` — V4 swap
|
|
346
|
+
* `inputToken → outputToken`; user receives ≥ `minAmountOut`.
|
|
347
|
+
* 4. `inputToken.transfer(feeRecipient, gasFeeAmount)` — operator
|
|
348
|
+
* gas reimbursement, paid in INPUT token (omitted when 0).
|
|
349
|
+
*
|
|
350
|
+
* ## Fee model — input-token strategy
|
|
351
|
+
*
|
|
352
|
+
* Operator gas fee is charged in the **input token** (option (a) chosen
|
|
353
|
+
* 2026-04-27). Generic across directions; user only needs to hold a
|
|
354
|
+
* single token in sufficient quantity.
|
|
355
|
+
*
|
|
356
|
+
* User must hold `amountIn + gasFeeAmount` of `inputTokenAddress`.
|
|
357
|
+
*
|
|
358
|
+
* ## PAFI Hook fee
|
|
359
|
+
*
|
|
360
|
+
* The V4 PAFIHook charges 10% on PT → USDT direction (one-way). The
|
|
361
|
+
* 10% is taken at pool level inside `UniversalRouter.execute`, so this
|
|
362
|
+
* builder doesn't model it explicitly — it shows up as a smaller
|
|
363
|
+
* `amountOut` from `findBestQuote`.
|
|
364
|
+
*
|
|
365
|
+
* - PT → USDT: pool charges 10% (output reduced)
|
|
366
|
+
* - USDT → PT: no hook fee
|
|
367
|
+
* - PT0 → PT1: 10% on PT0 → USDT leg, 0% on USDT → PT1 leg
|
|
368
|
+
*
|
|
369
|
+
* ## Order of operations
|
|
370
|
+
*
|
|
371
|
+
* Fee transfer is LAST so a reverting swap also refunds the fee
|
|
372
|
+
* (atomic batch revert semantics).
|
|
373
|
+
*/
|
|
374
|
+
interface BuildSwapUserOpParams {
|
|
375
|
+
/** User's EOA (with EIP-7702 delegation to BatchExecutor). */
|
|
376
|
+
userAddress: Address;
|
|
377
|
+
/** ERC-4337 account nonce — fetched from EntryPoint by the caller. */
|
|
378
|
+
aaNonce: bigint;
|
|
379
|
+
/** Token being spent — approved to Permit2 + UniversalRouter. */
|
|
380
|
+
inputTokenAddress: Address;
|
|
381
|
+
/** Token user receives. */
|
|
382
|
+
outputTokenAddress: Address;
|
|
383
|
+
/** UniversalRouter contract address (chain-specific). */
|
|
384
|
+
universalRouterAddress: Address;
|
|
385
|
+
/** Input token units to swap. User's balance must be ≥ `amountIn + gasFeeAmount`. */
|
|
386
|
+
amountIn: bigint;
|
|
387
|
+
/**
|
|
388
|
+
* Minimum output to accept. Caller applies slippage (typically
|
|
389
|
+
* 50-100 bps; multi-hop routes should bias higher) against a fresh
|
|
390
|
+
* quote. Sub-minimum swap reverts.
|
|
391
|
+
*/
|
|
392
|
+
minAmountOut: bigint;
|
|
393
|
+
/**
|
|
394
|
+
* V4 pool path for the swap. Single-hop = 1 PathKey, multi-hop = N.
|
|
395
|
+
* Get this from `findBestQuote().bestRoute.path`.
|
|
396
|
+
*/
|
|
397
|
+
swapPath: PathKey[];
|
|
398
|
+
/** Unix seconds. After this, the router rejects the swap. */
|
|
399
|
+
deadline: bigint;
|
|
400
|
+
/**
|
|
401
|
+
* Operator gas-reimbursement fee — paid in INPUT token. Omitted from
|
|
402
|
+
* the batch when 0n. Caller is responsible for quoting this in input
|
|
403
|
+
* token units (typically USDT-denominated then converted via
|
|
404
|
+
* `quoteOperatorFee`).
|
|
405
|
+
*/
|
|
406
|
+
gasFeeAmount: bigint;
|
|
407
|
+
/**
|
|
408
|
+
* Where the gas fee lands — typically the canonical PAFI fee
|
|
409
|
+
* recipient from `getContractAddresses(chainId).pafiFeeRecipient`.
|
|
410
|
+
*/
|
|
411
|
+
feeRecipient: Address;
|
|
412
|
+
/** Override ERC-4337 gas estimates. Defaults are conservative. */
|
|
413
|
+
gasLimits?: {
|
|
414
|
+
callGasLimit?: bigint;
|
|
415
|
+
verificationGasLimit?: bigint;
|
|
416
|
+
preVerificationGas?: bigint;
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Build an unsigned UserOp for the generalized swap flow.
|
|
421
|
+
*
|
|
422
|
+
* @throws when `amountIn` is non-positive, `gasFeeAmount` is negative,
|
|
423
|
+
* or `swapPath` is empty.
|
|
424
|
+
*/
|
|
425
|
+
declare function buildSwapUserOp(params: BuildSwapUserOpParams): PartialUserOperation;
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Combine point-token-specific pools and common pools for a given chain.
|
|
429
|
+
* Point token pools are listed first so callers can prioritise them.
|
|
430
|
+
*/
|
|
431
|
+
declare function combineRoutes(chainId: number, pointTokenAddress: Address): PoolKey[];
|
|
432
|
+
/**
|
|
433
|
+
* Build all possible swap paths from `tokenIn` to `tokenOut` using the given
|
|
434
|
+
* pools. Returns an array of PathKey[] routes (each up to `maxHops` hops).
|
|
435
|
+
*
|
|
436
|
+
* Supports both direct single-hop routes and multi-hop routes through
|
|
437
|
+
* intermediate tokens. Each pool is used at most once per path.
|
|
438
|
+
*
|
|
439
|
+
* @param pools - Available pools to route through
|
|
440
|
+
* @param tokenIn - Input token address
|
|
441
|
+
* @param tokenOut - Desired output token address
|
|
442
|
+
* @param maxHops - Maximum number of hops (default 3)
|
|
443
|
+
*/
|
|
444
|
+
declare function buildAllPaths(pools: PoolKey[], tokenIn: Address, tokenOut: Address, maxHops?: number): PathKey[][];
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Quote exact-input for a multi-hop path.
|
|
448
|
+
*/
|
|
449
|
+
declare function quoteExactInput(client: PublicClient, quoterAddress: Address, exactCurrency: Address, path: PathKey[], exactAmount: bigint): Promise<QuoteResult>;
|
|
450
|
+
/**
|
|
451
|
+
* Quote exact-input for a single-hop swap, given an explicit PoolKey and direction.
|
|
452
|
+
*/
|
|
453
|
+
declare function quoteExactInputSingle(client: PublicClient, quoterAddress: Address, poolKey: PoolKey, zeroForOne: boolean, exactAmount: bigint, hookData: `0x${string}`): Promise<{
|
|
454
|
+
amountOut: bigint;
|
|
455
|
+
gasEstimate: bigint;
|
|
456
|
+
}>;
|
|
457
|
+
/**
|
|
458
|
+
* Try multiple PathKey[] routes and return the best quote plus all results.
|
|
459
|
+
* Routes that fail (e.g. pool does not exist) are silently skipped.
|
|
460
|
+
*
|
|
461
|
+
* Uses viem multicall to batch all quotes into a single RPC call for speed.
|
|
462
|
+
*/
|
|
463
|
+
declare function quoteBestRoute(client: PublicClient, quoterAddress: Address, exactCurrency: Address, routes: PathKey[][], exactAmount: bigint): Promise<BestQuote>;
|
|
464
|
+
/**
|
|
465
|
+
* Find and quote the best swap route from `tokenIn` to `tokenOut`.
|
|
466
|
+
*
|
|
467
|
+
* Combines the caller's `pools` with `COMMON_POOLS[chainId]`, builds all
|
|
468
|
+
* possible paths (up to `maxHops`), then quotes them all via a single
|
|
469
|
+
* multicall and returns the best result.
|
|
470
|
+
*
|
|
471
|
+
* @param client - viem PublicClient
|
|
472
|
+
* @param chainId - Chain ID (used to look up COMMON_POOLS and V4_QUOTER_ADDRESSES)
|
|
473
|
+
* @param tokenIn - Input token address
|
|
474
|
+
* @param tokenOut - Desired output token address
|
|
475
|
+
* @param exactAmount - Exact input amount
|
|
476
|
+
* @param pools - Additional pools to consider (e.g. point-token-specific)
|
|
477
|
+
* @param quoterAddress - Override the default V4 Quoter address for this chain
|
|
478
|
+
* @param maxHops - Maximum number of hops per path (default 3)
|
|
479
|
+
*/
|
|
480
|
+
declare function findBestQuote(client: PublicClient, chainId: number, tokenIn: Address, tokenOut: Address, exactAmount: bigint, pools?: PoolKey[], quoterAddress?: Address, maxHops?: number): Promise<BestQuote>;
|
|
481
|
+
|
|
482
|
+
export { type ApiPerpDepositRequest, type ApiPerpDepositResponse, type ApiQuoteError, type ApiQuoteRequest, type ApiQuoteResponse, type ApiSwapRequest, type ApiSwapResponse, type BuildSwapUserOpParams, type SwapSimulationResult, TradingHandlers, type TradingHandlersConfig, buildAllPaths, buildErc20ApprovalCalldata, buildPermit2ApprovalCalldata, buildSwapFromQuote, buildSwapUserOp, buildUniversalRouterExecuteArgs, buildV4SwapInput, checkAllowance, combineRoutes, findBestQuote, quoteBestRoute, quoteExactInput, quoteExactInputSingle, simulateSwap };
|