agentwallet-sdk 2.4.1 → 2.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.
Files changed (41) hide show
  1. package/README.md +46 -19
  2. package/dist/chains.d.ts +37 -0
  3. package/dist/chains.d.ts.map +1 -0
  4. package/dist/chains.js +75 -0
  5. package/dist/chains.js.map +1 -0
  6. package/dist/index.d.ts +4 -499
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +4 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/swap/SwapModule.d.ts +4 -1
  11. package/dist/swap/SwapModule.d.ts.map +1 -1
  12. package/dist/swap/SwapModule.js +7 -2
  13. package/dist/swap/SwapModule.js.map +1 -1
  14. package/dist/swap/types.d.ts +4 -2
  15. package/dist/swap/types.d.ts.map +1 -1
  16. package/dist/swap/types.js.map +1 -1
  17. package/dist/types.d.ts +2 -1
  18. package/dist/types.d.ts.map +1 -1
  19. package/dist/types.js +1 -0
  20. package/dist/types.js.map +1 -1
  21. package/dist/x402/types.d.ts +2 -2
  22. package/dist/x402/types.d.ts.map +1 -1
  23. package/dist/x402/types.js +6 -2
  24. package/dist/x402/types.js.map +1 -1
  25. package/package.json +27 -12
  26. package/dist/bridge/__tests__/bridge.test.d.ts +0 -2
  27. package/dist/bridge/__tests__/bridge.test.d.ts.map +0 -1
  28. package/dist/bridge/__tests__/bridge.test.js +0 -508
  29. package/dist/bridge/__tests__/bridge.test.js.map +0 -1
  30. package/dist/swap/__tests__/swap.test.d.ts +0 -2
  31. package/dist/swap/__tests__/swap.test.d.ts.map +0 -1
  32. package/dist/swap/__tests__/swap.test.js +0 -272
  33. package/dist/swap/__tests__/swap.test.js.map +0 -1
  34. package/dist/x402/__tests__/budget.test.d.ts +0 -2
  35. package/dist/x402/__tests__/budget.test.d.ts.map +0 -1
  36. package/dist/x402/__tests__/budget.test.js +0 -114
  37. package/dist/x402/__tests__/budget.test.js.map +0 -1
  38. package/dist/x402/__tests__/client.test.d.ts +0 -2
  39. package/dist/x402/__tests__/client.test.d.ts.map +0 -1
  40. package/dist/x402/__tests__/client.test.js +0 -107
  41. package/dist/x402/__tests__/client.test.js.map +0 -1
package/README.md CHANGED
@@ -4,11 +4,24 @@
4
4
 
5
5
  Agent Wallet gives AI agents autonomous spending power with hard on-chain limits. No more choosing between "agent can drain everything" and "every transaction needs manual approval."
6
6
 
7
- ```
7
+ ```text
8
8
  Agent wants to spend $15 → ✅ Auto-approved (under $25 limit)
9
9
  Agent wants to spend $500 → ⏳ Queued for your approval
10
10
  Agent spent $490 today → 🛑 Next tx queued ($500/day limit hit)
11
- ```
11
+ ```text
12
+
13
+ ## How We Compare
14
+
15
+ | | **agentwallet-sdk** | **Coinbase Agentic Wallet** | **MoonPay Agents** |
16
+ |---|---|---|---|
17
+ | **Custody** | Non-custodial (keys on device) | Semi-custodial (TEE) | Non-custodial (claimed) |
18
+ | **Spend Limits** | On-chain (smart contract) | API-enforced | Not documented |
19
+ | **Chains** | 5 (Base, ETH, Arb, Polygon, Sepolia) | Base only | Unclear |
20
+ | **Agent Identity** | ERC-8004 + ERC-6551 | None | None |
21
+ | **Open Source** | MIT | Partial | Closed |
22
+ | **x402 Payments** | Native | Supported | "Compatible" |
23
+
24
+ > On-chain spend limits can't be bypassed even if the API layer is compromised. That's the difference between policy and math.
12
25
 
13
26
  ## Why Agent Wallet?
14
27
 
@@ -25,7 +38,7 @@ Built on **ERC-6551** (token-bound accounts). Your agent's wallet is tied to an
25
38
 
26
39
  ```bash
27
40
  npm install @agentwallet/sdk viem
28
- ```
41
+ ```text
29
42
 
30
43
  ```typescript
