@pafi-dev/trading 0.4.3 → 0.5.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/README.md +111 -50
- package/dist/index.cjs +179 -261
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +105 -128
- package/dist/index.d.ts +105 -128
- package/dist/index.js +176 -251
- package/dist/index.js.map +1 -1
- package/package.json +13 -2
package/README.md
CHANGED
|
@@ -3,25 +3,29 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@pafi-dev/trading)
|
|
4
4
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
5
5
|
|
|
6
|
-
On-chain trading for PAFI:
|
|
7
|
-
perp deposit. **Direction-agnostic**
|
|
8
|
-
ERC-20 → ERC-20 routable through PAFI's
|
|
6
|
+
On-chain trading for PAFI: V3 swap quote + UserOp build, Orderly
|
|
7
|
+
perp deposit. **Direction-agnostic** — quote and swap any
|
|
8
|
+
ERC-20 → ERC-20 routable through PAFI's Uniswap V3 pools.
|
|
9
9
|
|
|
10
|
-
**Browser + Node-safe.** Stateless (no DB, no signer, no auth).
|
|
11
|
-
`viem ^2`. Plus `@pafi-dev/core` for primitives.
|
|
10
|
+
**Browser + Node-safe.** Stateless (no DB, no signer, no auth).
|
|
11
|
+
Peer-deps: `viem ^2`. Plus `@pafi-dev/core` for primitives.
|
|
12
|
+
|
|
13
|
+
> PAFI's PT pools are standard Uniswap V3 (no hooks). USDT ↔ PT swaps
|
|
14
|
+
> incur only the standard LP fee on top of the SDK's operator fee.
|
|
12
15
|
|
|
13
16
|
---
|
|
14
17
|
|
|
15
18
|
## Why this exists
|
|
16
19
|
|
|
17
|
-
Issuer backends (`@pafi-dev/issuer`) cover
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
Issuer backends (`@pafi-dev/issuer`) cover issuer-signed flows (claim,
|
|
21
|
+
redeem). Trading is the FE-callable surface for actions that don't need
|
|
22
|
+
an issuer signature:
|
|
20
23
|
|
|
21
|
-
- **Quote** PT → USDT, USDT → PT, PT0 → PT1 — pure on-chain
|
|
24
|
+
- **Quote** PT → USDT, USDT → PT, PT0 → PT1 — pure on-chain V3 QuoterV2
|
|
22
25
|
read, multicall'd.
|
|
23
26
|
- **Build swap UserOp** with operator gas-reimbursement fee in the
|
|
24
|
-
input token (auto-quoted via Chainlink). Sponsored +
|
|
27
|
+
input token (auto-quoted via Chainlink + V3 subgraph). Sponsored +
|
|
28
|
+
fallback variants.
|
|
25
29
|
- **Build perp deposit UserOp** via the PAFI Orderly Relay (Relay
|
|
26
30
|
covers LayerZero `msg.value`, user pays USDC fee).
|
|
27
31
|
|
|
@@ -36,6 +40,9 @@ server-canonical quote/swap can wrap `TradingHandlers` directly.
|
|
|
36
40
|
pnpm add @pafi-dev/trading @pafi-dev/core viem
|
|
37
41
|
```
|
|
38
42
|
|
|
43
|
+
Make sure to install **`@pafi-dev/core`** explicitly at top-level
|
|
44
|
+
to avoid pnpm deduping into a nested copy with stale URLs/addresses.
|
|
45
|
+
|
|
39
46
|
---
|
|
40
47
|
|
|
41
48
|
## Quick start (FE)
|
|
@@ -66,7 +73,7 @@ const q = await trading.handleQuote({
|
|
|
66
73
|
// q.estimatedOutputAmount — bigint USDT raw (6 dec)
|
|
67
74
|
|
|
68
75
|
// Swap — builds UserOp ready to submit via Bundler + EIP-7702.
|
|
69
|
-
const swap = await trading.handleSwap({
|
|
76
|
+
const swap = await trading.handleSwap(userAddress, {
|
|
70
77
|
chainId: 8453,
|
|
71
78
|
userAddress,
|
|
72
79
|
inputTokenAddress: POINT_TOKEN,
|
|
@@ -74,42 +81,53 @@ const swap = await trading.handleSwap({
|
|
|
74
81
|
amount: 100n * 10n ** 18n,
|
|
75
82
|
aaNonce,
|
|
76
83
|
pools,
|
|
77
|
-
// gasFeeAmount auto-quotes
|
|
84
|
+
// gasFeeAmount auto-quotes; pass explicit bigint to override.
|
|
78
85
|
// slippageBps auto-picks 50 bps single-hop / 100 bps multi-hop.
|
|
79
86
|
});
|
|
80
87
|
// swap.userOp + swap.userOpFallback (when fee > 0)
|
|
81
|
-
// swap.hops
|
|
88
|
+
// swap.hops, swap.estimatedOutputAmount, swap.minAmountOut
|
|
82
89
|
```
|
|
83
90
|
|
|
84
91
|
---
|
|
85
92
|
|
|
86
93
|
## Direction matrix
|
|
87
94
|
|
|
88
|
-
| Direction | Use case | Hop count |
|
|
95
|
+
| Direction | Use case | Hop count | Fee | Operator fee token |
|
|
89
96
|
| --- | --- | --- | --- | --- |
|
|
90
|
-
| `PT → USDT` | Cashout | 1 |
|
|
91
|
-
| `USDT → PT` | Buy PT | 1 | 0% |
|
|
92
|
-
| `PT0 → PT1` |
|
|
93
|
-
|
|
94
|
-
**Same-issuer only** for PT0 → PT1 — caller is responsible for
|
|
95
|
-
ensuring both PTs belong to the same issuer. SDK doesn't enforce.
|
|
97
|
+
| `PT → USDT` | Cashout | 1 | 0.3% pool fee | USDT (output) |
|
|
98
|
+
| `USDT → PT` | Buy PT | 1 | 0.3% pool fee | PT (output) |
|
|
99
|
+
| `PT0 → PT1` | Multi-token swap | 2 (via USDT) | 0.3% × 2 hops | output PT |
|
|
96
100
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
`estimatedOutputAmount` from `findBestQuote`.
|
|
101
|
+
**Cross-issuer OK** for PT0 → PT1 (e.g. TPT ↔ LTR if both issuers ship
|
|
102
|
+
V3 pools with USDT). The router auto-routes through `USDT`.
|
|
100
103
|
|
|
101
104
|
---
|
|
102
105
|
|
|
103
106
|
## Operator fee strategy
|
|
104
107
|
|
|
105
|
-
The operator gas-reimbursement fee is charged in the **
|
|
106
|
-
(
|
|
107
|
-
needs to hold a single token.
|
|
108
|
+
The operator gas-reimbursement fee is charged in the **output token**.
|
|
109
|
+
User receives `(net - operatorFee)` instead of `net` after the swap.
|
|
108
110
|
|
|
109
|
-
|
|
|
111
|
+
| Output | Auto-quote source | Caller override |
|
|
110
112
|
| --- | --- | --- |
|
|
111
|
-
| PT | `quoteOperatorFeePt` (Chainlink +
|
|
112
|
-
| USDT |
|
|
113
|
+
| PT | `quoteOperatorFeePt` (Chainlink + V3 subgraph) | `gasFeeAmountOutput: bigint` |
|
|
114
|
+
| USDT | `quoteOperatorFeeUsdt` (Chainlink only) | `gasFeeAmountOutput: bigint` |
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
// USDT-output swap (e.g. PT → USDT cashout)
|
|
118
|
+
const swap = await trading.handleSwap(user, {
|
|
119
|
+
// ...
|
|
120
|
+
inputTokenAddress: PT,
|
|
121
|
+
outputTokenAddress: USDT,
|
|
122
|
+
// gasFeeAmountOutput auto-quotes ~$0.008 USDT
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Or override:
|
|
126
|
+
const swap = await trading.handleSwap(user, {
|
|
127
|
+
// ...
|
|
128
|
+
gasFeeAmountOutput: 10_000n, // 0.01 USDT explicit
|
|
129
|
+
});
|
|
130
|
+
```
|
|
113
131
|
|
|
114
132
|
---
|
|
115
133
|
|
|
@@ -125,7 +143,7 @@ const [poolsA, poolsB] = await Promise.all([
|
|
|
125
143
|
]);
|
|
126
144
|
const pools = [...poolsA, ...poolsB];
|
|
127
145
|
|
|
128
|
-
const swap = await trading.handleSwap({
|
|
146
|
+
const swap = await trading.handleSwap(user, {
|
|
129
147
|
chainId: 8453,
|
|
130
148
|
userAddress,
|
|
131
149
|
inputTokenAddress: POINT_TOKEN_0,
|
|
@@ -138,9 +156,14 @@ const swap = await trading.handleSwap({
|
|
|
138
156
|
console.log(swap.hops); // 2 — went through USDT
|
|
139
157
|
```
|
|
140
158
|
|
|
141
|
-
|
|
159
|
+
V3 router auto-picks the best route across `pools + COMMON_POOLS`
|
|
142
160
|
(default `maxHops=3`). Slippage auto-bumps to 100 bps for multi-hop.
|
|
143
161
|
|
|
162
|
+
Verify via subgraph that both PTs have indexed pools with USDT:
|
|
163
|
+
```graphql
|
|
164
|
+
{ pafiTokens { id pool { token0 { id symbol } token1 { id symbol } } } }
|
|
165
|
+
```
|
|
166
|
+
|
|
144
167
|
---
|
|
145
168
|
|
|
146
169
|
## Perp deposit
|
|
@@ -149,9 +172,9 @@ V4 router auto-picks the best route across `pools + COMMON_POOLS`
|
|
|
149
172
|
const deposit = await trading.handlePerpDeposit({
|
|
150
173
|
chainId: 8453,
|
|
151
174
|
userAddress,
|
|
152
|
-
amount: 1_000_000n,
|
|
175
|
+
amount: 1_000_000n, // 1 USDC (6 dec)
|
|
153
176
|
aaNonce,
|
|
154
|
-
brokerId: "orderly",
|
|
177
|
+
brokerId: "orderly", // "orderly" | "woofi_pro" | "logx"
|
|
155
178
|
// viaRelay: true (default) — uses PAFI Orderly Relay (no msg.value needed)
|
|
156
179
|
pointTokenAddress: POINT_TOKEN, // optional — for PT operator fee
|
|
157
180
|
});
|
|
@@ -168,17 +191,26 @@ native ETH.
|
|
|
168
191
|
|
|
169
192
|
## Standalone primitives
|
|
170
193
|
|
|
171
|
-
For callers that want to compose flows beyond
|
|
172
|
-
provides:
|
|
194
|
+
For callers that want to compose flows beyond `TradingHandlers`:
|
|
173
195
|
|
|
174
196
|
```ts
|
|
175
197
|
import {
|
|
176
198
|
buildSwapUserOp,
|
|
199
|
+
buildSwapUserOpExactOut,
|
|
177
200
|
findBestQuote,
|
|
201
|
+
findBestQuoteExactOut,
|
|
178
202
|
simulateSwap,
|
|
179
203
|
buildUniversalRouterExecuteArgs,
|
|
204
|
+
buildUniversalRouterExecuteArgsExactOut,
|
|
205
|
+
buildV3SwapInputExactIn,
|
|
206
|
+
buildV3SwapInputExactOut,
|
|
207
|
+
buildSwapFromQuote,
|
|
180
208
|
buildPermit2ApprovalCalldata,
|
|
209
|
+
buildErc20ApprovalCalldata,
|
|
210
|
+
checkAllowance,
|
|
181
211
|
fetchPafiPools,
|
|
212
|
+
V3_SWAP_EXACT_IN,
|
|
213
|
+
V3_SWAP_EXACT_OUT,
|
|
182
214
|
} from "@pafi-dev/trading";
|
|
183
215
|
```
|
|
184
216
|
|
|
@@ -189,7 +221,7 @@ simulations.
|
|
|
189
221
|
|
|
190
222
|
## Direct path — no AA, no bundler, no sponsor-relayer
|
|
191
223
|
|
|
192
|
-
|
|
224
|
+
Trading ships `swapDirect()` + `perpDepositDirect()` for the FE-only
|
|
193
225
|
flow where the user pays gas in ETH and broadcasts a single type-2 tx
|
|
194
226
|
calling its own EIP-7702 delegated bytecode. No Pimlico API key, no
|
|
195
227
|
sponsor-relayer, no PAFI infra.
|
|
@@ -206,7 +238,6 @@ import { createPublicClient, createWalletClient, custom, http, parseUnits } from
|
|
|
206
238
|
import { base } from "viem/chains";
|
|
207
239
|
|
|
208
240
|
const publicClient = createPublicClient({ chain: base, transport: http() });
|
|
209
|
-
// `walletClient` from Privy embedded wallet, MetaMask, etc.
|
|
210
241
|
const walletClient = createWalletClient({
|
|
211
242
|
account: wallet.address,
|
|
212
243
|
chain: base,
|
|
@@ -227,7 +258,6 @@ const result = await swapDirect({
|
|
|
227
258
|
// optional: slippageBps, deadline, gasFeeAmountOutput, waitForReceipt
|
|
228
259
|
});
|
|
229
260
|
|
|
230
|
-
console.log("Swap tx:", result.txHash);
|
|
231
261
|
// {
|
|
232
262
|
// txHash, receipt?, estimatedOutputAmount, minAmountOut, hops,
|
|
233
263
|
// deadline, feeAmountUsed
|
|
@@ -260,8 +290,8 @@ const result = await perpDepositDirect({
|
|
|
260
290
|
```
|
|
261
291
|
|
|
262
292
|
The Relay still charges its own USDC fee (`Relay.quoteTokenFee`) to
|
|
263
|
-
cover LayerZero `msg.value`;
|
|
264
|
-
(
|
|
293
|
+
cover LayerZero `msg.value`; separate from PAFI's operator fee
|
|
294
|
+
(skipped by default on direct path).
|
|
265
295
|
|
|
266
296
|
### When to use direct vs. AA path
|
|
267
297
|
|
|
@@ -280,6 +310,30 @@ the production gas-free UX via sponsor-relayer.
|
|
|
280
310
|
|
|
281
311
|
---
|
|
282
312
|
|
|
313
|
+
## Plain Swap (Permit2 sig — FE only, no AA)
|
|
314
|
+
|
|
315
|
+
For external wallets (MetaMask) that can't EIP-7702 delegate. Standard
|
|
316
|
+
Uniswap pattern: ERC-20 approve to PERMIT2, off-chain PermitSingle sig,
|
|
317
|
+
`UR.execute([PERMIT2_PERMIT, V3_SWAP_EXACT_IN], ...)`.
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
// Step 1: one-time ERC-20 approve to Permit2 (skip if allowance set)
|
|
321
|
+
// Step 2: sign Permit2 PermitSingle EIP-712 (skip if sub-allowance valid)
|
|
322
|
+
// Step 3: send UR.execute tx — user pays gas in ETH
|
|
323
|
+
|
|
324
|
+
// See privy-pimlico-eip7702-example/app/components/WalletPanel.tsx
|
|
325
|
+
// handleSwapPlain for the full flow.
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
**Cold start**: 1 tx + 1 sig + 1 tx (3 popups).
|
|
329
|
+
**Warm** (sub-allowance fresh): 1 tx.
|
|
330
|
+
|
|
331
|
+
> ⚠️ Common bug: `permitExpiration = (1 << 48) - 1` overflows to
|
|
332
|
+
> `65535` in JS (int32 shift). Use `2 ** 48 - 1` or BigInt for
|
|
333
|
+
> uint48 timestamps.
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
283
337
|
## API reference
|
|
284
338
|
|
|
285
339
|
### `TradingHandlers`
|
|
@@ -292,25 +346,32 @@ Methods:
|
|
|
292
346
|
|
|
293
347
|
| Method | Purpose |
|
|
294
348
|
| --- | --- |
|
|
295
|
-
| `handleQuote({ inputTokenAddress, outputTokenAddress, amount, pools? })` |
|
|
296
|
-
| `
|
|
297
|
-
| `
|
|
349
|
+
| `handleQuote({ inputTokenAddress, outputTokenAddress, amount, pools? })` | V3 exact-input quote. Returns `{ inputAmount, estimatedOutputAmount, gasEstimate, quoteError? }` |
|
|
350
|
+
| `handleQuoteExactOut({ ... })` | V3 exact-output quote |
|
|
351
|
+
| `handleSwap(authedAddr, { userAddress, inputTokenAddress, outputTokenAddress, amount, aaNonce, pools?, gasFeeAmountOutput?, slippageBps? })` | Build UserOp. Returns `{ userOp, userOpFallback?, estimatedOutputAmount, minAmountOut, hops, deadline, feeAmountUsed, feeRecipient }` |
|
|
352
|
+
| `handleSwapExactOut(authedAddr, { ... })` | Build exact-output swap UserOp |
|
|
353
|
+
| `handlePerpDeposit({ userAddress, amount, aaNonce, brokerId, viaRelay?, maxRelayFee?, pointTokenAddress?, gasFeePt? })` | Build perp deposit UserOp |
|
|
298
354
|
|
|
299
355
|
### Free functions
|
|
300
356
|
|
|
301
357
|
- `findBestQuote(client, chainId, tokenIn, tokenOut, amount, pools, quoterAddress?, maxHops?)`
|
|
302
|
-
- `
|
|
303
|
-
- `
|
|
304
|
-
- `buildSwapUserOp(params)` — direction-agnostic UserOp builder
|
|
305
|
-
- `buildUniversalRouterExecuteArgs`, `
|
|
358
|
+
- `findBestQuoteExactOut(...)`
|
|
359
|
+
- `quoteBestRoute`, `quoteBestRouteExactOut`, `quoteExactInput`, `quoteExactInputSingle`, `quoteExactOutput`, `quoteExactOutputSingle`
|
|
360
|
+
- `buildSwapUserOp(params)` / `buildSwapUserOpExactOut(params)` — direction-agnostic UserOp builder
|
|
361
|
+
- `buildUniversalRouterExecuteArgs`, `buildUniversalRouterExecuteArgsExactOut`, `buildV3SwapInputExactIn`, `buildV3SwapInputExactOut`, `buildSwapFromQuote`
|
|
306
362
|
- `buildPermit2ApprovalCalldata`, `buildErc20ApprovalCalldata`, `checkAllowance`
|
|
307
363
|
- `simulateSwap` — `eth_call` dry-run
|
|
308
|
-
- `fetchPafiPools(chainId, pointTokenAddress, subgraphUrl?)`
|
|
309
|
-
- `swapDirect(params)` — FE-direct
|
|
310
|
-
- `
|
|
364
|
+
- `fetchPafiPools(chainId, pointTokenAddress, subgraphUrl?)` — re-exported from core
|
|
365
|
+
- `swapDirect(params)`, `perpDepositDirect(params)` — FE-direct flows
|
|
366
|
+
- `V3_SWAP_EXACT_IN`, `V3_SWAP_EXACT_OUT` — Universal Router V3 command constants
|
|
311
367
|
|
|
312
368
|
---
|
|
313
369
|
|
|
370
|
+
## References
|
|
371
|
+
|
|
372
|
+
- Fee math: [`docs/FEE_FLOW.md`](../../../docs/FEE_FLOW.md)
|
|
373
|
+
- Architecture: [`ARCHITECTURE.md`](../../ARCHITECTURE.md) at SDK root
|
|
374
|
+
|
|
314
375
|
## License
|
|
315
376
|
|
|
316
377
|
Apache-2.0
|