@aspect-wallet/sdk 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/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +708 -0
- package/dist/audit/history.d.ts +44 -0
- package/dist/audit/history.d.ts.map +1 -0
- package/dist/audit/history.js +80 -0
- package/dist/audit/history.js.map +1 -0
- package/dist/auth/email.d.ts +52 -0
- package/dist/auth/email.d.ts.map +1 -0
- package/dist/auth/email.js +66 -0
- package/dist/auth/email.js.map +1 -0
- package/dist/auth/oauth.d.ts +47 -0
- package/dist/auth/oauth.d.ts.map +1 -0
- package/dist/auth/oauth.js +103 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/auth/passkey-auth.d.ts +39 -0
- package/dist/auth/passkey-auth.d.ts.map +1 -0
- package/dist/auth/passkey-auth.js +108 -0
- package/dist/auth/passkey-auth.js.map +1 -0
- package/dist/auth/session.d.ts +30 -0
- package/dist/auth/session.d.ts.map +1 -0
- package/dist/auth/session.js +61 -0
- package/dist/auth/session.js.map +1 -0
- package/dist/chain/registry.d.ts +25 -0
- package/dist/chain/registry.d.ts.map +1 -0
- package/dist/chain/registry.js +46 -0
- package/dist/chain/registry.js.map +1 -0
- package/dist/core/client.d.ts +78 -0
- package/dist/core/client.d.ts.map +1 -0
- package/dist/core/client.js +129 -0
- package/dist/core/client.js.map +1 -0
- package/dist/core/config.d.ts +22 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +91 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/errors.d.ts +32 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +95 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/dist/mfa/guardian.d.ts +27 -0
- package/dist/mfa/guardian.d.ts.map +1 -0
- package/dist/mfa/guardian.js +37 -0
- package/dist/mfa/guardian.js.map +1 -0
- package/dist/mfa/multisig.d.ts +28 -0
- package/dist/mfa/multisig.d.ts.map +1 -0
- package/dist/mfa/multisig.js +40 -0
- package/dist/mfa/multisig.js.map +1 -0
- package/dist/mfa/tiers.d.ts +34 -0
- package/dist/mfa/tiers.d.ts.map +1 -0
- package/dist/mfa/tiers.js +66 -0
- package/dist/mfa/tiers.js.map +1 -0
- package/dist/mfa/timelock.d.ts +32 -0
- package/dist/mfa/timelock.d.ts.map +1 -0
- package/dist/mfa/timelock.js +47 -0
- package/dist/mfa/timelock.js.map +1 -0
- package/dist/recovery/devices.d.ts +31 -0
- package/dist/recovery/devices.d.ts.map +1 -0
- package/dist/recovery/devices.js +33 -0
- package/dist/recovery/devices.js.map +1 -0
- package/dist/recovery/export.d.ts +33 -0
- package/dist/recovery/export.d.ts.map +1 -0
- package/dist/recovery/export.js +44 -0
- package/dist/recovery/export.js.map +1 -0
- package/dist/recovery/rotation.d.ts +26 -0
- package/dist/recovery/rotation.d.ts.map +1 -0
- package/dist/recovery/rotation.js +31 -0
- package/dist/recovery/rotation.js.map +1 -0
- package/dist/recovery/social.d.ts +33 -0
- package/dist/recovery/social.d.ts.map +1 -0
- package/dist/recovery/social.js +36 -0
- package/dist/recovery/social.js.map +1 -0
- package/dist/security/freeze.d.ts +34 -0
- package/dist/security/freeze.d.ts.map +1 -0
- package/dist/security/freeze.js +42 -0
- package/dist/security/freeze.js.map +1 -0
- package/dist/security/revoke.d.ts +27 -0
- package/dist/security/revoke.d.ts.map +1 -0
- package/dist/security/revoke.js +27 -0
- package/dist/security/revoke.js.map +1 -0
- package/dist/security/watchtower.d.ts +34 -0
- package/dist/security/watchtower.d.ts.map +1 -0
- package/dist/security/watchtower.js +38 -0
- package/dist/security/watchtower.js.map +1 -0
- package/dist/session-keys/manager.d.ts +40 -0
- package/dist/session-keys/manager.d.ts.map +1 -0
- package/dist/session-keys/manager.js +65 -0
- package/dist/session-keys/manager.js.map +1 -0
- package/dist/session-keys/permissions.d.ts +44 -0
- package/dist/session-keys/permissions.d.ts.map +1 -0
- package/dist/session-keys/permissions.js +63 -0
- package/dist/session-keys/permissions.js.map +1 -0
- package/dist/session-keys/templates.d.ts +49 -0
- package/dist/session-keys/templates.d.ts.map +1 -0
- package/dist/session-keys/templates.js +65 -0
- package/dist/session-keys/templates.js.map +1 -0
- package/dist/signer/eoa.d.ts +24 -0
- package/dist/signer/eoa.d.ts.map +1 -0
- package/dist/signer/eoa.js +32 -0
- package/dist/signer/eoa.js.map +1 -0
- package/dist/signer/interface.d.ts +60 -0
- package/dist/signer/interface.d.ts.map +1 -0
- package/dist/signer/interface.js +47 -0
- package/dist/signer/interface.js.map +1 -0
- package/dist/signer/multisig.d.ts +38 -0
- package/dist/signer/multisig.d.ts.map +1 -0
- package/dist/signer/multisig.js +56 -0
- package/dist/signer/multisig.js.map +1 -0
- package/dist/signer/passkey.d.ts +35 -0
- package/dist/signer/passkey.d.ts.map +1 -0
- package/dist/signer/passkey.js +112 -0
- package/dist/signer/passkey.js.map +1 -0
- package/dist/signer/session.d.ts +24 -0
- package/dist/signer/session.d.ts.map +1 -0
- package/dist/signer/session.js +32 -0
- package/dist/signer/session.js.map +1 -0
- package/dist/sponsor/paymaster.d.ts +27 -0
- package/dist/sponsor/paymaster.d.ts.map +1 -0
- package/dist/sponsor/paymaster.js +43 -0
- package/dist/sponsor/paymaster.js.map +1 -0
- package/dist/transport/api.d.ts +25 -0
- package/dist/transport/api.d.ts.map +1 -0
- package/dist/transport/api.js +79 -0
- package/dist/transport/api.js.map +1 -0
- package/dist/transport/bundler.d.ts +52 -0
- package/dist/transport/bundler.d.ts.map +1 -0
- package/dist/transport/bundler.js +109 -0
- package/dist/transport/bundler.js.map +1 -0
- package/dist/transport/iframe.d.ts +33 -0
- package/dist/transport/iframe.d.ts.map +1 -0
- package/dist/transport/iframe.js +131 -0
- package/dist/transport/iframe.js.map +1 -0
- package/dist/types.d.ts +366 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/userop/builder.d.ts +75 -0
- package/dist/userop/builder.d.ts.map +1 -0
- package/dist/userop/builder.js +150 -0
- package/dist/userop/builder.js.map +1 -0
- package/dist/userop/encoding.d.ts +44 -0
- package/dist/userop/encoding.d.ts.map +1 -0
- package/dist/userop/encoding.js +99 -0
- package/dist/userop/encoding.js.map +1 -0
- package/dist/userop/gas.d.ts +35 -0
- package/dist/userop/gas.d.ts.map +1 -0
- package/dist/userop/gas.js +53 -0
- package/dist/userop/gas.js.map +1 -0
- package/dist/userop/hash.d.ts +34 -0
- package/dist/userop/hash.d.ts.map +1 -0
- package/dist/userop/hash.js +55 -0
- package/dist/userop/hash.js.map +1 -0
- package/dist/userop/nonce.d.ts +53 -0
- package/dist/userop/nonce.d.ts.map +1 -0
- package/dist/userop/nonce.js +79 -0
- package/dist/userop/nonce.js.map +1 -0
- package/dist/wallet/factory.d.ts +63 -0
- package/dist/wallet/factory.d.ts.map +1 -0
- package/dist/wallet/factory.js +63 -0
- package/dist/wallet/factory.js.map +1 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,708 @@
|
|
|
1
|
+
# @aspect-wallet/sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for building on the **Aspect Wallet** ERC-4337 Account Abstraction system. Create smart wallets, send gasless transactions, authenticate with social logins, and manage session keys -- all from your frontend.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
| Feature | Description |
|
|
8
|
+
|---------|-------------|
|
|
9
|
+
| **Smart Wallets** | Deterministic CREATE2 addresses, lazy deployment on first transaction |
|
|
10
|
+
| **Gasless Transactions** | VerifyingPaymaster sponsors gas -- users pay nothing |
|
|
11
|
+
| **Social Login** | Google, Apple, Facebook, Email OTP -- bridged to on-chain signers via Turnkey |
|
|
12
|
+
| **Passkeys** | WebAuthn/P256 biometric signing with RIP-7212 precompile support |
|
|
13
|
+
| **Session Keys** | Scoped ephemeral keys with allowlist, spend limits, time bounds |
|
|
14
|
+
| **Multi-Factor Auth** | On-chain enforced: device+guardian, K-of-N multi-sig, timelock |
|
|
15
|
+
| **Account Recovery** | Social recovery via guardians with timelock, two-step key rotation |
|
|
16
|
+
| **Emergency Freeze** | Instantly revoke all session keys and freeze account on compromise |
|
|
17
|
+
| **Audit Trail** | Full operation history, real-time events, webhook notifications |
|
|
18
|
+
| **Multi-Chain** | Arbitrum, Base, Polygon, Ethereum -- same address across chains |
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @aspect-wallet/sdk viem
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { FcxWalletClient, EoaSigner } from '@aspect-wallet/sdk';
|
|
30
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
31
|
+
|
|
32
|
+
const client = new FcxWalletClient({
|
|
33
|
+
chain: 'arbitrum-sepolia',
|
|
34
|
+
paymasterUrl: 'https://paymaster.yourapp.com',
|
|
35
|
+
bundlerUrl: 'https://bundler.yourapp.com/rpc',
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Get deterministic wallet address (no deployment needed yet)
|
|
39
|
+
const walletAddress = await client.getWalletAddress();
|
|
40
|
+
|
|
41
|
+
// Execute a transaction (deploys wallet on first tx if needed)
|
|
42
|
+
const result = await client.execute({
|
|
43
|
+
target: '0xContractAddress...',
|
|
44
|
+
value: 0n,
|
|
45
|
+
data: '0x...',
|
|
46
|
+
sponsored: true, // Paymaster pays gas
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
console.log('TX:', result.transactionHash);
|
|
50
|
+
console.log('Gas paid by user:', 0); // Gasless!
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Wallet Lifecycle
|
|
56
|
+
|
|
57
|
+
### Counterfactual Address
|
|
58
|
+
|
|
59
|
+
Users get a wallet address **instantly** -- no on-chain transaction needed. Assets can be sent to this address before the contract exists.
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { computeWalletAddress, WalletFactory } from '@aspect-wallet/sdk';
|
|
63
|
+
|
|
64
|
+
// Option A: Pure computation (no RPC call)
|
|
65
|
+
const address = computeWalletAddress({
|
|
66
|
+
factory: '0x1DFCc3c2ae138Da4BdF00DBF62CFf6f922D353EA',
|
|
67
|
+
owner: signerAddress,
|
|
68
|
+
salt: 0n,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Option B: Query factory contract
|
|
72
|
+
const factory = new WalletFactory(publicClient, factoryAddress);
|
|
73
|
+
const address = await factory.getAddress(signerAddress, 0n);
|
|
74
|
+
const isDeployed = await factory.isDeployed(address);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Lazy Deployment
|
|
78
|
+
|
|
79
|
+
The wallet deploys automatically on the first UserOp. The SDK handles `initCode` transparently:
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// First transaction: SDK includes initCode, factory deploys via CREATE2
|
|
83
|
+
const result = await client.execute({
|
|
84
|
+
target: recipientAddress,
|
|
85
|
+
value: parseEther('0.01'),
|
|
86
|
+
data: '0x',
|
|
87
|
+
});
|
|
88
|
+
// Wallet now exists on-chain at the same address
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## UserOperation Builder
|
|
94
|
+
|
|
95
|
+
### High-Level (Recommended)
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
// Single call
|
|
99
|
+
const result = await client.execute({
|
|
100
|
+
target: tokenContract,
|
|
101
|
+
value: 0n,
|
|
102
|
+
data: encodeFunctionData({ abi: erc20Abi, functionName: 'transfer', args: [to, amount] }),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Batch call (atomic: all succeed or all revert)
|
|
106
|
+
const result = await client.executeBatch([
|
|
107
|
+
{ target: tokenA, value: 0n, data: approveData },
|
|
108
|
+
{ target: dex, value: 0n, data: swapData },
|
|
109
|
+
]);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Low-Level (Advanced)
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { UserOpBuilder, getUserOpHash } from '@aspect-wallet/sdk';
|
|
116
|
+
|
|
117
|
+
const builder = new UserOpBuilder({ publicClient, entryPoint, factory });
|
|
118
|
+
|
|
119
|
+
// Step 1: Build
|
|
120
|
+
const userOp = await builder
|
|
121
|
+
.setSender(walletAddress)
|
|
122
|
+
.setCallData({ target, value, data })
|
|
123
|
+
.setNonce({ key: 0n })
|
|
124
|
+
.build();
|
|
125
|
+
|
|
126
|
+
// Step 2: Estimate gas (applies 130% safety margin)
|
|
127
|
+
const estimated = await builder.estimateGas(userOp);
|
|
128
|
+
|
|
129
|
+
// Step 3: Get sponsorship
|
|
130
|
+
const sponsored = await paymasterClient.approve(estimated);
|
|
131
|
+
|
|
132
|
+
// Step 4: Compute hash
|
|
133
|
+
const hash = getUserOpHash(sponsored, entryPointAddress, chainId);
|
|
134
|
+
|
|
135
|
+
// Step 5: Sign
|
|
136
|
+
const signature = await signer.sign(hash);
|
|
137
|
+
sponsored.signature = signature;
|
|
138
|
+
|
|
139
|
+
// Step 6: Submit
|
|
140
|
+
const opHash = await bundlerClient.sendUserOperation(sponsored);
|
|
141
|
+
|
|
142
|
+
// Step 7: Wait for receipt
|
|
143
|
+
const receipt = await bundlerClient.waitForReceipt(opHash, { timeout: 60_000 });
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Hash Computation
|
|
147
|
+
|
|
148
|
+
The SDK's `getUserOpHash()` matches `EntryPoint.getUserOpHash()` exactly (verified on-chain):
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
import { getUserOpHash } from '@aspect-wallet/sdk';
|
|
152
|
+
|
|
153
|
+
const hash = getUserOpHash(userOp, entryPointAddress, chainId);
|
|
154
|
+
// Includes chainId + entryPoint address for anti-replay
|
|
155
|
+
// Variable-length fields (initCode, callData, paymasterAndData) individually hashed
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Gas Sponsorship (Paymaster)
|
|
161
|
+
|
|
162
|
+
Users transact with **zero ETH**. The VerifyingPaymaster contract pays gas, authorized by your backend signing service.
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import { PaymasterClient } from '@aspect-wallet/sdk';
|
|
166
|
+
|
|
167
|
+
const paymaster = new PaymasterClient({
|
|
168
|
+
url: 'https://paymaster.yourapp.com',
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Request sponsorship approval
|
|
172
|
+
const approval = await paymaster.approve(userOp);
|
|
173
|
+
// approval.paymasterAndData = 97 bytes: address(20) + validUntil(6) + validAfter(6) + signature(65)
|
|
174
|
+
|
|
175
|
+
// Insert into UserOp
|
|
176
|
+
userOp.paymasterAndData = approval.paymasterAndData;
|
|
177
|
+
|
|
178
|
+
// Check remaining budget
|
|
179
|
+
const budget = await paymaster.getBudget();
|
|
180
|
+
// budget.daily.remaining = 3800000000000000000n (3.8 ETH)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Transparent Sponsorship
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
const result = await client.execute({
|
|
187
|
+
target: contractAddress,
|
|
188
|
+
value: 0n,
|
|
189
|
+
data: callData,
|
|
190
|
+
sponsored: true, // SDK handles paymaster flow automatically
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Authentication & Social Login
|
|
197
|
+
|
|
198
|
+
### Email OTP
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { AuthClient } from '@aspect-wallet/sdk';
|
|
202
|
+
|
|
203
|
+
const auth = new AuthClient({ url: 'https://auth.yourapp.com' });
|
|
204
|
+
|
|
205
|
+
// Send OTP
|
|
206
|
+
await auth.sendEmailOtp('user@example.com');
|
|
207
|
+
|
|
208
|
+
// Verify OTP → get session + wallet
|
|
209
|
+
const session = await auth.verifyEmailOtp({
|
|
210
|
+
email: 'user@example.com',
|
|
211
|
+
code: '847291',
|
|
212
|
+
});
|
|
213
|
+
// session.wallet.address = '0x...' (deterministic, instant)
|
|
214
|
+
// session.signer.address = '0x...' (Turnkey-generated secp256k1 key)
|
|
215
|
+
// session.jwt = 'eyJ...' (for subsequent API calls)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### OAuth (Google / Apple / Facebook)
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import { OAuthClient } from '@aspect-wallet/sdk';
|
|
222
|
+
|
|
223
|
+
const oauth = new OAuthClient({ url: 'https://auth.yourapp.com' });
|
|
224
|
+
|
|
225
|
+
// Google
|
|
226
|
+
const session = await oauth.loginWithGoogle();
|
|
227
|
+
|
|
228
|
+
// Apple
|
|
229
|
+
const session = await oauth.loginWithApple();
|
|
230
|
+
|
|
231
|
+
// Facebook
|
|
232
|
+
const session = await oauth.loginWithFacebook();
|
|
233
|
+
|
|
234
|
+
// All return the same SessionInfo structure
|
|
235
|
+
// First login: creates Turnkey sub-org + generates signing key
|
|
236
|
+
// Returning login: loads existing key
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Passkey Authentication
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
import { PasskeyAuthClient } from '@aspect-wallet/sdk';
|
|
243
|
+
|
|
244
|
+
const passkey = new PasskeyAuthClient({ url: 'https://auth.yourapp.com' });
|
|
245
|
+
|
|
246
|
+
// Register passkey (after initial login)
|
|
247
|
+
await passkey.register({ displayName: 'My MacBook' });
|
|
248
|
+
|
|
249
|
+
// Login with passkey (biometric prompt)
|
|
250
|
+
const session = await passkey.authenticate();
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Signing
|
|
256
|
+
|
|
257
|
+
### Signer Interface
|
|
258
|
+
|
|
259
|
+
All signers implement `ISigner`:
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
interface ISigner {
|
|
263
|
+
type: 'eoa' | 'passkey' | 'turnkey' | 'session-key' | 'multi-sig';
|
|
264
|
+
address: string;
|
|
265
|
+
sign(userOpHash: Hex): Promise<Hex>;
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### EOA Signer (Testing / Backend)
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
import { EoaSigner } from '@aspect-wallet/sdk';
|
|
273
|
+
|
|
274
|
+
const signer = new EoaSigner({ privateKey: '0x...' });
|
|
275
|
+
const signature = await signer.sign(userOpHash);
|
|
276
|
+
// 65-byte ECDSA signature with EIP-191 prefix
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Passkey Signer (Browser)
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
import { PasskeySigner } from '@aspect-wallet/sdk';
|
|
283
|
+
|
|
284
|
+
const signer = new PasskeySigner({ credentialId: '...' });
|
|
285
|
+
const signature = await signer.sign(userOpHash);
|
|
286
|
+
// WebAuthn assertion: authenticatorData + clientDataJSON + r + s
|
|
287
|
+
// P256 verified on-chain via RIP-7212 precompile (3,450 gas)
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Modular Signature Format
|
|
291
|
+
|
|
292
|
+
For modular accounts (ERC-6900), signatures include a module ID prefix:
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
import { encodeModuleSignature } from '@aspect-wallet/sdk';
|
|
296
|
+
|
|
297
|
+
const modularSig = encodeModuleSignature(0, ecdsaSignature);
|
|
298
|
+
// moduleId(4 bytes) + signature = routes to correct on-chain validator
|
|
299
|
+
// 0 = SingleSigner, 1 = SessionKey, 2 = DeviceGuardian, 3 = MultiSig, 4 = WebAuthn
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Session Keys
|
|
305
|
+
|
|
306
|
+
Temporary scoped keys that let dApps execute transactions **without user interaction** per action, while strictly limiting what the key can do.
|
|
307
|
+
|
|
308
|
+
### Create Session Key
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
import { SessionKeyManager, SessionKeyTemplates } from '@aspect-wallet/sdk';
|
|
312
|
+
|
|
313
|
+
const manager = new SessionKeyManager(client);
|
|
314
|
+
|
|
315
|
+
// Custom permissions
|
|
316
|
+
const session = await manager.create({
|
|
317
|
+
permissions: {
|
|
318
|
+
allowlist: [
|
|
319
|
+
{ target: gameContract, selector: '0x12345678' }, // playMove()
|
|
320
|
+
{ target: gameContract, selector: '0xabcdef01' }, // claimReward()
|
|
321
|
+
],
|
|
322
|
+
spendLimit: { perTx: 0n, cumulative: 0n },
|
|
323
|
+
timeRange: {
|
|
324
|
+
validAfter: Math.floor(Date.now() / 1000),
|
|
325
|
+
validUntil: Math.floor(Date.now() / 1000) + 4 * 3600, // 4 hours
|
|
326
|
+
},
|
|
327
|
+
requiredPaymaster: paymasterAddress,
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// Or use pre-built templates
|
|
332
|
+
const gaming = SessionKeyTemplates.gaming({
|
|
333
|
+
gameContract: '0x...',
|
|
334
|
+
duration: 4 * 3600,
|
|
335
|
+
paymaster: '0x...',
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
const defi = SessionKeyTemplates.defi({
|
|
339
|
+
dexRouter: '0x...',
|
|
340
|
+
lendingPool: '0x...',
|
|
341
|
+
spendLimit: parseEther('1'),
|
|
342
|
+
duration: 3600,
|
|
343
|
+
});
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Execute with Session Key
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
// No user interaction needed -- session key signs automatically
|
|
350
|
+
const result = await manager.execute(session.moduleId, {
|
|
351
|
+
target: gameContract,
|
|
352
|
+
value: 0n,
|
|
353
|
+
data: playMoveData,
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// On-chain hooks enforce:
|
|
357
|
+
// - AllowlistModule: target + selector in whitelist
|
|
358
|
+
// - NativeTokenLimitModule: value within limits
|
|
359
|
+
// - TimeRangeModule: within valid time window
|
|
360
|
+
// - PaymasterGuardModule: using required paymaster
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Revoke
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
await manager.revoke(session.moduleId); // Immediate on-chain revocation
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## Multi-Factor Authentication
|
|
372
|
+
|
|
373
|
+
On-chain enforced MFA -- the blockchain rejects transactions that don't meet the multi-factor requirements.
|
|
374
|
+
|
|
375
|
+
### 4-Tier Step-Up Auth
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
import { TierSelector } from '@aspect-wallet/sdk';
|
|
379
|
+
|
|
380
|
+
// SDK auto-selects tier based on value + target
|
|
381
|
+
const result = await client.execute({
|
|
382
|
+
target: tokenContract,
|
|
383
|
+
value: parseEther('5'), // High value → Tier 3 (device + guardian)
|
|
384
|
+
data: transferData,
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
// Force specific tier
|
|
388
|
+
const result = await client.execute({
|
|
389
|
+
target: tokenContract,
|
|
390
|
+
value: parseEther('0.01'),
|
|
391
|
+
data: transferData,
|
|
392
|
+
authTier: 3, // Force device + guardian
|
|
393
|
+
});
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
| Tier | When | Signers | Latency |
|
|
397
|
+
|------|------|---------|---------|
|
|
398
|
+
| 1 | Low-value, scoped actions | Session key (auto-sign) | Instant |
|
|
399
|
+
| 2 | Normal transactions | Owner key | 1 biometric prompt |
|
|
400
|
+
| 3 | High-value transfers | Owner + Guardian | 2 approvals |
|
|
401
|
+
| 4 | Account management | 2-of-3 multi-sig + 24h timelock | Hours/days |
|
|
402
|
+
|
|
403
|
+
### Guardian Management
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
import { GuardianManager } from '@aspect-wallet/sdk';
|
|
407
|
+
|
|
408
|
+
const guardians = new GuardianManager(client);
|
|
409
|
+
|
|
410
|
+
await guardians.add({ guardian: '0x...', label: 'Mom' });
|
|
411
|
+
await guardians.add({ guardian: '0x...', label: 'Backend Co-Signer' });
|
|
412
|
+
await guardians.setThreshold(2); // 2-of-3 required for recovery
|
|
413
|
+
|
|
414
|
+
const list = await guardians.list(walletAddress);
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### Multi-Sig Operations
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
import { MultiSigManager } from '@aspect-wallet/sdk';
|
|
421
|
+
|
|
422
|
+
const multisig = new MultiSigManager(client);
|
|
423
|
+
|
|
424
|
+
// Propose (owner 1)
|
|
425
|
+
const proposal = await multisig.propose({
|
|
426
|
+
target: tokenContract,
|
|
427
|
+
value: 0n,
|
|
428
|
+
data: transferData,
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
// Approve (owner 2)
|
|
432
|
+
await multisig.approve(proposal.id);
|
|
433
|
+
// If threshold met → UserOp submitted automatically
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## Account Recovery
|
|
439
|
+
|
|
440
|
+
### Social Recovery (Guardian-Based)
|
|
441
|
+
|
|
442
|
+
```typescript
|
|
443
|
+
import { SocialRecovery } from '@aspect-wallet/sdk';
|
|
444
|
+
|
|
445
|
+
const recovery = new SocialRecovery(client);
|
|
446
|
+
|
|
447
|
+
// Guardian A initiates
|
|
448
|
+
await recovery.initiate({
|
|
449
|
+
account: userWallet,
|
|
450
|
+
newOwner: newKeyAddress,
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// Guardian B approves
|
|
454
|
+
await recovery.approve({ account: userWallet });
|
|
455
|
+
|
|
456
|
+
// After timelock (e.g., 3 days) → execute
|
|
457
|
+
await recovery.execute({ account: userWallet });
|
|
458
|
+
|
|
459
|
+
// Owner can cancel during timelock
|
|
460
|
+
await recovery.cancel();
|
|
461
|
+
|
|
462
|
+
// Check status
|
|
463
|
+
const status = await recovery.getStatus(walletAddress);
|
|
464
|
+
// { pending: true, approvals: 2, threshold: 2, executeAfter: ..., canExecute: false }
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Key Rotation (Two-Step)
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
import { KeyRotation } from '@aspect-wallet/sdk';
|
|
471
|
+
|
|
472
|
+
const rotation = new KeyRotation(client);
|
|
473
|
+
|
|
474
|
+
// Step 1: Current owner initiates
|
|
475
|
+
await rotation.initiate({ newOwner: newSignerAddress });
|
|
476
|
+
|
|
477
|
+
// Step 2: New owner accepts
|
|
478
|
+
await rotation.accept();
|
|
479
|
+
// Old key immediately loses access. Account address unchanged.
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### Multi-Device
|
|
483
|
+
|
|
484
|
+
```typescript
|
|
485
|
+
import { DeviceManager } from '@aspect-wallet/sdk';
|
|
486
|
+
|
|
487
|
+
const devices = new DeviceManager(client);
|
|
488
|
+
|
|
489
|
+
await devices.add({ name: 'iPhone 15', type: 'passkey', moduleId: 2 });
|
|
490
|
+
await devices.remove(moduleId); // Remove lost device
|
|
491
|
+
|
|
492
|
+
const list = await devices.list(walletAddress);
|
|
493
|
+
// [{ moduleId: 0, name: 'MacBook', type: 'turnkey', active: true }, ...]
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
---
|
|
497
|
+
|
|
498
|
+
## Security & Emergency
|
|
499
|
+
|
|
500
|
+
### Emergency Freeze
|
|
501
|
+
|
|
502
|
+
```typescript
|
|
503
|
+
import { FreezeManager } from '@aspect-wallet/sdk';
|
|
504
|
+
|
|
505
|
+
const freeze = new FreezeManager(client);
|
|
506
|
+
|
|
507
|
+
// Freeze account (revokes all session keys, suspends paymaster)
|
|
508
|
+
await freeze.freeze({ reason: 'suspicious_activity' });
|
|
509
|
+
|
|
510
|
+
// Unfreeze (requires MFA)
|
|
511
|
+
await freeze.unfreeze();
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### Bulk Session Key Revocation
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
import { RevokeManager } from '@aspect-wallet/sdk';
|
|
518
|
+
|
|
519
|
+
const revoke = new RevokeManager(client);
|
|
520
|
+
await revoke.revokeAll(); // Revoke ALL session keys at once
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### Watchtower Alerts
|
|
524
|
+
|
|
525
|
+
```typescript
|
|
526
|
+
import { WatchtowerClient } from '@aspect-wallet/sdk';
|
|
527
|
+
|
|
528
|
+
const watchtower = new WatchtowerClient(client);
|
|
529
|
+
|
|
530
|
+
await watchtower.subscribe({
|
|
531
|
+
account: walletAddress,
|
|
532
|
+
alerts: ['recovery_initiated', 'owner_changed', 'large_transfer'],
|
|
533
|
+
channels: {
|
|
534
|
+
email: 'user@example.com',
|
|
535
|
+
webhook: 'https://myapp.com/webhooks/security',
|
|
536
|
+
},
|
|
537
|
+
});
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
---
|
|
541
|
+
|
|
542
|
+
## Audit & Logging
|
|
543
|
+
|
|
544
|
+
```typescript
|
|
545
|
+
import { AuditClient } from '@aspect-wallet/sdk';
|
|
546
|
+
|
|
547
|
+
const audit = new AuditClient(client);
|
|
548
|
+
|
|
549
|
+
// Operation history
|
|
550
|
+
const history = await audit.getHistory(walletAddress, {
|
|
551
|
+
limit: 50,
|
|
552
|
+
types: ['execute', 'deploy', 'module_install'],
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
// Each operation includes:
|
|
556
|
+
// - userOpHash, transactionHash
|
|
557
|
+
// - target, value, selector
|
|
558
|
+
// - signer address + type (turnkey/passkey/session-key/multi-sig)
|
|
559
|
+
// - sponsored (boolean), paymaster address
|
|
560
|
+
// - gasCost, success, blockNumber, timestamp
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
## Chain Configuration
|
|
566
|
+
|
|
567
|
+
```typescript
|
|
568
|
+
import { ChainRegistry } from '@aspect-wallet/sdk';
|
|
569
|
+
|
|
570
|
+
const chains = new ChainRegistry();
|
|
571
|
+
|
|
572
|
+
// Get chain config
|
|
573
|
+
const config = chains.get('arbitrum-sepolia');
|
|
574
|
+
// { chainId: 421614, entryPoint: '0x5FF1...', factory: '0x1DFC...', paymaster: '0x13e6...', ... }
|
|
575
|
+
|
|
576
|
+
// Switch chains
|
|
577
|
+
client.switchChain('base');
|
|
578
|
+
|
|
579
|
+
// Same address across chains (CREATE2 determinism)
|
|
580
|
+
const arbAddr = computeWalletAddress({ factory: arbFactory, owner, salt: 0n });
|
|
581
|
+
const baseAddr = computeWalletAddress({ factory: baseFactory, owner, salt: 0n });
|
|
582
|
+
// arbAddr === baseAddr (if factories deployed at same address)
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
---
|
|
586
|
+
|
|
587
|
+
## Bundler Client
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
import { BundlerClient } from '@aspect-wallet/sdk';
|
|
591
|
+
|
|
592
|
+
const bundler = new BundlerClient({ url: 'https://bundler.yourapp.com/rpc' });
|
|
593
|
+
|
|
594
|
+
// Submit UserOp
|
|
595
|
+
const hash = await bundler.sendUserOperation(signedUserOp, entryPointAddress);
|
|
596
|
+
|
|
597
|
+
// Wait for receipt (exponential backoff)
|
|
598
|
+
const receipt = await bundler.waitForReceipt(hash, {
|
|
599
|
+
timeout: 60_000,
|
|
600
|
+
pollingInterval: 2_000,
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
// Gas estimation
|
|
604
|
+
const gas = await bundler.estimateUserOperationGas(userOp, entryPointAddress);
|
|
605
|
+
// { preVerificationGas, verificationGasLimit, callGasLimit }
|
|
606
|
+
|
|
607
|
+
// Check supported entry points
|
|
608
|
+
const entryPoints = await bundler.supportedEntryPoints();
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
## Error Handling
|
|
614
|
+
|
|
615
|
+
All errors are typed via `FcxError`:
|
|
616
|
+
|
|
617
|
+
```typescript
|
|
618
|
+
import { FcxError } from '@aspect-wallet/sdk';
|
|
619
|
+
|
|
620
|
+
try {
|
|
621
|
+
await client.execute({ ... });
|
|
622
|
+
} catch (e) {
|
|
623
|
+
if (e instanceof FcxError) {
|
|
624
|
+
switch (e.code) {
|
|
625
|
+
case 'USEROP_SIGNATURE_INVALID': // -32507
|
|
626
|
+
case 'USEROP_SIMULATION_FAILED': // -32500
|
|
627
|
+
case 'SPONSOR_BUDGET_EXHAUSTED': // Budget exceeded
|
|
628
|
+
case 'SESSION_KEY_EXPIRED': // Session key time range expired
|
|
629
|
+
case 'SESSION_KEY_NOT_ALLOWED': // Target/selector not in allowlist
|
|
630
|
+
case 'MFA_THRESHOLD_NOT_MET': // Not enough signatures
|
|
631
|
+
case 'AUTH_SESSION_EXPIRED': // JWT expired
|
|
632
|
+
// Handle specific error
|
|
633
|
+
break;
|
|
634
|
+
}
|
|
635
|
+
console.error(e.code, e.message, e.details);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
---
|
|
641
|
+
|
|
642
|
+
## Deployed Contracts
|
|
643
|
+
|
|
644
|
+
### Arbitrum Sepolia (Testnet)
|
|
645
|
+
|
|
646
|
+
| Contract | Address |
|
|
647
|
+
|----------|---------|
|
|
648
|
+
| EntryPoint v0.6 | `0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789` |
|
|
649
|
+
| SimpleAccountFactory | `0x1DFCc3c2ae138Da4BdF00DBF62CFf6f922D353EA` |
|
|
650
|
+
| SimpleAccount (impl) | `0xe6FEe0AF343A9c59a03070Ef4239519bD6C243F2` |
|
|
651
|
+
| VerifyingPaymaster | `0x13e6e887168d5ca78a47fac4EF71Ec5d0C6a9d2B` |
|
|
652
|
+
|
|
653
|
+
---
|
|
654
|
+
|
|
655
|
+
## Architecture
|
|
656
|
+
|
|
657
|
+
```
|
|
658
|
+
Your Frontend App
|
|
659
|
+
│
|
|
660
|
+
▼
|
|
661
|
+
@aspect-wallet/sdk (this package)
|
|
662
|
+
│
|
|
663
|
+
├── Bundler RPC ──────── eth_sendUserOperation ──► EntryPoint (on-chain)
|
|
664
|
+
│ │
|
|
665
|
+
├── Paymaster Service ── POST /api/paymaster/sign ├── Smart Account (proxy)
|
|
666
|
+
│ ├── Factory (CREATE2)
|
|
667
|
+
├── Auth Server ──────── POST /auth/email/verify ├── Paymaster (verifying)
|
|
668
|
+
│ POST /auth/oauth/google └── Validation Modules
|
|
669
|
+
│ ├── SingleSigner
|
|
670
|
+
└── Turnkey Enclave ─── Key generation + signing ├── WebAuthn (P256)
|
|
671
|
+
(AWS Nitro) via cross-origin iframe ├── MultiSig (K-of-N)
|
|
672
|
+
├── SessionKey + hooks
|
|
673
|
+
└── SocialRecovery
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
---
|
|
677
|
+
|
|
678
|
+
## TypeScript Types
|
|
679
|
+
|
|
680
|
+
All types are exported and available for your application:
|
|
681
|
+
|
|
682
|
+
```typescript
|
|
683
|
+
import type {
|
|
684
|
+
UserOperation,
|
|
685
|
+
ExecutionResult,
|
|
686
|
+
SessionInfo,
|
|
687
|
+
WalletState,
|
|
688
|
+
SessionKeyPermissions,
|
|
689
|
+
PaymasterApproval,
|
|
690
|
+
RecoveryStatus,
|
|
691
|
+
AuditOperation,
|
|
692
|
+
} from '@aspect-wallet/sdk';
|
|
693
|
+
|
|
694
|
+
// Or import types separately
|
|
695
|
+
import type { UserOperation } from '@aspect-wallet/sdk/types';
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
---
|
|
699
|
+
|
|
700
|
+
## Requirements
|
|
701
|
+
|
|
702
|
+
- Node.js >= 18
|
|
703
|
+
- `viem` >= 2.0.0 (peer dependency)
|
|
704
|
+
- EVM chain with ERC-4337 EntryPoint v0.6 deployed
|
|
705
|
+
|
|
706
|
+
## License
|
|
707
|
+
|
|
708
|
+
MIT
|