31
44
  import {
@@ -76,11 +89,12 @@ for (const tx of pending) {
76
89
  console.log(`Pending #${tx.txId}: ${tx.amount} to ${tx.to}`);
77
90
  await approveTransaction(wallet, tx.txId);
78
91
  }
79
- ```
92
+ ```text
80
93
 
81
94
  ## API Reference
82
95
 
83
96
  ### `createWallet(config)`
97
+
84
98
  Connect to an existing AgentAccountV2 contract.
85
99
 
86
100
  | Param | Type | Description |
@@ -91,6 +105,7 @@ Connect to an existing AgentAccountV2 contract.
91
105
  | `rpcUrl?` | `string` | Custom RPC URL |
92
106
 
93
107
  ### `setSpendPolicy(wallet, policy)` — Owner only
108
+
94
109
  Set per-token spending limits.
95
110
 
96
111
  | Field | Type | Description |
@@ -101,31 +116,39 @@ Set per-token spending limits.
101
116
  | `periodLength` | `number` | Window in seconds (default: 86400 = 24h) |
102
117
 
103
118
  ### `agentExecute(wallet, { to, value?, data? })`
119
+
104
120
  Execute a native ETH transaction. Auto-approves if within limits, queues if over.
105
121
 
106
122
  **Returns:** `{ executed: boolean, txHash: Hash, pendingTxId?: bigint }`
107
123
 
108
124
  ### `agentTransferToken(wallet, { token, to, amount })`
125
+
109
126
  Transfer ERC20 tokens, respecting spend limits.
110
127
 
111
128
  ### `checkBudget(wallet, token?)`
129
+
112
130
  Check remaining autonomous spending budget.
113
131
 
114
132
  **Returns:** `{ token, perTxLimit, remainingInPeriod }`
115
133
 
116
134
  ### `getPendingApprovals(wallet, fromId?, toId?)`
135
+
117
136
  List all pending (unexecuted, uncancelled) transactions awaiting owner approval.
118
137
 
119
138
  ### `approveTransaction(wallet, txId)` — Owner only
139
+
120
140
  Approve and execute a queued transaction.
121
141
 
122
142
  ### `cancelTransaction(wallet, txId)` — Owner only
143
+
123
144
  Cancel a queued transaction.
124
145
 
125
146
  ### `setOperator(wallet, operator, authorized)` — Owner only
147
+
126
148
  Add or remove an agent operator address.
127
149
 
128
150
  ### `getBudgetForecast(wallet, token?, now?)`
151
+
129
152
  **[MAX-ADDED]** Time-aware budget forecast — know not just what's left, but when it refills.
130
153
 
131
154
  **Returns:** `BudgetForecast` — includes `remainingInPeriod`, `secondsUntilReset`, `utilizationPercent`, full period metadata.
@@ -133,9 +156,10 @@ Add or remove an agent operator address.
133
156
  ```typescript
134
157
  const forecast = await getBudgetForecast(wallet, NATIVE_TOKEN);
135
158
  console.log(`${forecast.utilizationPercent}% used, resets in ${forecast.secondsUntilReset}s`);
136
- ```
159
+ ```text
137
160
 
138
161
  ### `getWalletHealth(wallet, operators?, tokens?, now?)`
162
+
139
163
  **[MAX-ADDED]** Single-call diagnostic snapshot for agent self-monitoring.
140
164
 
141
165
  **Returns:** `WalletHealth` — address, NFT binding, operator epoch, active operator statuses, pending queue depth, budget forecasts.
@@ -144,9 +168,10 @@ console.log(`${forecast.utilizationPercent}% used, resets in ${forecast.secondsU
144
168
  const health = await getWalletHealth(wallet, [agentHotWallet], [NATIVE_TOKEN, usdcAddress]);
145
169
  if (health.pendingQueueDepth > 5) console.warn('Queue backing up!');
146
170
  if (!health.activeOperators[0].active) console.error('Agent operator deactivated!');
147
- ```
171
+ ```text
148
172
 
149
173
  ### `batchAgentTransfer(wallet, transfers)`
174
+
150
175
  **[MAX-ADDED]** Execute multiple token transfers sequentially — reduces boilerplate for multi-recipient payments.
151
176
 
152
177
  ```typescript
@@ -154,9 +179,10 @@ const hashes = await batchAgentTransfer(wallet, [
154
179
  { token: USDC, to: serviceA, amount: 100n },
155
180
  { token: USDC, to: serviceB, amount: 200n },
156
181
  ]);
157
- ```
182
+ ```text
158
183
 
159
184
  ### `getActivityHistory(wallet, { fromBlock?, toBlock? })`
185
+
160
186
  **[MAX-ADDED]** Query on-chain event history for self-auditing — no external indexer needed.
161
187
 
162
188
  **Returns:** `ActivityEntry[]` — sorted by block number, covers executions, queued txs, approvals, cancellations, policy updates, operator changes.
@@ -166,7 +192,7 @@ const history = await getActivityHistory(wallet, { fromBlock: 10000n });
166
192
  for (const entry of history) {
167
193
  console.log(`[${entry.type}] block ${entry.blockNumber}: ${JSON.stringify(entry.args)}`);
168
194
  }
169
- ```
195
+ ```text
170
196
 
171
197
  ## Supported Chains
172
198
 
@@ -207,7 +233,7 @@ const data = await response.json();
207
233
  // - Checked your budget (client-side + on-chain)
208
234
  // - Paid USDC via your AgentWallet contract
209
235
  // - Retried the request with payment proof
210
- ```
236
+ ```text
211
237
 
212
238
  ### Drop-in Fetch Replacement
213
239
 
@@ -218,7 +244,7 @@ const x402Fetch = createX402Fetch(wallet, { globalDailyLimit: 100_000_000n });
218
244
 
219
245
  // Use exactly like fetch()
220
246
  const res = await x402Fetch('https://any-x402-api.com/endpoint');
221
- ```
247
+ ```text
222
248
 
223
249
  ### Budget Controls
224
250
 
@@ -236,7 +262,7 @@ client.budgetTracker.setServiceBudget({
236
262
  maxPerRequest: 2_000_000n,
237
263
  dailyLimit: 20_000_000n,
238
264
  });
239
- ```
265
+ ```text
240
266
 
241
267
  ### Payment Approval Callback
242
268
 
@@ -250,11 +276,11 @@ const client = createX402Client(wallet, {
250
276
  console.log(`Paid ${log.amount} via tx ${log.txHash}`);
251
277
  },
252
278
  });
253
- ```
279
+ ```text
254
280
 
255
281
  ### How x402 Works
256
282
 
257
- ```
283
+ ```text
258
284
  Agent → GET /api/data → Server returns 402 + PAYMENT-REQUIRED header
259
285
 
260
286
  Client parses payment requirements (amount, token, recipient, network)
@@ -266,7 +292,7 @@ AgentWallet executes USDC transfer on Base
266
292
  Client retries request with X-PAYMENT header (payment proof)
267
293
 
268
294
  Server verifies payment → returns 200 + data
269
- ```
295
+ ```text
270
296
 
271
297
  Your agent's keys never leave the non-custodial wallet. All payments respect on-chain spend limits set by the wallet owner.
272
298
 
@@ -275,6 +301,7 @@ Your agent's keys never leave the non-custodial wallet. All payments respect on-
275
301
  Give your AI agent a portable, censorship-resistant identity on Ethereum via [ERC-8004 Trustless Agents](https://eips.ethereum.org/EIPS/eip-8004).
276
302
 
277
303
  ERC-8004 provides three things:
304
+
278
305
  - **Identity Registry** — ERC-721 NFT that resolves to an agent's registration file (name, description, services, capabilities)
279
306
  - **Reputation Registry** — On-chain feedback signals (composable scoring)
280
307
  - **Validation Registry** — Hooks for stakers, zkML verifiers, and TEE oracles
@@ -324,21 +351,21 @@ console.log(agentData.owner); // NFT owner address
324
351
  // Validate a registration file before publishing
325
352
  const errors = validateRegistrationFile(agentData.registrationFile!);
326
353
  if (errors.length === 0) console.log('Valid ERC-8004 registration ✅');
327
- ```
354
+ ```text
328
355
 
329
356
  ### Agent Registry Identifier
330
357
 
331
358
  Each agent is globally identified by a namespaced string:
332
359
 
333
- ```
360
+ ```text
334
361
  eip155:8453:0xRegistryAddress ← namespace:chainId:contractAddress
335
- ```
362
+ ```text
336
363
 
337
364
  ```typescript
338
365
  import { formatAgentRegistry } from '@agentwallet/sdk';
339
366
  const id = formatAgentRegistry(8453, '0xYOUR_REGISTRY');
340
367
  // → "eip155:8453:0xYOUR_REGISTRY"
341
- ```
368
+ ```text
342
369
 
343
370
  ### Fully On-Chain Storage (No IPFS Required)
344
371
 
@@ -351,7 +378,7 @@ const uri = buildDataURI({ name: 'MyAgent', description: '...', type: '...' });
351
378
 
352
379
  // Decode it back
353
380
  const file = parseDataURI(uri);
354
- ```
381
+ ```text
355
382
 
356
383
  ---
357
384
 
@@ -0,0 +1,37 @@
1
+ import type { Address } from 'viem';
2
+ /** Chains supported for swap + x402 + wallet operations */
3
+ export type SupportedChain = 'base' | 'arbitrum' | 'optimism';
4
+ export interface ChainConfig {
5
+ chainId: number;
6
+ name: string;
7
+ /** Public default RPC URL (no API key required) */
8
+ rpcUrl: string;
9
+ /** USDC token contract address (official Circle deployment) */
10
+ usdc: Address;
11
+ /** Uniswap V3 SwapRouter02 address */
12
+ uniswapRouter?: Address;
13
+ /** Uniswap V3 QuoterV2 address */
14
+ uniswapQuoter?: Address;
15
+ /** Wrapped native token address */
16
+ weth?: Address;
17
+ /** x402 network identifier (e.g. "base:8453") */
18
+ x402Network: string;
19
+ }
20
+ /**
21
+ * Chain configuration for all supported chains.
22
+ *
23
+ * Uniswap V3 addresses verified against:
24
+ * https://docs.uniswap.org/contracts/v3/reference/deployments/
25
+ *
26
+ * USDC addresses verified against:
27
+ * https://www.circle.com/en/multichain-usdc
28
+ *
29
+ * WETH on Base / Optimism is the canonical OP-stack bridged WETH at 0x4200...0006
30
+ * Arbitrum WETH is the standard bridged WETH at 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1
31
+ */
32
+ export declare const CHAIN_CONFIG: Record<SupportedChain, ChainConfig>;
33
+ /** Get chain config — throws a clear error if chain is unknown */
34
+ export declare function getChainConfig(chain: SupportedChain): ChainConfig;
35
+ /** Resolve a chain from chainId — returns undefined if not found */
36
+ export declare function chainFromId(chainId: number): SupportedChain | undefined;
37
+ //# sourceMappingURL=chains.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chains.d.ts","sourceRoot":"","sources":["../src/chains.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC,2DAA2D;AAC3D,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,CAAC;AAE9D,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,IAAI,EAAE,OAAO,CAAC;IACd,sCAAsC;IACtC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kCAAkC;IAClC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mCAAmC;IACnC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,iDAAiD;IACjD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,cAAc,EAAE,WAAW,CA2CnD,CAAC;AAEX,kEAAkE;AAClE,wBAAgB,cAAc,CAAC,KAAK,EAAE,cAAc,GAAG,WAAW,CAQjE;AAED,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAKvE"}
package/dist/chains.js ADDED
@@ -0,0 +1,75 @@
1
+ // [MAX-ADDED] Shared chain configuration — single source of truth for multi-chain support.
2
+ // Chains: Base, Arbitrum, Optimism (Ethereum is bridge-only, no swap support due to gas).
3
+ /**
4
+ * Chain configuration for all supported chains.
5
+ *
6
+ * Uniswap V3 addresses verified against:
7
+ * https://docs.uniswap.org/contracts/v3/reference/deployments/
8
+ *
9
+ * USDC addresses verified against:
10
+ * https://www.circle.com/en/multichain-usdc
11
+ *
12
+ * WETH on Base / Optimism is the canonical OP-stack bridged WETH at 0x4200...0006
13
+ * Arbitrum WETH is the standard bridged WETH at 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1
14
+ */
15
+ export const CHAIN_CONFIG = {
16
+ base: {
17
+ chainId: 8453,
18
+ name: 'Base',
19
+ rpcUrl: 'https://mainnet.base.org',
20
+ // Official USDC on Base (Circle native deployment)
21
+ usdc: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
22
+ // Uniswap V3 on Base — SwapRouter02 + QuoterV2
23
+ // Source: https://docs.uniswap.org/contracts/v3/reference/deployments/base-deployments
24
+ uniswapRouter: '0x2626664c2603336E57B271c5C0b26F421741e481',
25
+ uniswapQuoter: '0x3d4e44Eb1374240CE5F1B871ab261CD16335B76a',
26
+ // OP-stack canonical WETH
27
+ weth: '0x4200000000000000000000000000000000000006',
28
+ x402Network: 'base:8453',
29
+ },
30
+ arbitrum: {
31
+ chainId: 42161,
32
+ name: 'Arbitrum One',
33
+ rpcUrl: 'https://arb1.arbitrum.io/rpc',
34
+ // Official USDC on Arbitrum (Circle native deployment — NOT USDC.e bridged)
35
+ usdc: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
36
+ // Uniswap V3 on Arbitrum — SwapRouter02 + QuoterV2
37
+ // Source: https://docs.uniswap.org/contracts/v3/reference/deployments/arbitrum-deployments
38
+ uniswapRouter: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45',
39
+ uniswapQuoter: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e',
40
+ // Standard bridged WETH on Arbitrum
41
+ weth: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
42
+ x402Network: 'arbitrum:42161',
43
+ },
44
+ optimism: {
45
+ chainId: 10,
46
+ name: 'OP Mainnet',
47
+ rpcUrl: 'https://mainnet.optimism.io',
48
+ // Official USDC on Optimism (Circle native deployment — NOT USDC.e bridged)
49
+ usdc: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85',
50
+ // Uniswap V3 on Optimism — SwapRouter02 + QuoterV2
51
+ // Source: https://docs.uniswap.org/contracts/v3/reference/deployments/optimism-deployments
52
+ uniswapRouter: '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45',
53
+ uniswapQuoter: '0x61fFE014bA17989E743c5F6cB21bF9697530B21e',
54
+ // OP-stack canonical WETH
55
+ weth: '0x4200000000000000000000000000000000000006',
56
+ x402Network: 'optimism:10',
57
+ },
58
+ };
59
+ /** Get chain config — throws a clear error if chain is unknown */
60
+ export function getChainConfig(chain) {
61
+ const config = CHAIN_CONFIG[chain];
62
+ if (!config) {
63
+ throw new Error(`Unsupported chain: "${chain}". Supported chains: ${Object.keys(CHAIN_CONFIG).join(', ')}`);
64
+ }
65
+ return config;
66
+ }
67
+ /** Resolve a chain from chainId — returns undefined if not found */
68
+ export function chainFromId(chainId) {
69
+ for (const [key, cfg] of Object.entries(CHAIN_CONFIG)) {
70
+ if (cfg.chainId === chainId)
71
+ return key;
72
+ }
73
+ return undefined;
74
+ }
75
+ //# sourceMappingURL=chains.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chains.js","sourceRoot":"","sources":["../src/chains.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,0FAA0F;AAwB1F;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,YAAY,GAAwC;IAC/D,IAAI,EAAE;QACJ,OAAO,EAAE,IAAI;QACb,IAAI,EAAE,MAAM;QACZ,MAAM,EAAE,0BAA0B;QAClC,mDAAmD;QACnD,IAAI,EAAE,4CAA4C;QAClD,+CAA+C;QAC/C,uFAAuF;QACvF,aAAa,EAAE,4CAA4C;QAC3D,aAAa,EAAE,4CAA4C;QAC3D,0BAA0B;QAC1B,IAAI,EAAE,4CAA4C;QAClD,WAAW,EAAE,WAAW;KACzB;IACD,QAAQ,EAAE;QACR,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,cAAc;QACpB,MAAM,EAAE,8BAA8B;QACtC,4EAA4E;QAC5E,IAAI,EAAE,4CAA4C;QAClD,mDAAmD;QACnD,2FAA2F;QAC3F,aAAa,EAAE,4CAA4C;QAC3D,aAAa,EAAE,4CAA4C;QAC3D,oCAAoC;QACpC,IAAI,EAAE,4CAA4C;QAClD,WAAW,EAAE,gBAAgB;KAC9B;IACD,QAAQ,EAAE;QACR,OAAO,EAAE,EAAE;QACX,IAAI,EAAE,YAAY;QAClB,MAAM,EAAE,6BAA6B;QACrC,4EAA4E;QAC5E,IAAI,EAAE,4CAA4C;QAClD,mDAAmD;QACnD,2FAA2F;QAC3F,aAAa,EAAE,4CAA4C;QAC3D,aAAa,EAAE,4CAA4C;QAC3D,0BAA0B;QAC1B,IAAI,EAAE,4CAA4C;QAClD,WAAW,EAAE,aAAa;KAC3B;CACO,CAAC;AAEX,kEAAkE;AAClE,MAAM,UAAU,cAAc,CAAC,KAAqB;IAClD,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,uBAAuB,KAAK,wBAAwB,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC3F,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;QACtD,IAAI,GAAG,CAAC,OAAO,KAAK,OAAO;YAAE,OAAO,GAAqB,CAAC;IAC5D,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}