@pafi-dev/core 0.3.0-beta.9 → 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 +165 -1084
- package/dist/auth/index.cjs +16 -2
- package/dist/auth/index.cjs.map +1 -1
- package/dist/auth/index.d.cts +93 -2
- package/dist/auth/index.d.ts +93 -2
- package/dist/auth/index.js +17 -3
- package/dist/{chunk-KJKDLD7N.cjs → chunk-2PY5RNVS.cjs} +11 -11
- package/dist/{chunk-KJKDLD7N.cjs.map → chunk-2PY5RNVS.cjs.map} +1 -1
- package/dist/{chunk-DMW67WBU.cjs → chunk-52SZJDFT.cjs} +6 -2
- package/dist/chunk-52SZJDFT.cjs.map +1 -0
- package/dist/{chunk-Z2V525IS.cjs → chunk-ARZSGP5Y.cjs} +6 -6
- package/dist/{chunk-Z2V525IS.cjs.map → chunk-ARZSGP5Y.cjs.map} +1 -1
- package/dist/{chunk-GWLEEXM4.cjs → chunk-FNJZUNK3.cjs} +90 -2
- package/dist/chunk-FNJZUNK3.cjs.map +1 -0
- package/dist/{chunk-I2P3TIP7.js → chunk-N5M3BLOV.js} +2 -2
- package/dist/{chunk-73CQTNLB.js → chunk-S5CCEO4O.js} +6 -2
- package/dist/{chunk-73CQTNLB.js.map → chunk-S5CCEO4O.js.map} +1 -1
- package/dist/{chunk-W23EJNYG.js → chunk-T6Y32SNK.js} +12 -2
- package/dist/chunk-T6Y32SNK.js.map +1 -0
- package/dist/{chunk-O4SMTUOY.js → chunk-W6VULMCO.js} +90 -2
- package/dist/chunk-W6VULMCO.js.map +1 -0
- package/dist/{chunk-DUY7Z2AI.js → chunk-WTWG6QXP.js} +2 -2
- package/dist/{chunk-EDR5SFJN.cjs → chunk-YU7MFR6Y.cjs} +12 -2
- package/dist/chunk-YU7MFR6Y.cjs.map +1 -0
- package/dist/eip712/index.cjs +3 -3
- package/dist/eip712/index.js +2 -2
- package/dist/{index-B7pGBych.d.cts → index-BEx-Q1bW.d.cts} +23 -40
- package/dist/{index-B06IJlHe.d.ts → index-r4xCdQO7.d.ts} +23 -40
- package/dist/index.cjs +178 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +211 -26
- package/dist/index.d.ts +211 -26
- package/dist/index.js +176 -35
- package/dist/index.js.map +1 -1
- package/dist/quoting/index.cjs +3 -3
- package/dist/quoting/index.js +2 -2
- package/dist/swap/index.cjs +3 -2
- package/dist/swap/index.cjs.map +1 -1
- package/dist/swap/index.d.cts +1 -1
- package/dist/swap/index.d.ts +1 -1
- package/dist/swap/index.js +2 -1
- package/package.json +1 -1
- package/dist/chunk-DMW67WBU.cjs.map +0 -1
- package/dist/chunk-EDR5SFJN.cjs.map +0 -1
- package/dist/chunk-GWLEEXM4.cjs.map +0 -1
- package/dist/chunk-O4SMTUOY.js.map +0 -1
- package/dist/chunk-W23EJNYG.js.map +0 -1
- /package/dist/{chunk-I2P3TIP7.js.map → chunk-N5M3BLOV.js.map} +0 -0
- /package/dist/{chunk-DUY7Z2AI.js.map → chunk-WTWG6QXP.js.map} +0 -0
package/README.md
CHANGED
|
@@ -1,1189 +1,270 @@
|
|
|
1
1
|
# @pafi-dev/core
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
contract ABIs, Relay calldata encoding, on-chain quoting, and swap
|
|
6
|
-
building.
|
|
7
|
-
|
|
8
|
-
## What's new in 0.3.0-alpha.0 — UserOp primitives (v1.4 preview)
|
|
9
|
-
|
|
10
|
-
Preview release adding the **sponsored UserOp building blocks** for
|
|
11
|
-
EIP-7702 + ERC-4337 flows. No breaking changes — all v0.2.x exports
|
|
12
|
-
remain. Additions:
|
|
13
|
-
|
|
14
|
-
- `userop/types.ts` — `UserOperation`, `PartialUserOperation`,
|
|
15
|
-
`Operation`, `ENTRY_POINT_V07` constant
|
|
16
|
-
- `userop/operations.ts` — `erc20TransferOp`, `erc20ApproveOp`,
|
|
17
|
-
`erc20BurnOp`, `rawCallOp`
|
|
18
|
-
- `userop/batchExecute.ts` — `encodeBatchExecute()` +
|
|
19
|
-
`BATCH_EXECUTOR_ABI` (EIP-7702 delegation target)
|
|
20
|
-
- `userop/buildUserOperation.ts` — `buildPartialUserOperation()` +
|
|
21
|
-
`assembleUserOperation()`
|
|
22
|
-
- `paymaster/` — `PaymasterConfig`, `SponsorshipScenario`, module-level
|
|
23
|
-
`setPaymasterConfig` / `getPaymasterConfig`
|
|
24
|
-
- `utils/checkEthAndBranch.ts` — decide sponsored-vs-normal path based
|
|
25
|
-
on the user's native balance
|
|
26
|
-
|
|
27
|
-
**Deprecated:** `buildSwapFromQuote`, `encodeMintAndSwap`,
|
|
28
|
-
`buildRelaySwapParams` — retired from the app-side flow. Still shipped
|
|
29
|
-
for v0.2.x consumers; removed in 2.0.
|
|
30
|
-
|
|
31
|
-
**Not yet ready** (blocked on SC team): `MintRequest` v2 + `BurnConsent`
|
|
32
|
-
EIP-712 builders. Comes in 0.3.0-beta.
|
|
3
|
+
[](https://www.npmjs.com/package/@pafi-dev/core)
|
|
4
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
33
5
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
6
|
+
Core TypeScript SDK for the PAFI point token system. Covers EIP-712 signing, contract addresses + ABIs, ERC-4337 UserOp building, swap/perp calldata encoding, and on-chain quoting.
|
|
7
|
+
|
|
8
|
+
**No HTTP client.** Safe to use in both browser (frontend) and Node.js (issuer backend).
|
|
37
9
|
|
|
38
10
|
---
|
|
39
11
|
|
|
40
|
-
|
|
41
|
-
primitives that need a signer or a provider (or neither) — never the
|
|
42
|
-
HTTP wire. The HTTP contract (routes, request/response types) is owned
|
|
43
|
-
by [`@pafi/issuer`](../issuer/README.md) as the single source of truth,
|
|
44
|
-
and frontends import those types `type`-only so browser bundles never
|
|
45
|
-
pull in server code like `jose` or `node:crypto`.
|
|
12
|
+
## Requirements
|
|
46
13
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
14
|
+
- Node.js >= 18
|
|
15
|
+
- TypeScript >= 5.0
|
|
16
|
+
- `viem` ^2.0.0 (peer dependency)
|
|
17
|
+
|
|
18
|
+
---
|
|
51
19
|
|
|
52
20
|
## Installation
|
|
53
21
|
|
|
54
22
|
```bash
|
|
23
|
+
npm install @pafi-dev/core viem
|
|
24
|
+
# or
|
|
55
25
|
pnpm add @pafi-dev/core viem
|
|
56
26
|
```
|
|
57
27
|
|
|
58
|
-
`viem ^2.0.0` is a peer dependency and must be installed alongside this package.
|
|
59
|
-
|
|
60
|
-
---
|
|
61
|
-
|
|
62
|
-
## Quick Start
|
|
63
|
-
|
|
64
|
-
```ts
|
|
65
|
-
import { createPublicClient, createWalletClient, http } from "viem";
|
|
66
|
-
import { privateKeyToAccount } from "viem/accounts";
|
|
67
|
-
import { PafiSDK } from "@pafi-dev/core";
|
|
68
|
-
|
|
69
|
-
const account = privateKeyToAccount("0x...");
|
|
70
|
-
|
|
71
|
-
const sdk = new PafiSDK({
|
|
72
|
-
chainId: 8453,
|
|
73
|
-
rpcUrl: "https://base-mainnet.g.alchemy.com/v2/...",
|
|
74
|
-
signer: createWalletClient({
|
|
75
|
-
account,
|
|
76
|
-
transport: http("https://base-mainnet.g.alchemy.com/v2/..."),
|
|
77
|
-
}),
|
|
78
|
-
pointTokenAddress: "0xPointToken...",
|
|
79
|
-
relayContractAddress: "0xRelay...",
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
// Fetch on-chain nonce then sign a mint request
|
|
83
|
-
const nonce = await sdk.getMintRequestNonce(account.address);
|
|
84
|
-
|
|
85
|
-
const sig = await sdk.signMintRequest({
|
|
86
|
-
to: account.address,
|
|
87
|
-
amount: 1_000_000n,
|
|
88
|
-
nonce,
|
|
89
|
-
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
console.log(sig.serialized);
|
|
93
|
-
```
|
|
94
|
-
|
|
95
28
|
---
|
|
96
29
|
|
|
97
|
-
##
|
|
30
|
+
## Modules
|
|
98
31
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
| Sub-path | Contents |
|
|
32
|
+
| Import path | What it provides |
|
|
102
33
|
|---|---|
|
|
103
|
-
| `@pafi-dev/core` | All exports
|
|
104
|
-
| `@pafi-dev/core/eip712` | `
|
|
105
|
-
| `@pafi-dev/core/
|
|
106
|
-
| `@pafi-dev/core/
|
|
107
|
-
| `@pafi-dev/core/quoting` | `
|
|
108
|
-
| `@pafi-dev/core/
|
|
109
|
-
| `@pafi-dev/core/
|
|
110
|
-
| `@pafi-dev/core/abi` | All contract ABIs |
|
|
34
|
+
| `@pafi-dev/core` | All exports below |
|
|
35
|
+
| `@pafi-dev/core/eip712` | `signMintRequest`, `verifyMintRequest`, `signBurnRequest`, `signReceiverConsent` |
|
|
36
|
+
| `@pafi-dev/core/contract` | `getContractAddresses()`, ABIs, address constants |
|
|
37
|
+
| `@pafi-dev/core/swap` | `buildSwapWithGasDeduction()` — PT→USDT via UniversalRouter |
|
|
38
|
+
| `@pafi-dev/core/quoting` | `findBestQuote()` — on-chain V4 Quoter multicall |
|
|
39
|
+
| `@pafi-dev/core/auth` | `createLoginMessage()`, `signSponsorAuth()`, `verifySponsorAuth()` |
|
|
40
|
+
| `@pafi-dev/core/relay` | `buildDelegationUserOp()`, `getAaNonce()` |
|
|
111
41
|
|
|
112
42
|
---
|
|
113
43
|
|
|
114
|
-
##
|
|
115
|
-
|
|
116
|
-
### PafiSDK Class
|
|
117
|
-
|
|
118
|
-
The `PafiSDK` class is a convenience wrapper around all pure functions. All methods delegate to the same pure functions exported from the sub-path modules.
|
|
119
|
-
|
|
120
|
-
#### Constructor
|
|
121
|
-
|
|
122
|
-
```ts
|
|
123
|
-
import { PafiSDK } from "@pafi-dev/core";
|
|
44
|
+
## Contract addresses
|
|
124
45
|
|
|
125
|
-
|
|
126
|
-
```
|
|
46
|
+
All deployed PAFI contract addresses live in one place, keyed by `chainId`:
|
|
127
47
|
|
|
128
48
|
```ts
|
|
129
|
-
|
|
130
|
-
chainId?: number;
|
|
131
|
-
pointTokenAddress?: Address;
|
|
132
|
-
relayContractAddress?: Address;
|
|
133
|
-
signer?: WalletClient; // viem WalletClient
|
|
134
|
-
provider?: PublicClient; // viem PublicClient (takes precedence over rpcUrl)
|
|
135
|
-
rpcUrl?: string; // creates a PublicClient automatically if provider is omitted
|
|
136
|
-
}
|
|
137
|
-
```
|
|
49
|
+
import { getContractAddresses, POINT_TOKEN_FACTORY_ADDRESSES } from "@pafi-dev/core";
|
|
138
50
|
|
|
139
|
-
|
|
51
|
+
const addrs = getContractAddresses(8453); // Base mainnet
|
|
52
|
+
// addrs.pointToken "0x7d25E7156E51F865D522fd3ef257a6B5DD41b97e"
|
|
53
|
+
// addrs.issuerRegistry "0xda2D3338CF70F462Ac175F5f2edfa45660CA4f31"
|
|
54
|
+
// addrs.mintingOracle "0xD85165939C700E51c8a45099316C6482634C2Ab9"
|
|
55
|
+
// addrs.pafiHook "0x870cAF9882d3160602AaC1769C2B264A2d8EC044"
|
|
56
|
+
// addrs.usdt "0x5d313485Ba59C3bb91e1A9C0C11782F0b83d5dcd"
|
|
57
|
+
// addrs.batchExecutor "0x7702cb554e6bFb442cb743A7dF23154544a7176C"
|
|
140
58
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
```ts
|
|
144
|
-
sdk.setPointTokenAddress(address: Address): void
|
|
145
|
-
sdk.setRelayContractAddress(address: Address): void
|
|
146
|
-
sdk.setSigner(signer: WalletClient): void
|
|
147
|
-
sdk.setProvider(provider: PublicClient): void
|
|
59
|
+
POINT_TOKEN_FACTORY_ADDRESSES[8453] // "0x36c0BAb2faBE45EfA6d13001143e43A266Af673B"
|
|
148
60
|
```
|
|
149
61
|
|
|
150
|
-
|
|
62
|
+
Protocol constants (same on all EVM chains):
|
|
151
63
|
|
|
152
64
|
```ts
|
|
153
|
-
|
|
154
|
-
//
|
|
155
|
-
|
|
65
|
+
import { ENTRY_POINT_V07, PERMIT2_ADDRESS } from "@pafi-dev/core";
|
|
66
|
+
// ENTRY_POINT_V07 "0x0000000071727De22E5E9d8BAf0edAc6f37da032"
|
|
67
|
+
// PERMIT2_ADDRESS "0x000000000022D473030F116dDEE9F6B43aC78BA3"
|
|
156
68
|
```
|
|
157
69
|
|
|
158
70
|
---
|
|
159
71
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
#### Pure functions (from `@pafi-dev/core/eip712`)
|
|
163
|
-
|
|
164
|
-
```ts
|
|
165
|
-
import {
|
|
166
|
-
buildDomain,
|
|
167
|
-
signMintRequest,
|
|
168
|
-
verifyMintRequest,
|
|
169
|
-
signReceiverConsent,
|
|
170
|
-
verifyReceiverConsent,
|
|
171
|
-
} from "@pafi-dev/core/eip712";
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
##### `buildDomain`
|
|
175
|
-
|
|
176
|
-
```ts
|
|
177
|
-
function buildDomain(config: PointTokenDomainConfig): {
|
|
178
|
-
name: string;
|
|
179
|
-
version: "1";
|
|
180
|
-
chainId: number;
|
|
181
|
-
verifyingContract: Address;
|
|
182
|
-
}
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
Constructs the EIP-712 domain object. Version is always `"1"`.
|
|
72
|
+
## EIP-712 signing
|
|
186
73
|
|
|
187
|
-
|
|
74
|
+
Used by the **issuer backend** to sign mint/burn authorization. The frontend
|
|
75
|
+
sends the signature alongside the UserOp to the Relay contract.
|
|
188
76
|
|
|
189
77
|
```ts
|
|
190
|
-
|
|
191
|
-
walletClient: WalletClient,
|
|
192
|
-
domain: PointTokenDomainConfig,
|
|
193
|
-
message: MintRequest,
|
|
194
|
-
): Promise<EIP712Signature>
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
Signs a `MintRequest` struct with the connected wallet.
|
|
198
|
-
|
|
199
|
-
##### `verifyMintRequest`
|
|
200
|
-
|
|
201
|
-
```ts
|
|
202
|
-
async function verifyMintRequest(
|
|
203
|
-
domain: PointTokenDomainConfig,
|
|
204
|
-
message: MintRequest,
|
|
205
|
-
signature: Hex,
|
|
206
|
-
expectedMinter: Address,
|
|
207
|
-
): Promise<SignatureVerification>
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
Recovers the signer from a `MintRequest` signature and compares against `expectedMinter`.
|
|
211
|
-
|
|
212
|
-
##### `signReceiverConsent`
|
|
213
|
-
|
|
214
|
-
```ts
|
|
215
|
-
async function signReceiverConsent(
|
|
216
|
-
walletClient: WalletClient,
|
|
217
|
-
domain: PointTokenDomainConfig,
|
|
218
|
-
message: ReceiverConsent,
|
|
219
|
-
): Promise<EIP712Signature>
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
Signs a `ReceiverConsent` struct.
|
|
223
|
-
|
|
224
|
-
##### `verifyReceiverConsent`
|
|
225
|
-
|
|
226
|
-
```ts
|
|
227
|
-
async function verifyReceiverConsent(
|
|
228
|
-
domain: PointTokenDomainConfig,
|
|
229
|
-
message: ReceiverConsent,
|
|
230
|
-
signature: Hex,
|
|
231
|
-
expectedReceiver: Address,
|
|
232
|
-
): Promise<SignatureVerification>
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
Recovers and validates a `ReceiverConsent` signature.
|
|
236
|
-
|
|
237
|
-
#### PafiSDK methods
|
|
238
|
-
|
|
239
|
-
```ts
|
|
240
|
-
await sdk.signMintRequest(message: MintRequest): Promise<EIP712Signature>
|
|
241
|
-
await sdk.verifyMintRequest(message: MintRequest, signature: Hex, expectedMinter: Address): Promise<SignatureVerification>
|
|
242
|
-
await sdk.signReceiverConsent(message: ReceiverConsent): Promise<EIP712Signature>
|
|
243
|
-
await sdk.verifyReceiverConsent(message: ReceiverConsent, signature: Hex, expectedReceiver: Address): Promise<SignatureVerification>
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
All four methods automatically resolve the domain from on-chain state via `getDomain()`.
|
|
247
|
-
|
|
248
|
-
---
|
|
249
|
-
|
|
250
|
-
### Relay Calldata
|
|
251
|
-
|
|
252
|
-
#### Pure functions (from `@pafi-dev/core/relay`)
|
|
253
|
-
|
|
254
|
-
```ts
|
|
255
|
-
import {
|
|
256
|
-
encodeMintAndSwap,
|
|
257
|
-
decodeMintAndSwap,
|
|
258
|
-
encodeMintAndSwapV2,
|
|
259
|
-
decodeMintAndSwapV2,
|
|
260
|
-
} from "@pafi-dev/core/relay";
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
##### V1 (Relay.sol)
|
|
264
|
-
|
|
265
|
-
```ts
|
|
266
|
-
// ABI-encode a mintAndSwap(MintParams, SwapParams) call for Relay.sol
|
|
267
|
-
function encodeMintAndSwap(mint: MintParams, swap: SwapParams): Hex
|
|
268
|
-
|
|
269
|
-
// Decode calldata produced by encodeMintAndSwap
|
|
270
|
-
function decodeMintAndSwap(calldata: Hex): { mint: MintParams; swap: SwapParams }
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
##### V2 (RelayV2.sol)
|
|
274
|
-
|
|
275
|
-
```ts
|
|
276
|
-
// ABI-encode a mintAndSwap(MintParams, SwapParams) call for RelayV2.sol
|
|
277
|
-
function encodeMintAndSwapV2(mint: MintParams, swap: SwapParams): Hex
|
|
278
|
-
|
|
279
|
-
// Decode calldata produced by encodeMintAndSwapV2
|
|
280
|
-
function decodeMintAndSwapV2(calldata: Hex): { mint: MintParams; swap: SwapParams }
|
|
281
|
-
```
|
|
78
|
+
import { signMintRequest, verifyMintRequest } from "@pafi-dev/core";
|
|
282
79
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const calldata = encodeMintAndSwap(
|
|
289
|
-
{
|
|
290
|
-
pointToken: "0xPointToken...",
|
|
291
|
-
receiver: "0xReceiver...",
|
|
292
|
-
amount: 1_000_000n,
|
|
293
|
-
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
|
|
294
|
-
minterSig: "0x...",
|
|
295
|
-
receiverSig: "0x...",
|
|
296
|
-
},
|
|
297
|
-
{
|
|
298
|
-
path: [
|
|
299
|
-
{
|
|
300
|
-
intermediateCurrency: "0xUSDT...",
|
|
301
|
-
fee: 500,
|
|
302
|
-
tickSpacing: 10,
|
|
303
|
-
hooks: "0x0000000000000000000000000000000000000000",
|
|
304
|
-
hookData: "0x",
|
|
305
|
-
},
|
|
306
|
-
],
|
|
307
|
-
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600),
|
|
308
|
-
},
|
|
309
|
-
);
|
|
310
|
-
```
|
|
80
|
+
const domain = {
|
|
81
|
+
name: "GG56 Points", // must match PointToken.name() on-chain
|
|
82
|
+
chainId: 8453,
|
|
83
|
+
verifyingContract: addrs.pointToken,
|
|
84
|
+
};
|
|
311
85
|
|
|
312
|
-
|
|
86
|
+
const { signature } = await signMintRequest(issuerWalletClient, domain, {
|
|
87
|
+
to: userAddress,
|
|
88
|
+
amount: 500n * 10n ** 18n,
|
|
89
|
+
nonce: await getMintRequestNonce(publicClient, addrs.pointToken, userAddress),
|
|
90
|
+
deadline: BigInt(Math.floor(Date.now() / 1000) + 900), // 15 min
|
|
91
|
+
});
|
|
313
92
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
sdk.decodeMintAndSwap(calldata: Hex): { mint: MintParams; swap: SwapParams }
|
|
317
|
-
sdk.encodeExtData(minAmountOut: bigint, feeInUsdt: bigint): Hex
|
|
318
|
-
sdk.buildRelaySwapParams({ quote, minAmountOut, feeInUsdt, deadline }): { swapParams, extData }
|
|
93
|
+
// Offline verification (optional — Relay contract verifies on-chain)
|
|
94
|
+
const { valid } = await verifyMintRequest(domain, request, signature, expectedSigner);
|
|
319
95
|
```
|
|
320
96
|
|
|
321
97
|
---
|
|
322
98
|
|
|
323
|
-
|
|
99
|
+
## SponsorAuth — paymaster authorization
|
|
324
100
|
|
|
325
|
-
|
|
101
|
+
The issuer backend signs a `SponsorAuth` payload so the PAFI paymaster proxy can
|
|
102
|
+
verify the request is authorized before spending gas on behalf of the user.
|
|
326
103
|
|
|
327
104
|
```ts
|
|
328
105
|
import {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
} from "@pafi-dev/core/contract";
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
| Function | Signature | Description |
|
|
338
|
-
|---|---|---|
|
|
339
|
-
| `getMintRequestNonce` | `(client, pointToken, receiver) => Promise<bigint>` | Current `MintRequest` nonce for `receiver` |
|
|
340
|
-
| `getReceiverConsentNonce` | `(client, pointToken, receiver) => Promise<bigint>` | Current `ReceiverConsent` nonce for `receiver` |
|
|
341
|
-
| `isMinter` | `(client, pointToken, account) => Promise<boolean>` | Returns `true` if `account` has the minter role |
|
|
342
|
-
| `getTokenName` | `(client, pointToken) => Promise<string>` | ERC-20 `name()` — used as the EIP-712 domain name |
|
|
343
|
-
| `getPointTokenIssuerAddress` | `(client, pointToken) => Promise<Address>` | Issuer address registered on the token |
|
|
344
|
-
|
|
345
|
-
#### IssuerRegistry
|
|
346
|
-
|
|
347
|
-
```ts
|
|
348
|
-
import { getIssuer, isActiveIssuer } from "@pafi-dev/core/contract";
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
| Function | Signature | Description |
|
|
352
|
-
|---|---|---|
|
|
353
|
-
| `getIssuer` | `(client, registryAddress, issuer) => Promise<Issuer>` | Full `Issuer` struct for the given address |
|
|
354
|
-
| `isActiveIssuer` | `(client, registryAddress, issuer) => Promise<boolean>` | Whether the issuer is currently active |
|
|
355
|
-
|
|
356
|
-
#### MintingOracle
|
|
357
|
-
|
|
358
|
-
```ts
|
|
359
|
-
import { verifyMintCap, getPointTokenIssuer } from "@pafi-dev/core/contract";
|
|
360
|
-
```
|
|
361
|
-
|
|
362
|
-
| Function | Signature | Description |
|
|
363
|
-
|---|---|---|
|
|
364
|
-
| `verifyMintCap` | `(client, oracleAddress, issuer, amount) => Promise<void>` | Reverts if `amount` would exceed the issuer's mint cap |
|
|
365
|
-
| `getPointTokenIssuer` | `(client, oracleAddress, pointToken) => Promise<Address>` | Maps a point token address back to its issuer |
|
|
366
|
-
|
|
367
|
-
#### Relay helpers
|
|
368
|
-
|
|
369
|
-
```ts
|
|
370
|
-
import { getFeeBasisPoints, getSlippageBasisPoints, getUsdt } from "@pafi-dev/core/contract";
|
|
371
|
-
```
|
|
372
|
-
|
|
373
|
-
| Function | Signature | Description |
|
|
374
|
-
|---|---|---|
|
|
375
|
-
| `getFeeBasisPoints` | `(client, relayAddress) => Promise<bigint>` | Protocol fee in basis points |
|
|
376
|
-
| `getSlippageBasisPoints` | `(client, relayAddress) => Promise<bigint>` | Slippage tolerance in basis points |
|
|
377
|
-
| `getUsdt` | `(client, relayAddress) => Promise<Address>` | USDT address used by the Relay contract |
|
|
378
|
-
|
|
379
|
-
#### PafiSDK methods
|
|
380
|
-
|
|
381
|
-
```ts
|
|
382
|
-
await sdk.getMintRequestNonce(receiver: Address): Promise<bigint>
|
|
383
|
-
await sdk.getReceiverConsentNonce(receiver: Address): Promise<bigint>
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
#### PafiSDK quoting + swap methods
|
|
387
|
-
|
|
388
|
-
```ts
|
|
389
|
-
// Quote — finds the best route via multicall
|
|
390
|
-
await sdk.findBestQuote(tokenIn, tokenOut, amount, pools?, quoterAddress?): Promise<BestQuote>
|
|
106
|
+
signSponsorAuth,
|
|
107
|
+
verifySponsorAuth,
|
|
108
|
+
computeCallDataHash,
|
|
109
|
+
type SponsorAuthPayload,
|
|
110
|
+
} from "@pafi-dev/core";
|
|
391
111
|
|
|
392
|
-
//
|
|
393
|
-
|
|
112
|
+
// Issuer backend: build + sign
|
|
113
|
+
const payload: SponsorAuthPayload = {
|
|
114
|
+
chainId: 8453,
|
|
115
|
+
sender: userAddress,
|
|
116
|
+
callDataHash: computeCallDataHash(userOp.callData),
|
|
117
|
+
nonce: BigInt(Date.now()) * 1_000_000n + BigInt(Math.floor(Math.random() * 1_000_000)),
|
|
118
|
+
expiresAt: Math.floor(Date.now() / 1000) + 600, // 10 min
|
|
119
|
+
scenario: "mint", // "mint" | "burn" | "swap"
|
|
120
|
+
issuerId: "gg56",
|
|
121
|
+
};
|
|
394
122
|
|
|
395
|
-
|
|
396
|
-
sdk.buildRelaySwapParams({ quote, minAmountOut, feeInUsdt, deadline }): { swapParams, extData }
|
|
123
|
+
const sig = await signSponsorAuth(issuerWalletClient, payload);
|
|
397
124
|
|
|
398
|
-
//
|
|
399
|
-
|
|
125
|
+
// Paymaster proxy: verify before forwarding to Pimlico
|
|
126
|
+
const result = await verifySponsorAuth(payload, sig, expectedIssuerAddress);
|
|
127
|
+
// result.ok true/false
|
|
128
|
+
// result.reason "EXPIRED" | "INVALID_SIGNER" | "INVALID_SIGNATURE_FORMAT"
|
|
400
129
|
```
|
|
401
130
|
|
|
402
131
|
---
|
|
403
132
|
|
|
404
|
-
|
|
133
|
+
## Swap calldata — PT → USDT
|
|
405
134
|
|
|
406
|
-
|
|
135
|
+
Builds the `callData` for a `BatchExecutor.execute()` call that:
|
|
136
|
+
1. Approves Permit2
|
|
137
|
+
2. Sets a Permit2 allowance for UniversalRouter
|
|
138
|
+
3. Swaps PT → USDT via UniversalRouter
|
|
139
|
+
4. Optionally deducts a gas fee in PT
|
|
407
140
|
|
|
408
141
|
```ts
|
|
409
142
|
import {
|
|
410
|
-
|
|
411
|
-
quoteExactInputSingle,
|
|
412
|
-
quoteBestRoute,
|
|
143
|
+
buildSwapWithGasDeduction,
|
|
413
144
|
findBestQuote,
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
} from "@pafi-dev/core
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
##### `quoteExactInput`
|
|
420
|
-
|
|
421
|
-
```ts
|
|
422
|
-
async function quoteExactInput(
|
|
423
|
-
client: PublicClient,
|
|
424
|
-
quoterAddress: Address,
|
|
425
|
-
exactCurrency: Address,
|
|
426
|
-
path: PathKey[],
|
|
427
|
-
exactAmount: bigint,
|
|
428
|
-
): Promise<QuoteResult>
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
Quotes a multi-hop exact-input swap against the V4 quoter.
|
|
432
|
-
|
|
433
|
-
##### `quoteExactInputSingle`
|
|
434
|
-
|
|
435
|
-
```ts
|
|
436
|
-
async function quoteExactInputSingle(
|
|
437
|
-
client: PublicClient,
|
|
438
|
-
quoterAddress: Address,
|
|
439
|
-
poolKey: PoolKey,
|
|
440
|
-
zeroForOne: boolean,
|
|
441
|
-
exactAmount: bigint,
|
|
442
|
-
hookData: Hex,
|
|
443
|
-
): Promise<{ amountOut: bigint; gasEstimate: bigint }>
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
Quotes a single-hop swap given an explicit pool key and swap direction.
|
|
447
|
-
|
|
448
|
-
##### `quoteBestRoute`
|
|
449
|
-
|
|
450
|
-
```ts
|
|
451
|
-
async function quoteBestRoute(
|
|
452
|
-
client: PublicClient,
|
|
453
|
-
quoterAddress: Address,
|
|
454
|
-
exactCurrency: Address,
|
|
455
|
-
routes: PathKey[][],
|
|
456
|
-
exactAmount: bigint,
|
|
457
|
-
): Promise<BestQuote>
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
Quotes multiple routes via a single `multicall` RPC call and returns the one with the highest `amountOut`. Routes that fail (e.g. pool does not exist) are silently dropped. Throws if no routes succeed.
|
|
461
|
-
|
|
462
|
-
##### `findBestQuote`
|
|
463
|
-
|
|
464
|
-
```ts
|
|
465
|
-
async function findBestQuote(
|
|
466
|
-
client: PublicClient,
|
|
467
|
-
chainId: number,
|
|
468
|
-
tokenIn: Address,
|
|
469
|
-
tokenOut: Address,
|
|
470
|
-
exactAmount: bigint,
|
|
471
|
-
pools?: PoolKey[],
|
|
472
|
-
quoterAddress?: Address,
|
|
473
|
-
maxHops?: number,
|
|
474
|
-
): Promise<BestQuote>
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
One-call quoting helper. Merges caller-provided `pools` with `COMMON_POOLS[chainId]`, builds all possible paths via `buildAllPaths`, then quotes them all via multicall. Uses `V4_QUOTER_ADDRESSES[chainId]` unless `quoterAddress` is provided.
|
|
478
|
-
|
|
479
|
-
##### `buildAllPaths`
|
|
480
|
-
|
|
481
|
-
```ts
|
|
482
|
-
function buildAllPaths(
|
|
483
|
-
pools: PoolKey[],
|
|
484
|
-
tokenIn: Address,
|
|
485
|
-
tokenOut: Address,
|
|
486
|
-
maxHops?: number,
|
|
487
|
-
): PathKey[][]
|
|
488
|
-
```
|
|
489
|
-
|
|
490
|
-
Builds all possible swap paths from `tokenIn` to `tokenOut` using DFS through the given pools. Each pool is used at most once per path. Returns an array of `PathKey[]` routes (up to `maxHops`, default 3).
|
|
491
|
-
|
|
492
|
-
##### `combineRoutes`
|
|
493
|
-
|
|
494
|
-
```ts
|
|
495
|
-
function combineRoutes(
|
|
496
|
-
chainId: number,
|
|
497
|
-
pointTokenAddress: Address,
|
|
498
|
-
): PoolKey[]
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
Merges `POINT_TOKEN_POOLS[chainId][pointTokenAddress]` and `COMMON_POOLS[chainId]` into a single pool list, with point token pools listed first.
|
|
145
|
+
getContractAddresses,
|
|
146
|
+
UNIVERSAL_ROUTER_ADDRESSES,
|
|
147
|
+
} from "@pafi-dev/core";
|
|
502
148
|
|
|
503
|
-
|
|
149
|
+
const addrs = getContractAddresses(8453);
|
|
504
150
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
const { bestRoute, allRoutes } = await findBestQuote(
|
|
514
|
-
client,
|
|
515
|
-
8453, // Base chain ID — auto-resolves quoter address
|
|
516
|
-
pointToken,
|
|
517
|
-
USDC,
|
|
518
|
-
1_000_000n,
|
|
519
|
-
pointTokenPools, // merged with COMMON_POOLS[8453]
|
|
151
|
+
// 1. Quote (on-chain V4 Quoter multicall — read-only)
|
|
152
|
+
const quote = await findBestQuote(
|
|
153
|
+
publicClient,
|
|
154
|
+
8453,
|
|
155
|
+
addrs.pointToken,
|
|
156
|
+
addrs.usdt,
|
|
157
|
+
500n * 10n ** 18n,
|
|
158
|
+
pools, // PoolKey[] from issuer backend GET /pools
|
|
520
159
|
);
|
|
521
160
|
|
|
522
|
-
|
|
523
|
-
|
|
161
|
+
// 2. Build unsigned UserOp
|
|
162
|
+
const userOp = buildSwapWithGasDeduction({
|
|
163
|
+
chainId: 8453,
|
|
164
|
+
sender: userAddress,
|
|
165
|
+
pointTokenAddress: addrs.pointToken,
|
|
166
|
+
usdtAddress: addrs.usdt,
|
|
167
|
+
universalRouterAddress: UNIVERSAL_ROUTER_ADDRESSES[8453],
|
|
168
|
+
pafiHookAddress: addrs.pafiHook,
|
|
169
|
+
quote,
|
|
170
|
+
slippageBps: 50,
|
|
171
|
+
deadline: BigInt(Math.floor(Date.now() / 1000) + 300),
|
|
172
|
+
aaNonce: await getAaNonce(publicClient, userAddress),
|
|
173
|
+
batchExecutorAddress: addrs.batchExecutor,
|
|
174
|
+
gasFeePt: 0n, // set > 0 to deduct operator fee in PT
|
|
175
|
+
});
|
|
524
176
|
```
|
|
525
177
|
|
|
526
178
|
---
|
|
527
179
|
|
|
528
|
-
|
|
180
|
+
## EIP-7702 delegation
|
|
529
181
|
|
|
530
|
-
|
|
182
|
+
One-time setup: sponsors the EIP-7702 delegation so the user's EOA gains
|
|
183
|
+
`BatchExecutor` capabilities without spending native ETH.
|
|
531
184
|
|
|
532
185
|
```ts
|
|
533
186
|
import {
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
} from "@pafi-dev/core/swap";
|
|
540
|
-
```
|
|
541
|
-
|
|
542
|
-
##### `checkAllowance`
|
|
543
|
-
|
|
544
|
-
```ts
|
|
545
|
-
async function checkAllowance(
|
|
546
|
-
client: PublicClient,
|
|
547
|
-
token: Address,
|
|
548
|
-
owner: Address,
|
|
549
|
-
spender: Address,
|
|
550
|
-
): Promise<bigint>
|
|
551
|
-
```
|
|
552
|
-
|
|
553
|
-
Reads the ERC-20 allowance of `spender` on behalf of `owner`.
|
|
554
|
-
|
|
555
|
-
##### `buildErc20ApprovalCalldata`
|
|
556
|
-
|
|
557
|
-
```ts
|
|
558
|
-
function buildErc20ApprovalCalldata(spender: Address, amount: bigint): Hex
|
|
559
|
-
```
|
|
560
|
-
|
|
561
|
-
Encodes `ERC20.approve(spender, amount)`.
|
|
562
|
-
|
|
563
|
-
##### `buildPermit2ApprovalCalldata`
|
|
564
|
-
|
|
565
|
-
```ts
|
|
566
|
-
function buildPermit2ApprovalCalldata(
|
|
567
|
-
token: Address,
|
|
568
|
-
spender: Address,
|
|
569
|
-
amount: bigint,
|
|
570
|
-
expiration: number,
|
|
571
|
-
): Hex
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
Encodes `Permit2.approve(token, spender, amount, expiration)`.
|
|
575
|
-
|
|
576
|
-
##### `buildUniversalRouterExecuteArgs`
|
|
577
|
-
|
|
578
|
-
```ts
|
|
579
|
-
function buildUniversalRouterExecuteArgs(
|
|
580
|
-
currencyIn: Address,
|
|
581
|
-
path: PathKey[],
|
|
582
|
-
amountIn: bigint,
|
|
583
|
-
minAmountOut: bigint,
|
|
584
|
-
outputCurrency: Address,
|
|
585
|
-
): { commands: Hex; inputs: Hex[] }
|
|
586
|
-
```
|
|
587
|
-
|
|
588
|
-
Builds the `commands` and `inputs` arguments for `UniversalRouter.execute`. The command sequence is `V4_SWAP` (`0x10`). Actions encoded inside the payload are `SWAP_EXACT_IN → SETTLE_ALL → TAKE_ALL`.
|
|
589
|
-
|
|
590
|
-
##### `buildV4SwapInput`
|
|
591
|
-
|
|
592
|
-
```ts
|
|
593
|
-
function buildV4SwapInput(
|
|
594
|
-
currencyIn: Address,
|
|
595
|
-
path: PathKey[],
|
|
596
|
-
amountIn: bigint,
|
|
597
|
-
minAmountOut: bigint,
|
|
598
|
-
outputCurrency: Address,
|
|
599
|
-
): Hex
|
|
600
|
-
```
|
|
601
|
-
|
|
602
|
-
Encodes only the V4_SWAP command payload (`inputs[0]`). Use `buildUniversalRouterExecuteArgs` unless you need the raw payload for custom command composition.
|
|
603
|
-
|
|
604
|
-
##### `buildSwapFromQuote`
|
|
605
|
-
|
|
606
|
-
```ts
|
|
607
|
-
function buildSwapFromQuote(params: {
|
|
608
|
-
quote: QuoteResult;
|
|
609
|
-
currencyIn: Address;
|
|
610
|
-
currencyOut: Address;
|
|
611
|
-
amountIn: bigint;
|
|
612
|
-
minAmountOut: bigint;
|
|
613
|
-
}): { commands: Hex; inputs: Hex[] }
|
|
614
|
-
```
|
|
615
|
-
|
|
616
|
-
Build UniversalRouter execute args directly from a `QuoteResult` (returned by `findBestQuote` or `quoteBestRoute`). The caller provides `minAmountOut` after applying their own slippage tolerance.
|
|
187
|
+
buildDelegationUserOp,
|
|
188
|
+
getAaNonce,
|
|
189
|
+
getContractAddresses,
|
|
190
|
+
BATCH_EXECUTOR_ADDRESS_BASE_MAINNET,
|
|
191
|
+
} from "@pafi-dev/core";
|
|
617
192
|
|
|
618
|
-
|
|
193
|
+
const aaNonce = await getAaNonce(publicClient, userAddress);
|
|
619
194
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
// 1. Quote
|
|
625
|
-
const { bestRoute } = await findBestQuote(client, 8453, tokenIn, USDC, amountIn, myPools);
|
|
626
|
-
|
|
627
|
-
// 2. Build swap calldata (apply slippage externally)
|
|
628
|
-
const minReceive = bestRoute.amountOut * 99n / 100n; // 1% slippage
|
|
629
|
-
const { commands, inputs } = buildSwapFromQuote({
|
|
630
|
-
quote: bestRoute,
|
|
631
|
-
currencyIn: tokenIn,
|
|
632
|
-
currencyOut: USDC,
|
|
633
|
-
amountIn,
|
|
634
|
-
minAmountOut: minReceive,
|
|
195
|
+
const userOp = buildDelegationUserOp({
|
|
196
|
+
userAddress,
|
|
197
|
+
aaNonce,
|
|
635
198
|
});
|
|
636
199
|
|
|
637
|
-
//
|
|
200
|
+
// Then: sign the EIP-7702 authorization off-chain via Privy
|
|
201
|
+
// const authorization = await signAuthorization({ contractAddress: BATCH_EXECUTOR_ADDRESS_BASE_MAINNET, ... });
|
|
202
|
+
// await smartClient.sendTransaction({ to: userAddress, data: "0x", authorization });
|
|
638
203
|
```
|
|
639
204
|
|
|
640
205
|
---
|
|
641
206
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
#### Pure functions (from `@pafi-dev/core/auth`)
|
|
645
|
-
|
|
646
|
-
```ts
|
|
647
|
-
import {
|
|
648
|
-
createLoginMessage,
|
|
649
|
-
parseLoginMessage,
|
|
650
|
-
verifyLoginMessage,
|
|
651
|
-
} from "@pafi-dev/core/auth";
|
|
652
|
-
```
|
|
653
|
-
|
|
654
|
-
These are stateless helpers — they build, parse, and verify EIP-4361
|
|
655
|
-
login messages without touching the network. The frontend uses them to
|
|
656
|
-
construct the message the wallet signs; the backend uses `verifyLoginMessage`
|
|
657
|
-
inside its own `AuthService` (see `@pafi/issuer`).
|
|
658
|
-
|
|
659
|
-
```ts
|
|
660
|
-
function createLoginMessage(params: {
|
|
661
|
-
domain: string; // e.g. "app.example.com"
|
|
662
|
-
address: Address; // user's wallet
|
|
663
|
-
chainId: number;
|
|
664
|
-
nonce: string; // from issuer backend /auth/nonce
|
|
665
|
-
uri: string; // e.g. "https://app.example.com"
|
|
666
|
-
statement?: string; // human-readable sign-in message
|
|
667
|
-
version?: string; // defaults to "1"
|
|
668
|
-
issuedAt?: Date;
|
|
669
|
-
expirationTime?: Date;
|
|
670
|
-
notBefore?: Date;
|
|
671
|
-
requestId?: string;
|
|
672
|
-
}): string
|
|
673
|
-
|
|
674
|
-
function parseLoginMessage(message: string): LoginMessageParams
|
|
675
|
-
|
|
676
|
-
async function verifyLoginMessage(
|
|
677
|
-
message: string,
|
|
678
|
-
signature: Hex,
|
|
679
|
-
): Promise<{ valid: boolean; address: Address }>
|
|
680
|
-
```
|
|
681
|
-
|
|
682
|
-
#### PafiSDK helpers
|
|
207
|
+
## SIWE login message
|
|
683
208
|
|
|
684
209
|
```ts
|
|
685
|
-
|
|
686
|
-
await sdk.createLoginMessage(params: Omit<LoginMessageParams, "address" | "chainId">): Promise<string>
|
|
687
|
-
|
|
688
|
-
// Sign a login message with the current signer (personal_sign)
|
|
689
|
-
await sdk.signLoginMessage(message: string): Promise<Hex>
|
|
690
|
-
```
|
|
210
|
+
import { createLoginMessage } from "@pafi-dev/core";
|
|
691
211
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
//
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
// 2. Build + sign the login message
|
|
700
|
-
const message = await sdk.createLoginMessage({
|
|
701
|
-
domain: "app.example.com",
|
|
702
|
-
uri: "https://app.example.com",
|
|
703
|
-
nonce,
|
|
212
|
+
const message = createLoginMessage({
|
|
213
|
+
address: userAddress,
|
|
214
|
+
domain: "app.gg56.com",
|
|
215
|
+
chainId: 8453,
|
|
216
|
+
nonce: nonceFromServer, // GET /auth/nonce
|
|
217
|
+
issuedAt: new Date().toISOString(),
|
|
218
|
+
uri: "https://app.gg56.com",
|
|
704
219
|
});
|
|
705
|
-
const signature = await sdk.signLoginMessage(message);
|
|
706
|
-
|
|
707
|
-
// 3. POST to the issuer backend, receive a JWT
|
|
708
|
-
const { token, userAddress } = await fetch(`${ISSUER_URL}/auth/login`, {
|
|
709
|
-
method: "POST",
|
|
710
|
-
headers: { "Content-Type": "application/json" },
|
|
711
|
-
body: JSON.stringify({ message, signature }),
|
|
712
|
-
}).then(r => r.json());
|
|
713
|
-
|
|
714
|
-
// 4. Store the JWT and attach it to subsequent protected requests
|
|
715
|
-
// (handled by the frontend's own state management — @pafi-dev/core does
|
|
716
|
-
// not manage tokens)
|
|
717
|
-
```
|
|
718
|
-
|
|
719
|
-
> **Why isn't there an HTTP client?** The HTTP contract (routes,
|
|
720
|
-
> request/response shapes) is owned by `@pafi/issuer`. Frontends import
|
|
721
|
-
> those types `type`-only and build their own `fetch()` calls. This
|
|
722
|
-
> keeps the frontend bundle free of server-side dependencies like
|
|
723
|
-
> `jose` and `node:crypto`, and ensures the protocol has exactly one
|
|
724
|
-
> source of truth.
|
|
725
|
-
|
|
726
|
-
---
|
|
727
220
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
All ABIs are available from `@pafi-dev/core/abi` or from the root import.
|
|
731
|
-
|
|
732
|
-
```ts
|
|
733
|
-
import {
|
|
734
|
-
pointTokenAbi,
|
|
735
|
-
relayAbi,
|
|
736
|
-
relayV2Abi,
|
|
737
|
-
issuerRegistryAbi,
|
|
738
|
-
pointTokenFactoryAbi,
|
|
739
|
-
mintingOracleAbi,
|
|
740
|
-
erc20Abi,
|
|
741
|
-
universalRouterAbi,
|
|
742
|
-
permit2Abi,
|
|
743
|
-
v4QuoterAbi,
|
|
744
|
-
} from "@pafi-dev/core/abi";
|
|
221
|
+
// User signs with wallet, then POST /auth/login { message, signature }
|
|
745
222
|
```
|
|
746
223
|
|
|
747
|
-
| Export | Contract |
|
|
748
|
-
|---|---|
|
|
749
|
-
| `pointTokenAbi` | PointToken ERC-20 with minting and nonce functions |
|
|
750
|
-
| `relayAbi` | Relay.sol — `mintAndSwap`, fee and slippage reads |
|
|
751
|
-
| `relayV2Abi` | RelayV2.sol — same interface, new deployment |
|
|
752
|
-
| `issuerRegistryAbi` | IssuerRegistry — `getIssuer`, `isActiveIssuer` |
|
|
753
|
-
| `pointTokenFactoryAbi` | PointTokenFactory |
|
|
754
|
-
| `mintingOracleAbi` | MintingOracle — `verifyMintCap`, `pointTokenToIssuer` |
|
|
755
|
-
| `erc20Abi` | Standard ERC-20 (`allowance`, `approve`, `transfer`, `balanceOf`) |
|
|
756
|
-
| `universalRouterAbi` | Uniswap UniversalRouter — `execute` |
|
|
757
|
-
| `permit2Abi` | Uniswap Permit2 — `approve` |
|
|
758
|
-
| `v4QuoterAbi` | Uniswap V4 Quoter — `quoteExactInput`, `quoteExactInputSingle` |
|
|
759
|
-
|
|
760
224
|
---
|
|
761
225
|
|
|
762
|
-
|
|
226
|
+
## Perp deposit calldata — USDC → Orderly
|
|
763
227
|
|
|
764
228
|
```ts
|
|
765
229
|
import {
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
receiverConsentTypes,
|
|
230
|
+
buildPerpDepositWithGasDeduction,
|
|
231
|
+
computeAccountId,
|
|
232
|
+
BROKER_HASHES,
|
|
233
|
+
TOKEN_HASHES,
|
|
234
|
+
ORDERLY_VAULT_ADDRESSES,
|
|
772
235
|
} from "@pafi-dev/core";
|
|
773
|
-
```
|
|
774
|
-
|
|
775
|
-
| Constant | Type | Description |
|
|
776
|
-
|---|---|---|
|
|
777
|
-
| `SUPPORTED_CHAINS` | `Record<number, ChainConfig>` | Chain ID to chain metadata map |
|
|
778
|
-
| `COMMON_TOKENS` | `Record<number, Record<string, Address>>` | Chain ID to `{ symbol → address }` token map |
|
|
779
|
-
| `COMMON_POOLS` | `Record<number, PoolKey[]>` | Chain ID to common pool list (e.g. stablecoin pairs) |
|
|
780
|
-
| `POINT_TOKEN_POOLS` | `Record<number, Record<Address, PoolKey[]>>` | Chain ID to per-point-token pool list |
|
|
781
|
-
| `mintRequestTypes` | `const` | EIP-712 typed data types for `MintRequest` |
|
|
782
|
-
| `receiverConsentTypes` | `const` | EIP-712 typed data types for `ReceiverConsent` |
|
|
783
|
-
|
|
784
|
-
`COMMON_POOLS` and `POINT_TOKEN_POOLS` are consumed by `combineRoutes` to assemble candidate routes for quoting.
|
|
785
|
-
|
|
786
|
-
---
|
|
787
|
-
|
|
788
|
-
### Errors
|
|
789
|
-
|
|
790
|
-
All errors extend `PafiSDKError`, which extends `Error`.
|
|
791
|
-
|
|
792
|
-
```ts
|
|
793
|
-
import { PafiSDKError, ConfigurationError, SigningError, ApiError } from "@pafi-dev/core";
|
|
794
|
-
```
|
|
795
|
-
|
|
796
|
-
| Class | Thrown when |
|
|
797
|
-
|---|---|
|
|
798
|
-
| `PafiSDKError` | Base class — not thrown directly |
|
|
799
|
-
| `ConfigurationError` | A required field (`pointTokenAddress`, `signer`, `provider`, etc.) is not set when a method requires it |
|
|
800
|
-
| `SigningError` | A signing operation fails |
|
|
801
|
-
| `ApiError` | Generic HTTP error helper for callers writing their own `fetch()` against the issuer backend; carries an optional `status` |
|
|
802
|
-
|
|
803
|
-
#### Example
|
|
804
|
-
|
|
805
|
-
```ts
|
|
806
|
-
import { ConfigurationError, SigningError } from "@pafi-dev/core";
|
|
807
|
-
|
|
808
|
-
try {
|
|
809
|
-
await sdk.signMintRequest(message);
|
|
810
|
-
} catch (err) {
|
|
811
|
-
if (err instanceof ConfigurationError) {
|
|
812
|
-
console.error(`SDK misconfigured: ${err.message}`);
|
|
813
|
-
} else if (err instanceof SigningError) {
|
|
814
|
-
console.error(`Signing failed: ${err.message}`);
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
```
|
|
818
|
-
|
|
819
|
-
---
|
|
820
|
-
|
|
821
|
-
## EIP-712 Type Reference
|
|
822
|
-
|
|
823
|
-
The PointToken contract is the authoritative EIP-712 domain contract.
|
|
824
|
-
|
|
825
|
-
### Domain
|
|
826
|
-
|
|
827
|
-
```
|
|
828
|
-
name: <token name from ERC-20 name()>
|
|
829
|
-
version: "1"
|
|
830
|
-
chainId: <network chain ID>
|
|
831
|
-
verifyingContract: <PointToken address>
|
|
832
|
-
```
|
|
833
|
-
|
|
834
|
-
### MintRequest
|
|
835
|
-
|
|
836
|
-
```
|
|
837
|
-
MintRequest(address to, uint256 amount, uint256 nonce, uint256 deadline)
|
|
838
|
-
```
|
|
839
|
-
|
|
840
|
-
| Field | Type | Description |
|
|
841
|
-
|---|---|---|
|
|
842
|
-
| `to` | `address` | Recipient of the minted tokens |
|
|
843
|
-
| `amount` | `uint256` | Token amount to mint |
|
|
844
|
-
| `nonce` | `uint256` | Anti-replay nonce from `mintRequestNonces(to)` |
|
|
845
|
-
| `deadline` | `uint256` | Unix timestamp after which the signature expires |
|
|
846
|
-
|
|
847
|
-
The minter (issuer signer) signs this struct. The nonce must match `pointToken.mintRequestNonces(to)` at the time of execution.
|
|
848
|
-
|
|
849
|
-
### ReceiverConsent
|
|
850
|
-
|
|
851
|
-
```
|
|
852
|
-
ReceiverConsent(address onBehalfOf, address originalReceiver, uint256 amount, uint256 nonce, uint256 deadline)
|
|
853
|
-
```
|
|
854
236
|
|
|
855
|
-
|
|
856
|
-
|---|---|---|
|
|
857
|
-
| `onBehalfOf` | `address` | Address whose points are being redirected |
|
|
858
|
-
| `originalReceiver` | `address` | The original recipient who is granting consent |
|
|
859
|
-
| `amount` | `uint256` | Token amount involved in the consent |
|
|
860
|
-
| `nonce` | `uint256` | Anti-replay nonce from `receiverConsentNonces(originalReceiver)` |
|
|
861
|
-
| `deadline` | `uint256` | Unix timestamp after which the signature expires |
|
|
237
|
+
const accountId = computeAccountId(userAddress, BROKER_HASHES["woofi_pro"]);
|
|
862
238
|
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
---
|
|
866
|
-
|
|
867
|
-
## PointToken Mint Paths
|
|
868
|
-
|
|
869
|
-
The Relay contract supports three mint paths depending on which signatures are provided:
|
|
870
|
-
|
|
871
|
-
| Path | Signer | Receiver consent required | Description |
|
|
872
|
-
|---|---|---|---|
|
|
873
|
-
| 1 | Minter (issuer signer) | No | Issuer mints directly to `receiver`. Only the `minterSig` is validated. |
|
|
874
|
-
| 2 | Minter + Receiver | Yes | Issuer mints on behalf of `receiver`, who has pre-signed a `ReceiverConsent`. Both signatures are validated. |
|
|
875
|
-
| 3 | Minter + Relayer | No | Issuer authorizes a relayer to execute the mint-and-swap. The relayer submits the transaction. |
|
|
876
|
-
|
|
877
|
-
In all paths, the `MintParams.minterSig` is a `MintRequest` EIP-712 signature. Path 2 additionally requires `MintParams.receiverSig`, which is a `ReceiverConsent` EIP-712 signature.
|
|
878
|
-
|
|
879
|
-
---
|
|
880
|
-
|
|
881
|
-
## Type Reference
|
|
882
|
-
|
|
883
|
-
### Core types
|
|
884
|
-
|
|
885
|
-
```ts
|
|
886
|
-
interface MintRequest {
|
|
887
|
-
to: Address;
|
|
888
|
-
amount: bigint;
|
|
889
|
-
nonce: bigint;
|
|
890
|
-
deadline: bigint;
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
interface ReceiverConsent {
|
|
894
|
-
onBehalfOf: Address;
|
|
895
|
-
originalReceiver: Address;
|
|
896
|
-
amount: bigint;
|
|
897
|
-
nonce: bigint;
|
|
898
|
-
deadline: bigint;
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
interface EIP712Signature {
|
|
902
|
-
v: number;
|
|
903
|
-
r: Hex;
|
|
904
|
-
s: Hex;
|
|
905
|
-
serialized: Hex;
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
interface SignatureVerification {
|
|
909
|
-
isValid: boolean;
|
|
910
|
-
recoveredAddress: Address;
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
interface PointTokenDomainConfig {
|
|
914
|
-
name: string;
|
|
915
|
-
verifyingContract: Address;
|
|
916
|
-
chainId: number;
|
|
917
|
-
}
|
|
918
|
-
```
|
|
919
|
-
|
|
920
|
-
### Relay types
|
|
921
|
-
|
|
922
|
-
```ts
|
|
923
|
-
interface MintParams {
|
|
924
|
-
pointToken: Address;
|
|
925
|
-
receiver: Address;
|
|
926
|
-
amount: bigint;
|
|
927
|
-
deadline: bigint;
|
|
928
|
-
minterSig: Hex;
|
|
929
|
-
receiverSig: Hex;
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
interface PathKey {
|
|
933
|
-
intermediateCurrency: Address;
|
|
934
|
-
fee: number;
|
|
935
|
-
tickSpacing: number;
|
|
936
|
-
hooks: Address;
|
|
937
|
-
hookData: Hex;
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
interface SwapParams {
|
|
941
|
-
path: PathKey[];
|
|
942
|
-
deadline: bigint;
|
|
943
|
-
}
|
|
944
|
-
```
|
|
945
|
-
|
|
946
|
-
### Pool and issuer types
|
|
947
|
-
|
|
948
|
-
```ts
|
|
949
|
-
interface PoolKey {
|
|
950
|
-
currency0: Address;
|
|
951
|
-
currency1: Address;
|
|
952
|
-
fee: number;
|
|
953
|
-
tickSpacing: number;
|
|
954
|
-
hooks: Address;
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
interface Issuer {
|
|
958
|
-
issuerAddress: Address;
|
|
959
|
-
signerAddress: Address;
|
|
960
|
-
name: string;
|
|
961
|
-
symbol: string;
|
|
962
|
-
declaredTotalSupply: bigint;
|
|
963
|
-
capBasisPoints: number;
|
|
964
|
-
active: boolean;
|
|
965
|
-
pointToken: Address;
|
|
966
|
-
mintingOracle: Address;
|
|
967
|
-
}
|
|
968
|
-
```
|
|
969
|
-
|
|
970
|
-
### Quoting types
|
|
971
|
-
|
|
972
|
-
```ts
|
|
973
|
-
interface QuoteResult {
|
|
974
|
-
amountOut: bigint;
|
|
975
|
-
gasEstimate: bigint;
|
|
976
|
-
path: PathKey[];
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
interface BestQuote {
|
|
980
|
-
bestRoute: QuoteResult;
|
|
981
|
-
allRoutes: QuoteResult[];
|
|
982
|
-
}
|
|
983
|
-
```
|
|
984
|
-
|
|
985
|
-
---
|
|
986
|
-
|
|
987
|
-
## Integration Guide
|
|
988
|
-
|
|
989
|
-
### Flow 1: Direct swap via UniversalRouter
|
|
990
|
-
|
|
991
|
-
Use this when a user wants to swap tokens directly (not through the PAFI Relay).
|
|
992
|
-
|
|
993
|
-
```ts
|
|
994
|
-
import { PafiSDK } from "@pafi-dev/core";
|
|
995
|
-
import { UNIVERSAL_ROUTER_ADDRESSES } from "@pafi-dev/core";
|
|
996
|
-
import { universalRouterAbi } from "@pafi-dev/core/abi";
|
|
997
|
-
|
|
998
|
-
const sdk = new PafiSDK({
|
|
999
|
-
chainId: 8453,
|
|
1000
|
-
rpcUrl: "https://base-mainnet.g.alchemy.com/v2/...",
|
|
1001
|
-
signer: walletClient,
|
|
1002
|
-
});
|
|
1003
|
-
|
|
1004
|
-
// 1. Quote — discovers all routes and picks the best via multicall
|
|
1005
|
-
const { bestRoute, allRoutes } = await sdk.findBestQuote(
|
|
1006
|
-
tokenIn, // e.g. WETH address
|
|
1007
|
-
USDC, // output token
|
|
1008
|
-
parseEther("1"), // input amount
|
|
1009
|
-
myPools, // optional: your own pools (merged with COMMON_POOLS)
|
|
1010
|
-
);
|
|
1011
|
-
|
|
1012
|
-
console.log(`Best route: ${formatUnits(bestRoute.amountOut, 6)} USDC`);
|
|
1013
|
-
console.log(`Found ${allRoutes.length} valid routes`);
|
|
1014
|
-
|
|
1015
|
-
// 2. Build swap calldata — caller decides minAmountOut (slippage)
|
|
1016
|
-
const minReceive = bestRoute.amountOut * 99n / 100n; // 1% slippage
|
|
1017
|
-
const { commands, inputs } = sdk.buildSwapFromQuote({
|
|
1018
|
-
quote: bestRoute,
|
|
1019
|
-
currencyIn: tokenIn,
|
|
1020
|
-
currencyOut: USDC,
|
|
1021
|
-
amountIn: parseEther("1"),
|
|
1022
|
-
minAmountOut: minReceive,
|
|
1023
|
-
});
|
|
1024
|
-
|
|
1025
|
-
// 3. Approve (one-time): token → Permit2 → UniversalRouter
|
|
1026
|
-
// ERC20.approve(PERMIT2, MAX)
|
|
1027
|
-
// Permit2.approve(token, UNIVERSAL_ROUTER, MAX, MAX_EXPIRY)
|
|
1028
|
-
|
|
1029
|
-
// 4. Execute swap
|
|
1030
|
-
const deadline = BigInt(Math.floor(Date.now() / 1000) + 1800);
|
|
1031
|
-
await walletClient.writeContract({
|
|
1032
|
-
address: UNIVERSAL_ROUTER_ADDRESSES[8453],
|
|
1033
|
-
abi: universalRouterAbi,
|
|
1034
|
-
functionName: "execute",
|
|
1035
|
-
args: [commands, inputs, deadline],
|
|
1036
|
-
});
|
|
1037
|
-
```
|
|
1038
|
-
|
|
1039
|
-
### Flow 2: Mint-and-swap via Relay (the PAFI cash-out flow)
|
|
1040
|
-
|
|
1041
|
-
The frontend signs, the issuer backend submits. This is the primary PAFI flow.
|
|
1042
|
-
|
|
1043
|
-
#### Frontend (signs + sends to backend)
|
|
1044
|
-
|
|
1045
|
-
The SDK builds the EIP-712 typed data object. You can sign it with any signer:
|
|
1046
|
-
- **viem WalletClient** — use `sdk.signReceiverConsent()` directly
|
|
1047
|
-
- **Privy / WalletConnect / external** — use `sdk.buildReceiverConsentTypedData()` to get the typed data, then pass it to the external signer's `signTypedData`
|
|
1048
|
-
|
|
1049
|
-
```ts
|
|
1050
|
-
import { PafiSDK } from "@pafi-dev/core";
|
|
1051
|
-
|
|
1052
|
-
const sdk = new PafiSDK({
|
|
1053
|
-
chainId: 8453,
|
|
1054
|
-
rpcUrl: "https://base-mainnet.g.alchemy.com/v2/...",
|
|
1055
|
-
pointTokenAddress: "0xPointToken...",
|
|
1056
|
-
// signer is optional — not needed if using Privy/external signer
|
|
1057
|
-
});
|
|
1058
|
-
|
|
1059
|
-
// 1. Quote
|
|
1060
|
-
const { bestRoute } = await sdk.findBestQuote(
|
|
1061
|
-
pointTokenAddress, USDC, amount, pointTokenPools,
|
|
1062
|
-
);
|
|
1063
|
-
|
|
1064
|
-
// 2. Set swap terms
|
|
1065
|
-
const minReceive = bestRoute.amountOut * 99n / 100n;
|
|
1066
|
-
const feeInUsdt = 1_000_000n; // 1 USDC relayer fee
|
|
1067
|
-
const extData = sdk.encodeExtData(minReceive, feeInUsdt);
|
|
1068
|
-
|
|
1069
|
-
// 3. Read nonce + build typed data
|
|
1070
|
-
const nonce = await sdk.getReceiverConsentNonce(userAddress);
|
|
1071
|
-
const deadline = BigInt(Math.floor(Date.now() / 1000) + 1800);
|
|
1072
|
-
const consentMessage = {
|
|
1073
|
-
onBehalfOf: relayAddress,
|
|
1074
|
-
originalReceiver: userAddress,
|
|
1075
|
-
amount,
|
|
1076
|
-
nonce,
|
|
1077
|
-
deadline,
|
|
1078
|
-
extData,
|
|
1079
|
-
};
|
|
1080
|
-
|
|
1081
|
-
// 4a. Sign with Privy (or any external signer)
|
|
1082
|
-
const typedData = await sdk.buildReceiverConsentTypedData(consentMessage);
|
|
1083
|
-
const receiverSig = await privy.signTypedData(typedData);
|
|
1084
|
-
// receiverSig is a 0x-prefixed hex string (65 bytes)
|
|
1085
|
-
|
|
1086
|
-
// 4b. Or sign with viem WalletClient (if signer is set on SDK)
|
|
1087
|
-
// const { serialized: receiverSig } = await sdk.signReceiverConsent(consentMessage);
|
|
1088
|
-
|
|
1089
|
-
// 5. Send to issuer backend (HTTP — not part of @pafi-dev/core)
|
|
1090
|
-
await fetch(`${ISSUER_URL}/claim-and-swap`, {
|
|
1091
|
-
method: "POST",
|
|
1092
|
-
headers: { "Content-Type": "application/json" },
|
|
1093
|
-
body: JSON.stringify({
|
|
1094
|
-
receiver: userAddress,
|
|
1095
|
-
amount: amount.toString(),
|
|
1096
|
-
deadline: deadline.toString(),
|
|
1097
|
-
receiverSig,
|
|
1098
|
-
path: bestRoute.path,
|
|
1099
|
-
minAmountOut: minReceive.toString(),
|
|
1100
|
-
feeInUsdt: feeInUsdt.toString(),
|
|
1101
|
-
}),
|
|
1102
|
-
});
|
|
1103
|
-
```
|
|
1104
|
-
|
|
1105
|
-
#### Issuer backend (signs + submits tx)
|
|
1106
|
-
|
|
1107
|
-
The backend can sign with a viem WalletClient, or use `buildMintRequestTypedData`
|
|
1108
|
-
to get the typed data for an external signer (HSM/KMS).
|
|
1109
|
-
|
|
1110
|
-
```ts
|
|
1111
|
-
import { PafiSDK } from "@pafi-dev/core";
|
|
1112
|
-
import { relayAbi } from "@pafi-dev/core/abi";
|
|
1113
|
-
|
|
1114
|
-
const sdk = new PafiSDK({
|
|
239
|
+
const userOp = buildPerpDepositWithGasDeduction({
|
|
1115
240
|
chainId: 8453,
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
// const { serialized: minterSig } = await sdk.signMintRequest(mintMessage);
|
|
1127
|
-
|
|
1128
|
-
// Option B: Build typed data for external signer (HSM/KMS/Privy)
|
|
1129
|
-
const typedData = await sdk.buildMintRequestTypedData(mintMessage);
|
|
1130
|
-
const minterSig = await externalSigner.signTypedData(typedData);
|
|
1131
|
-
|
|
1132
|
-
// 2. Build Relay params from the frontend's quote path
|
|
1133
|
-
const { swapParams, extData } = sdk.buildRelaySwapParams({
|
|
1134
|
-
quote: { path: requestBody.path },
|
|
1135
|
-
minAmountOut: BigInt(requestBody.minAmountOut),
|
|
1136
|
-
feeInUsdt: BigInt(requestBody.feeInUsdt),
|
|
1137
|
-
deadline: BigInt(requestBody.deadline),
|
|
1138
|
-
});
|
|
1139
|
-
|
|
1140
|
-
// 3. Submit to Relay contract
|
|
1141
|
-
await relayerWallet.writeContract({
|
|
1142
|
-
address: relayAddress,
|
|
1143
|
-
abi: relayAbi,
|
|
1144
|
-
functionName: "mintAndSwap",
|
|
1145
|
-
args: [
|
|
1146
|
-
{
|
|
1147
|
-
pointToken: pointTokenAddress,
|
|
1148
|
-
receiver,
|
|
1149
|
-
amount,
|
|
1150
|
-
deadline,
|
|
1151
|
-
minterSig,
|
|
1152
|
-
receiverSig: requestBody.receiverSig,
|
|
1153
|
-
extData,
|
|
1154
|
-
},
|
|
1155
|
-
swapParams,
|
|
1156
|
-
],
|
|
241
|
+
sender: userAddress,
|
|
242
|
+
usdcAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
243
|
+
vaultAddress: ORDERLY_VAULT_ADDRESSES[8453],
|
|
244
|
+
amount: 100n * 10n ** 6n,
|
|
245
|
+
accountId,
|
|
246
|
+
brokerHash: BROKER_HASHES["woofi_pro"],
|
|
247
|
+
tokenHash: TOKEN_HASHES["USDC"],
|
|
248
|
+
layerZeroFee: await vault.getDepositFee(),
|
|
249
|
+
aaNonce: await getAaNonce(publicClient, userAddress),
|
|
250
|
+
batchExecutorAddress: addrs.batchExecutor,
|
|
1157
251
|
});
|
|
1158
252
|
```
|
|
1159
253
|
|
|
1160
254
|
---
|
|
1161
255
|
|
|
1162
|
-
##
|
|
256
|
+
## Changelog
|
|
1163
257
|
|
|
1164
|
-
|
|
258
|
+
### 0.4.0
|
|
259
|
+
- `sponsorAuth` module added — `signSponsorAuth`, `verifySponsorAuth`, `computeCallDataHash`
|
|
260
|
+
- `PERMIT2_ADDRESS` and `ENTRY_POINT_V07` centralised in `constants.ts`
|
|
261
|
+
- `BATCH_EXECUTOR_ADDRESS_BASE_*` derived from `addresses.ts` — no duplicate hardcoding
|
|
1165
262
|
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
pnpm build # Compile TypeScript with tsup (ESM + CJS dual output)
|
|
1169
|
-
pnpm test # Run vitest test suite
|
|
1170
|
-
pnpm typecheck # Type-check without emitting (tsc --noEmit)
|
|
1171
|
-
```
|
|
1172
|
-
|
|
1173
|
-
From the monorepo root:
|
|
1174
|
-
|
|
1175
|
-
```bash
|
|
1176
|
-
pnpm build # Build all packages
|
|
1177
|
-
pnpm test # Test all packages
|
|
1178
|
-
pnpm typecheck # Type-check all packages
|
|
1179
|
-
```
|
|
1180
|
-
|
|
1181
|
-
### Output
|
|
1182
|
-
|
|
1183
|
-
`tsup` produces dual ESM + CJS output under `dist/`. Each sub-path export (`./eip712`, `./relay`, etc.) has its own entry in the bundle with full type declarations.
|
|
1184
|
-
|
|
1185
|
-
---
|
|
263
|
+
### 0.3.0-beta.10
|
|
264
|
+
- All Base mainnet contract addresses live and verified against deployed contracts
|
|
1186
265
|
|
|
1187
|
-
|
|
266
|
+
### 0.3.0-beta.8/9
|
|
267
|
+
- Full 4-scenario UserOp builders (mint, burn, swap, perp_deposit)
|
|
1188
268
|
|
|
1189
|
-
|
|
269
|
+
### 0.3.0-alpha.0
|
|
270
|
+
- Initial EIP-7702 BatchExecutor support, UserOp primitives
|