@pushchain/core 6.0.27-alpha.1 → 6.0.27-alpha.3

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.
@@ -2,13 +2,15 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PRESET_CATALOG = void 0;
4
4
  const viem_1 = require("viem");
5
+ const aave_reserves_1 = require("./aave-reserves");
6
+ const default_tokens_1 = require("./default-tokens");
5
7
  /** Mainnet and testnet templates from the October 15 Notion export and protocol sources.
6
8
  * Read-only deployment/code evidence and exclusions: plan/agw/research/preset-catalog-2026-10-15/.
7
9
  * Mainnet entries are templates only; no mainnet deployment or execution guarantee.
8
10
  */
9
11
  const address = (value) => (0, viem_1.getAddress)(value.toLowerCase());
10
12
  const wallet = 'agentic:wallet';
11
- const aave = (pool) => [
13
+ const aave = (pool, chain) => [
12
14
  ['supply(address,uint256,address,uint16)', 2, 'Supply'],
13
15
  ['withdraw(address,uint256,address)', 2, 'Withdraw'],
14
16
  ['borrow(address,uint256,uint256,uint16,address)', 4, 'Borrow'],
@@ -19,8 +21,14 @@ const aave = (pool) => [
19
21
  pins: [{ arg: arg, expected: wallet }],
20
22
  app: 'Aave V3',
21
23
  label: label,
24
+ budget: {
25
+ kind: 'token-argument',
26
+ tokenArg: 0,
27
+ amountArg: 1,
28
+ tokens: aave_reserves_1.AAVE_RESERVES[chain],
29
+ },
22
30
  }));
23
- const market = (target, app) => [
31
+ const market = (target, app, token) => [
24
32
  'mint(uint256)',
25
33
  'redeem(uint256)',
26
34
  'redeemUnderlying(uint256)',
@@ -31,6 +39,13 @@ const market = (target, app) => [
31
39
  selector: selector,
32
40
  app,
33
41
  label: selector.split('(')[0],
42
+ budget: selector === 'redeem(uint256)'
43
+ ? {
44
+ kind: 'unsupported',
45
+ reason: 'SHARE_AMOUNT',
46
+ alternative: 'redeemUnderlying(uint256)',
47
+ }
48
+ : { kind: 'fixed-token', token, amountArg: 0 },
34
49
  }));
35
50
  const sepoliaPool = address('0x6Ae43d3271ff6888e7Fc43Fd7321a503ff738951');
36
51
  const basePool = address('0x8bAB6d1b75f19e9eD9fCe8b9BD338844fF79aE27');
@@ -76,7 +91,7 @@ const mainnetMarkets = (chain) => {
76
91
  : '0xfD36E2c2a6789Db23113685031d7F16329158384';
77
92
  return {
78
93
  allowedCalls: [
79
- ...entries.flatMap(([target, app]) => market(address(target), app)),
94
+ ...entries.flatMap(([target, app, token]) => market(address(target), app, address(token))),
80
95
  ...(entries.length
81
96
  ? [
82
97
  {
@@ -84,6 +99,7 @@ const mainnetMarkets = (chain) => {
84
99
  selector: 'enterMarkets(address[])',
85
100
  app: chain === 'eip155:8453' ? 'Moonwell' : 'Venus',
86
101
  label: 'Enter markets',
102
+ budget: { kind: 'setup' },
87
103
  },
88
104
  ]
89
105
  : []),
@@ -138,6 +154,7 @@ exports.PRESET_CATALOG = [
138
154
  ],
139
155
  ].map(([chain, router, app]) => ({
140
156
  chainNamespace: chain,
157
+ defaultTokens: default_tokens_1.DEX_DEFAULT_TOKENS[chain],
141
158
  allowedCalls: [],
142
159
  swapRouters: [{ address: address(router), app }],
143
160
  approvals: [{ spender: address(router), app }],
@@ -154,40 +171,65 @@ exports.PRESET_CATALOG = [
154
171
  ['eip155:56', '0x6807dc923806fE8Fd134338EABCA509979a7e0cB'],
155
172
  ].map(([chainNamespace, pool]) => ({
156
173
  chainNamespace,
174
+ defaultTokens: default_tokens_1.LENDING_DEFAULT_TOKENS[chainNamespace],
157
175
  allowedCalls: [
158
- ...aave(address(pool)),
176
+ ...aave(address(pool), chainNamespace),
159
177
  ...mainnetMarkets(chainNamespace).allowedCalls,
160
178
  ],
161
179
  approvals: [
162
- { spender: address(pool), app: 'Aave V3' },
180
+ {
181
+ spender: address(pool),
182
+ app: 'Aave V3',
183
+ tokens: aave_reserves_1.AAVE_RESERVES[chainNamespace],
184
+ },
163
185
  ...(mainnetMarkets(chainNamespace).approvals ?? []),
164
186
  ],
165
187
  })),
166
188
  {
167
189
  chainNamespace: 'eip155:11155111',
168
- allowedCalls: aave(sepoliaPool),
169
- approvals: [{ spender: sepoliaPool, app: 'Aave V3' }],
190
+ defaultTokens: default_tokens_1.LENDING_DEFAULT_TOKENS['eip155:11155111'],
191
+ allowedCalls: aave(sepoliaPool, 'eip155:11155111'),
192
+ approvals: [
193
+ {
194
+ spender: sepoliaPool,
195
+ app: 'Aave V3',
196
+ tokens: aave_reserves_1.AAVE_RESERVES['eip155:11155111'],
197
+ },
198
+ ],
170
199
  },
171
200
  {
172
201
  chainNamespace: 'eip155:421614',
173
- allowedCalls: aave(arbitrumPool),
174
- approvals: [{ spender: arbitrumPool, app: 'Aave V3' }],
202
+ defaultTokens: default_tokens_1.LENDING_DEFAULT_TOKENS['eip155:421614'],
203
+ allowedCalls: aave(arbitrumPool, 'eip155:421614'),
204
+ approvals: [
205
+ {
206
+ spender: arbitrumPool,
207
+ app: 'Aave V3',
208
+ tokens: aave_reserves_1.AAVE_RESERVES['eip155:421614'],
209
+ },
210
+ ],
175
211
  },
176
212
  {
177
213
  chainNamespace: 'eip155:84532',
214
+ defaultTokens: default_tokens_1.LENDING_DEFAULT_TOKENS['eip155:84532'],
178
215
  allowedCalls: [
179
- ...aave(basePool),
180
- ...market(moonUSDBC, 'Moonwell mUSDbC'),
181
- ...market(moonWETH, 'Moonwell mWETH'),
216
+ ...aave(basePool, 'eip155:84532'),
217
+ ...market(moonUSDBC, 'Moonwell mUSDbC', address('0x4616975f7E6045FD9737e95bC527e9e14c727BD2')),
218
+ ...market(moonWETH, 'Moonwell mWETH', address('0x663A6F02d547dFd639BF689FED298712f2FB69Fb')),
182
219
  {
183
220
  target: address('0xC72ba45f2Ae134F21DAEb2b7B5fd5De0064Ee9bB'),
184
221
  selector: 'enterMarkets(address[])',
185
222
  app: 'Moonwell',
186
223
  label: 'Enter markets',
224
+ budget: { kind: 'setup' },
187
225
  },
188
226
  ],
189
227
  approvals: [
190
- { spender: basePool, app: 'Aave V3' },
228
+ {
229
+ spender: basePool,
230
+ app: 'Aave V3',
231
+ tokens: aave_reserves_1.AAVE_RESERVES['eip155:84532'],
232
+ },
191
233
  {
192
234
  spender: moonUSDBC,
193
235
  app: 'Moonwell mUSDbC',
@@ -202,14 +244,16 @@ exports.PRESET_CATALOG = [
202
244
  },
203
245
  {
204
246
  chainNamespace: 'eip155:97',
247
+ defaultTokens: default_tokens_1.LENDING_DEFAULT_TOKENS['eip155:97'],
205
248
  allowedCalls: [
206
- ...market(venusUSDT, 'Venus vUSDT'),
207
- ...market(venusUSDC, 'Venus vUSDC'),
249
+ ...market(venusUSDT, 'Venus vUSDT', address('0xA11c8D9DC9b66E209Ef60F0C8D969D3CD988782c')),
250
+ ...market(venusUSDC, 'Venus vUSDC', address('0x16227D60f7a0e586C66B005219dfc887D13C9531')),
208
251
  {
209
252
  target: address('0x94d1820b2D1c7c7452A163983Dc888CEC546b77D'),
210
253
  selector: 'enterMarkets(address[])',
211
254
  app: 'Venus',
212
255
  label: 'Enter markets',
256
+ budget: { kind: 'setup' },
213
257
  },
214
258
  ],
215
259
  approvals: [
@@ -233,25 +277,29 @@ exports.PRESET_CATALOG = [
233
277
  chains: [
234
278
  {
235
279
  chainNamespace: 'eip155:1',
280
+ defaultTokens: default_tokens_1.STAKING_DEFAULT_TOKENS['eip155:1'],
236
281
  allowedCalls: [
237
282
  {
238
283
  target: stETH,
239
284
  selector: 'submit(address)',
240
285
  maxValue: BigInt(0),
241
286
  app: 'Lido',
242
- label: 'Stake ETH (set an explicit value cap)',
287
+ label: 'Stake ETH',
288
+ budget: { kind: 'native-value' },
243
289
  },
244
290
  {
245
291
  target: wstETH,
246
292
  selector: 'wrap(uint256)',
247
293
  app: 'Lido wstETH',
248
294
  label: 'Wrap stETH',
295
+ budget: { kind: 'fixed-token', token: stETH, amountArg: 0 },
249
296
  },
250
297
  {
251
298
  target: wstETH,
252
299
  selector: 'unwrap(uint256)',
253
300
  app: 'Lido wstETH',
254
301
  label: 'Unwrap wstETH',
302
+ budget: { kind: 'fixed-token', token: wstETH, amountArg: 0 },
255
303
  },
256
304
  {
257
305
  target: lidoQueue,
@@ -259,29 +307,32 @@ exports.PRESET_CATALOG = [
259
307
  pins: [{ arg: 1, expected: wallet }],
260
308
  app: 'Lido withdrawal queue',
261
309
  label: 'Request withdrawal',
310
+ budget: { kind: 'unsupported', reason: 'DYNAMIC_AMOUNT' },
262
311
  },
263
312
  {
264
313
  target: lidoQueue,
265
314
  selector: 'claimWithdrawals(uint256[],uint256[])',
266
315
  app: 'Lido withdrawal queue',
267
316
  label: 'Claim finalized withdrawals',
317
+ budget: { kind: 'unsupported', reason: 'POSITION_AMOUNT' },
268
318
  },
269
319
  {
270
320
  target: lidoQueue,
271
321
  selector: 'claimWithdrawal(uint256)',
272
322
  app: 'Lido withdrawal queue',
273
323
  label: 'Claim finalized withdrawal',
324
+ budget: { kind: 'unsupported', reason: 'POSITION_AMOUNT' },
274
325
  },
275
326
  {
276
327
  target: address('0x858646372CC42E1A627fcE94aa7A7033e7CF075A'),
277
328
  selector: 'depositIntoStrategy(address,address,uint256)',
278
329
  app: 'EigenLayer',
279
330
  label: 'Deposit into strategy (withdrawals not included)',
331
+ budget: { kind: 'token-argument', tokenArg: 1, amountArg: 2 },
280
332
  },
281
333
  ],
282
334
  approvals: [
283
335
  { spender: wstETH, token: stETH, app: 'Lido wstETH' },
284
- { spender: lidoQueue, token: stETH, app: 'Lido withdrawal queue' },
285
336
  {
286
337
  spender: address('0x858646372CC42E1A627fcE94aa7A7033e7CF075A'),
287
338
  app: 'EigenLayer',
@@ -290,12 +341,14 @@ exports.PRESET_CATALOG = [
290
341
  },
291
342
  {
292
343
  chainNamespace: 'eip155:11155111',
344
+ defaultTokens: default_tokens_1.STAKING_DEFAULT_TOKENS['eip155:11155111'],
293
345
  allowedCalls: [
294
346
  {
295
347
  target: eigen,
296
348
  selector: 'depositIntoStrategy(address,address,uint256)',
297
349
  app: 'EigenLayer',
298
350
  label: 'Deposit into strategy (withdrawals not included)',
351
+ budget: { kind: 'token-argument', tokenArg: 1, amountArg: 2 },
299
352
  },
300
353
  ],
301
354
  approvals: [{ spender: eigen, app: 'EigenLayer' }],
@@ -0,0 +1,9 @@
1
+ import type { Address } from 'viem';
2
+ import type { RuleAsset } from '../agentic.types';
3
+ /** Preset-only permission policy. Ordinary rule authoring retains zero defaults.
4
+ * Amounts are token quantities, not USD valuations or destination lifetime caps.
5
+ * Metadata verified read-only on October 18; evidence is retained under plan/agw/.
6
+ */
7
+ export declare const PRESET_VALIDITY_SECONDS: number;
8
+ export declare const PRESET_GAS_BUDGETS: Readonly<Record<string, bigint>>;
9
+ export declare function defaultPresetAsset(chain: string, token: Address): RuleAsset;
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PRESET_GAS_BUDGETS = exports.PRESET_VALIDITY_SECONDS = void 0;
4
+ exports.defaultPresetAsset = defaultPresetAsset;
5
+ const viem_1 = require("viem");
6
+ const errors_1 = require("../errors");
7
+ /** Preset-only permission policy. Ordinary rule authoring retains zero defaults.
8
+ * Amounts are token quantities, not USD valuations or destination lifetime caps.
9
+ * Metadata verified read-only on October 18; evidence is retained under plan/agw/.
10
+ */
11
+ exports.PRESET_VALIDITY_SECONDS = 7 * 24 * 60 * 60;
12
+ // Explicit profiles keep future chains from inheriting an unreviewed budget.
13
+ // 20 PC is a ceiling, not the amount charged: execution obtains a fresh quote.
14
+ // This budget has testnet protocol-flow coverage; mainnet remains template-only.
15
+ exports.PRESET_GAS_BUDGETS = Object.fromEntries([
16
+ 'eip155:1',
17
+ 'eip155:8453',
18
+ 'eip155:42161',
19
+ 'eip155:56',
20
+ 'eip155:11155111',
21
+ 'eip155:84532',
22
+ 'eip155:421614',
23
+ 'eip155:97',
24
+ ].map((chain) => [chain, (0, viem_1.parseUnits)('20', 18)]));
25
+ const stable6 = { decimals: 6, perCall: '10', total: '100' };
26
+ const stable18 = { ...stable6, decimals: 18 };
27
+ const eth = { decimals: 18, perCall: '0.001', total: '0.01' };
28
+ const bnb = { decimals: 18, perCall: '0.01', total: '0.1' };
29
+ const link = { decimals: 18, perCall: '1', total: '10' };
30
+ // Match by chain AND address. Never infer decimals or limits from a symbol,
31
+ // caller metadata, or the position of a token in an arbitrary selection.
32
+ const TOKEN_BUDGETS = {
33
+ 'eip155:1': [
34
+ ['0x0000000000000000000000000000000000000000', eth],
35
+ ['0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', eth],
36
+ ['0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', stable6],
37
+ ['0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84', eth],
38
+ ['0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0', eth],
39
+ ],
40
+ 'eip155:8453': [
41
+ ['0x4200000000000000000000000000000000000006', eth],
42
+ ['0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', stable6],
43
+ ],
44
+ 'eip155:42161': [
45
+ ['0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', eth],
46
+ ['0xaf88d065e77c8cC2239327C5EDb3A432268e5831', stable6],
47
+ ],
48
+ 'eip155:56': [
49
+ ['0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c', bnb],
50
+ ['0x55d398326f99059fF775485246999027B3197955', stable18],
51
+ ],
52
+ 'eip155:11155111': [
53
+ ['0xfff9976782d46cc05630d1f6ebab18b2324d6b14', eth],
54
+ ['0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238', stable6],
55
+ ['0xf8Fb3713D459D7C1018BD0A49D19b4C44290EBE5', link],
56
+ ['0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9', eth],
57
+ ],
58
+ 'eip155:84532': [
59
+ ['0x4200000000000000000000000000000000000006', eth],
60
+ ['0x036CbD53842c5426634e7929541eC2318f3dCF7e', stable6],
61
+ ['0x810D46F9a9027E28F9B01F75E2bdde839dA61115', link],
62
+ ['0x4616975f7E6045FD9737e95bC527e9e14c727BD2', stable6],
63
+ ['0x663A6F02d547dFd639BF689FED298712f2FB69Fb', eth],
64
+ ],
65
+ 'eip155:421614': [
66
+ ['0x980B62Da83eFf3D4576C647993b0c1D7faf17c73', eth],
67
+ ['0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d', stable6],
68
+ ],
69
+ 'eip155:97': [
70
+ ['0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd', bnb],
71
+ ['0xA11c8D9DC9b66E209Ef60F0C8D969D3CD988782c', stable6],
72
+ ['0x16227D60f7a0e586C66B005219dfc887D13C9531', stable6],
73
+ ],
74
+ };
75
+ function defaultPresetAsset(chain, token) {
76
+ const budget = TOKEN_BUDGETS[chain]?.find(([address]) => (0, viem_1.getAddress)(address) === (0, viem_1.getAddress)(token))?.[1];
77
+ if (!budget)
78
+ throw (0, errors_1.capabilityUnavailable)('presetPolicy', `No reviewed default spending policy for ${chain}:${token}`);
79
+ return {
80
+ token,
81
+ maxPerCall: (0, viem_1.parseUnits)(budget.perCall, budget.decimals),
82
+ maxTotal: (0, viem_1.parseUnits)(budget.total, budget.decimals),
83
+ };
84
+ }
85
+ //# sourceMappingURL=default-policy.js.map
@@ -0,0 +1,4 @@
1
+ import { type Address } from 'viem';
2
+ export declare const DEX_DEFAULT_TOKENS: Readonly<Record<string, readonly Address[]>>;
3
+ export declare const LENDING_DEFAULT_TOKENS: Readonly<Record<string, readonly Address[]>>;
4
+ export declare const STAKING_DEFAULT_TOKENS: Readonly<Record<string, readonly Address[]>>;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.STAKING_DEFAULT_TOKENS = exports.LENDING_DEFAULT_TOKENS = exports.DEX_DEFAULT_TOKENS = void 0;
4
+ const viem_1 = require("viem");
5
+ /** Curated subsets of the reviewed catalog. Addresses are destination tokens,
6
+ * not a promise that a Push PRC20 route or protocol liquidity is available.
7
+ * Keep these sets within the policy's 32-call/32-pin limits after expansion.
8
+ * Testnet DEX pairs and lending/staking assets match the live preset fixtures.
9
+ * Mainnet addresses share the catalog's October 15 read-only source baseline.
10
+ * Spending caps are intentionally separate from token selection.
11
+ */
12
+ const addresses = (...values) => values.map((value) => (0, viem_1.getAddress)(value.toLowerCase()));
13
+ exports.DEX_DEFAULT_TOKENS = {
14
+ 'eip155:1': addresses('0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'),
15
+ 'eip155:8453': addresses('0x4200000000000000000000000000000000000006', '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'),
16
+ 'eip155:42161': addresses('0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', '0xaf88d065e77c8cC2239327C5EDb3A432268e5831'),
17
+ 'eip155:56': addresses('0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c', '0x55d398326f99059fF775485246999027B3197955'),
18
+ 'eip155:11155111': addresses('0xfff9976782d46cc05630d1f6ebab18b2324d6b14', '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238'),
19
+ 'eip155:84532': addresses('0x4200000000000000000000000000000000000006', '0x036CbD53842c5426634e7929541eC2318f3dCF7e'),
20
+ 'eip155:421614': addresses('0x980B62Da83eFf3D4576C647993b0c1D7faf17c73', '0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d'),
21
+ 'eip155:97': addresses('0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd', '0xA11c8D9DC9b66E209Ef60F0C8D969D3CD988782c'),
22
+ };
23
+ exports.LENDING_DEFAULT_TOKENS = {
24
+ 'eip155:1': exports.DEX_DEFAULT_TOKENS['eip155:1'],
25
+ // One shared reserve/market token keeps Aave + Moonwell/Venus within 32 pins.
26
+ 'eip155:8453': addresses('0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'),
27
+ 'eip155:42161': exports.DEX_DEFAULT_TOKENS['eip155:42161'],
28
+ 'eip155:56': addresses('0x55d398326f99059fF775485246999027B3197955'),
29
+ // Protocol testnet reserves differ from Circle USDC and DEX wrapped ETH.
30
+ 'eip155:11155111': addresses('0xf8Fb3713D459D7C1018BD0A49D19b4C44290EBE5'),
31
+ 'eip155:84532': addresses('0x810D46F9a9027E28F9B01F75E2bdde839dA61115', '0x4616975f7E6045FD9737e95bC527e9e14c727BD2', '0x663A6F02d547dFd639BF689FED298712f2FB69Fb'),
32
+ 'eip155:421614': addresses('0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d'),
33
+ 'eip155:97': addresses('0xA11c8D9DC9b66E209Ef60F0C8D969D3CD988782c', '0x16227D60f7a0e586C66B005219dfc887D13C9531'),
34
+ };
35
+ exports.STAKING_DEFAULT_TOKENS = {
36
+ 'eip155:1': addresses(viem_1.zeroAddress, '0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84', '0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0'),
37
+ 'eip155:11155111': addresses('0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9'),
38
+ };
39
+ //# sourceMappingURL=default-tokens.js.map
@@ -6,6 +6,10 @@ const viem_1 = require("viem");
6
6
  const errors_1 = require("../errors");
7
7
  const selectors_1 = require("../codec/selectors");
8
8
  const catalog_1 = require("./catalog");
9
+ const operation_limits_1 = require("./operation-limits");
10
+ const enums_1 = require("../../constants/enums");
11
+ const routes_1 = require("./routes");
12
+ const default_policy_1 = require("./default-policy");
9
13
  const MAX_UINT256 = (BigInt(1) << BigInt(256)) - BigInt(1);
10
14
  const invalid = (message) => new errors_1.AgenticError(errors_1.AGENTIC_ERROR_CODE.INVALID_RULE, message);
11
15
  function cap(value, name) {
@@ -50,8 +54,24 @@ function buildPresetApi(catalog) {
50
54
  ...p,
51
55
  chains: p.chains.map((c) => ({
52
56
  ...c,
53
- allowedCalls: c.allowedCalls.map(copyCall),
54
- approvals: c.approvals?.map((a) => ({ ...a })),
57
+ ...(c.defaultTokens ? { defaultTokens: [...c.defaultTokens] } : {}),
58
+ allowedCalls: c.allowedCalls.map((call) => ({
59
+ ...copyCall(call),
60
+ ...(call.budget
61
+ ? {
62
+ budget: {
63
+ ...call.budget,
64
+ ...(call.budget.kind === 'token-argument' && call.budget.tokens
65
+ ? { tokens: [...call.budget.tokens] }
66
+ : {}),
67
+ },
68
+ }
69
+ : {}),
70
+ })),
71
+ approvals: c.approvals?.map((a) => ({
72
+ ...a,
73
+ ...(a.tokens ? { tokens: [...a.tokens] } : {}),
74
+ })),
55
75
  swapRouters: c.swapRouters?.map((r) => ({ ...r })),
56
76
  })),
57
77
  }));
@@ -77,15 +97,23 @@ function buildPresetApi(catalog) {
77
97
  throw invalid(`unknown preset ${String(preset)}`);
78
98
  if (!options || typeof options !== 'object' || Array.isArray(options))
79
99
  throw invalid('preset options must be an object');
100
+ const now = Math.floor(Date.now() / 1000);
101
+ const network = options.network ?? enums_1.PUSH_NETWORK.TESTNET_DONUT;
102
+ const funding = options.funding ?? 'push';
103
+ if (!Object.values(enums_1.PUSH_NETWORK).includes(network))
104
+ throw invalid('unknown preset Push network');
105
+ if (funding !== 'push' && funding !== 'destination')
106
+ throw invalid('preset funding must be push or destination');
80
107
  if (options.agent !== undefined &&
81
108
  (!(0, viem_1.isAddress)(options.agent, { strict: false }) ||
82
109
  (0, viem_1.getAddress)(options.agent) === viem_1.zeroAddress))
83
110
  throw invalid('preset agent must be a nonzero Push address');
84
111
  if (options.validUntil !== undefined &&
85
112
  (!Number.isSafeInteger(options.validUntil) ||
86
- options.validUntil <= Math.floor(Date.now() / 1000) ||
113
+ options.validUntil <= now ||
87
114
  options.validUntil >= 2 ** 48))
88
115
  throw invalid('preset validUntil must be a future uint48 timestamp');
116
+ const validUntil = options.validUntil ?? now + default_policy_1.PRESET_VALIDITY_SECONDS;
89
117
  if (options.maxGasPerCall !== undefined)
90
118
  cap(options.maxGasPerCall, 'maxGasPerCall');
91
119
  if (options.assets !== undefined && !Array.isArray(options.assets))
@@ -98,6 +126,12 @@ function buildPresetApi(catalog) {
98
126
  if (!chains.length && options.assets?.length)
99
127
  throw (0, errors_1.capabilityUnavailable)('presetCatalog', `preset ${preset} has no approved app entries`);
100
128
  const assets = new Map();
129
+ // An explicit array (including []) replaces the defaults for all selected
130
+ // chains. Never add permissions on chains the caller left unconfigured.
131
+ if (options.assets === undefined)
132
+ for (const chain of chains)
133
+ if (chain.defaultTokens)
134
+ assets.set(chain.chainNamespace, chain.defaultTokens.map((token) => (0, default_policy_1.defaultPresetAsset)(chain.chainNamespace, token)));
101
135
  for (const asset of options.assets ?? []) {
102
136
  if (!asset || typeof asset !== 'object')
103
137
  throw invalid('preset asset must be an object');
@@ -125,8 +159,19 @@ function buildPresetApi(catalog) {
125
159
  });
126
160
  assets.set(chain, group);
127
161
  }
162
+ const readiness = [];
128
163
  const rules = chains.map((chain) => {
129
- const calls = chain.allowedCalls.map(copyCall);
164
+ const maxGasPerCall = options.maxGasPerCall ??
165
+ chain.maxGasPerCall ??
166
+ (chain.defaultTokens
167
+ ? default_policy_1.PRESET_GAS_BUDGETS[chain.chainNamespace]
168
+ : undefined);
169
+ const selectedAssets = assets.get(chain.chainNamespace) ?? [];
170
+ const limits = (0, operation_limits_1.applyOperationLimits)(chain.allowedCalls.map((c) => ({
171
+ ...copyCall(c),
172
+ ...(c.budget ? { budget: c.budget } : {}),
173
+ })), selectedAssets);
174
+ const calls = limits.calls;
130
175
  if (chain.swapRouters?.length) {
131
176
  const tokens = assets.get(chain.chainNamespace) ?? [];
132
177
  if (tokens.some((a) => tokenAddress(a.token) === viem_1.zeroAddress ||
@@ -185,6 +230,21 @@ function buildPresetApi(catalog) {
185
230
  });
186
231
  }
187
232
  }
233
+ if (chain.swapRouters?.length) {
234
+ const swaps = calls.filter((c) => c.selector.startsWith('exact'));
235
+ for (const c of swaps) {
236
+ const positive = c.pins?.some((p) => 'max' in p && p.max !== undefined && p.max > BigInt(0));
237
+ if (positive)
238
+ limits.enabled = true;
239
+ else
240
+ limits.disabledCalls.push({
241
+ target: c.target,
242
+ selector: c.selector,
243
+ app: c.app,
244
+ reason: selectedAssets.length < 2 ? 'TOKEN_REQUIRED' : 'ZERO_CAP',
245
+ });
246
+ }
247
+ }
188
248
  const approvals = new Set();
189
249
  for (const asset of assets.get(chain.chainNamespace) ?? []) {
190
250
  const token = tokenAddress(asset.token);
@@ -198,6 +258,9 @@ function buildPresetApi(catalog) {
198
258
  if (approval.token &&
199
259
  (0, viem_1.getAddress)(token) !== (0, viem_1.getAddress)(approval.token))
200
260
  continue;
261
+ if (approval.tokens &&
262
+ !approval.tokens.some((t) => (0, viem_1.getAddress)(t) === (0, viem_1.getAddress)(token)))
263
+ continue;
201
264
  const spender = (0, viem_1.getAddress)(approval.spender);
202
265
  const target = (0, viem_1.getAddress)(token);
203
266
  const approvalKey = `${target}:${spender}`;
@@ -236,29 +299,81 @@ function buildPresetApi(catalog) {
236
299
  throw (0, errors_1.capabilityUnavailable)('presetCalls', 'an unpinned alternative would bypass restrictions for this target/function');
237
300
  pairs.set(pair, !!call.pins?.length);
238
301
  }
302
+ const issues = [];
303
+ if (!options.agent)
304
+ issues.push({
305
+ code: 'AGENT_REQUIRED',
306
+ message: 'Choose the agent Push address.',
307
+ });
308
+ if ((maxGasPerCall ?? BigInt(0)) === BigInt(0))
309
+ issues.push({
310
+ code: 'GAS_BUDGET_REQUIRED',
311
+ message: 'Set a positive PC gas budget per outbound.',
312
+ });
313
+ if (!selectedAssets.length)
314
+ issues.push({
315
+ code: 'ASSETS_REQUIRED',
316
+ message: 'Choose tokens and their limits, then regenerate this preset.',
317
+ });
318
+ if (chain.swapRouters?.length && selectedAssets.length < 2)
319
+ issues.push({
320
+ code: 'TOKEN_PAIR_REQUIRED',
321
+ message: 'Choose both swap tokens, then regenerate this preset.',
322
+ });
323
+ if (!limits.enabled)
324
+ issues.push({
325
+ code: 'NO_ENABLED_OPERATION',
326
+ message: 'No asset operation has a positive per-call limit.',
327
+ });
328
+ const configurationReady = issues.length === 0;
329
+ const routes = (0, routes_1.presetRoutes)(chain.chainNamespace, selectedAssets, network, funding);
330
+ issues.push(...routes.issues);
331
+ const bridgeAssets = funding === 'destination' ? [] : selectedAssets;
332
+ readiness.push({
333
+ chainNamespace: chain.chainNamespace,
334
+ ready: issues.length === 0,
335
+ configurationReady,
336
+ funding,
337
+ selectedAssets: selectedAssets.map((a) => ({
338
+ ...a,
339
+ token: typeof a.token === 'string' ? a.token : { ...a.token },
340
+ })),
341
+ bridgeAssets: routes.bridgeAssets,
342
+ issues,
343
+ disabledCalls: limits.disabledCalls,
344
+ excludedCalls: limits.excludedCalls,
345
+ knownTokens: [
346
+ ...new Set([...limits.knownTokens, ...(chain.defaultTokens ?? [])]),
347
+ ],
348
+ zeroBridgeLimits: bridgeAssets
349
+ .filter((a) => a.maxPerCall === BigInt(0) || a.maxTotal === BigInt(0))
350
+ .map(operation_limits_1.assetAddress),
351
+ });
239
352
  return {
240
353
  chainNamespace: chain.chainNamespace,
241
354
  allowedCalls: calls,
242
355
  ...(options.agent !== undefined
243
356
  ? { agent: (0, viem_1.getAddress)(options.agent) }
244
357
  : {}),
245
- ...(options.validUntil !== undefined
246
- ? { validUntil: options.validUntil }
247
- : {}),
248
- ...(options.assets !== undefined
249
- ? { assets: assets.get(chain.chainNamespace) ?? [] }
358
+ validUntil,
359
+ ...(options.assets !== undefined || chain.defaultTokens
360
+ ? { assets: bridgeAssets }
250
361
  : {}),
251
- ...((options.maxGasPerCall ?? chain.maxGasPerCall) !== undefined
252
- ? {
253
- maxGasPerCall: cap(options.maxGasPerCall ?? chain.maxGasPerCall, 'maxGasPerCall'),
254
- }
362
+ ...(maxGasPerCall !== undefined
363
+ ? { maxGasPerCall: cap(maxGasPerCall, 'maxGasPerCall') }
255
364
  : {}),
256
365
  };
257
366
  });
258
- return { rules };
367
+ return {
368
+ rules,
369
+ readiness: {
370
+ ready: readiness.length > 0 && readiness.every((c) => c.ready),
371
+ chains: readiness,
372
+ },
373
+ };
259
374
  },
260
375
  };
261
- // The overload narrows templates only when both required fields were supplied and validated.
376
+ // Expiry is generated; the overload narrows templates when the agent is supplied.
262
377
  return Object.freeze(api);
263
378
  }
264
379
  exports.agenticPresets = buildPresetApi(catalog_1.PRESET_CATALOG);
@@ -0,0 +1,13 @@
1
+ import type { RuleAsset, RuleCall } from '../agentic.types';
2
+ import type { PresetCallDefinition } from './catalog';
3
+ import type { PresetCallIssue } from './types';
4
+ export declare const assetAddress: (asset: RuleAsset) => string;
5
+ export declare const nativeAsset: (asset: RuleAsset) => boolean;
6
+ /** Compile reviewed protocol argument positions. No exchange-rate or share guesses. */
7
+ export declare function applyOperationLimits(definitions: readonly PresetCallDefinition[], assets: RuleAsset[]): {
8
+ calls: RuleCall[];
9
+ enabled: boolean;
10
+ disabledCalls: PresetCallIssue[];
11
+ excludedCalls: PresetCallIssue[];
12
+ knownTokens: string[];
13
+ };