@smartagentkit/sdk 0.1.2 → 0.1.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.
package/dist/index.mjs CHANGED
@@ -3,7 +3,9 @@ import {
3
3
  createPublicClient,
4
4
  createWalletClient,
5
5
  http,
6
- encodeFunctionData
6
+ encodeFunctionData,
7
+ isAddress as isAddress2,
8
+ isHex
7
9
  } from "viem";
8
10
  import {
9
11
  privateKeyToAccount,
@@ -18,7 +20,7 @@ import { erc7579Actions } from "permissionless/actions/erc7579";
18
20
  import { getHookMultiPlexer } from "@rhinestone/module-sdk";
19
21
 
20
22
  // src/presets.ts
21
- import { parseEther } from "viem";
23
+ import { parseEther, isAddress } from "viem";
22
24
 
23
25
  // src/constants.ts
24
26
  var MODULE_TYPE_VALIDATOR = 1;
@@ -30,6 +32,7 @@ var ENTRYPOINT_V07 = "0x0000000071727De22E5E9d8BAf0edAc6f37da032";
30
32
  var SAFE_7579_MODULE = "0x7579EE8307284F293B1927136486880611F20002";
31
33
  var SAFE_7579_LAUNCHPAD = "0x7579011aB74c46090561ea277Ba79D510c6C00ff";
32
34
  var RHINESTONE_ATTESTER = "0x000000333034E9f539ce08819E12c1b8Cb29084d";
35
+ var SMART_SESSIONS_VALIDATOR = "0x00000000002B0eCfbD0496EE71e01257dA0E37DE";
33
36
  var ATTESTERS_THRESHOLD = 1;
34
37
  var WINDOW_1_HOUR = 3600;
35
38
  var WINDOW_1_DAY = 86400;
@@ -167,15 +170,6 @@ var MODULE_ONINSTALL_ABI = [
167
170
  stateMutability: "nonpayable"
168
171
  }
169
172
  ];
170
- var SET_TRUSTED_FORWARDER_ABI = [
171
- {
172
- name: "setTrustedForwarder",
173
- type: "function",
174
- inputs: [{ name: "forwarder", type: "address" }],
175
- outputs: [],
176
- stateMutability: "nonpayable"
177
- }
178
- ];
179
173
  var HOOK_MULTIPLEXER_ABI = [
180
174
  {
181
175
  name: "addHook",
@@ -202,119 +196,6 @@ var MODULE_ADDRESSES = {
202
196
  // Will be populated after deployment
203
197
  };
204
198
 
205
- // src/presets.ts
206
- var PRESETS = {
207
- /**
208
- * DeFi Trader preset:
209
- * - Daily spending limit on ETH and stablecoins
210
- * - Allowlist of approved DEX contracts
211
- * - Emergency pause with owner as guardian
212
- */
213
- "defi-trader": (owner, params = {}) => [
214
- {
215
- type: "spending-limit",
216
- limits: [
217
- {
218
- token: NATIVE_TOKEN,
219
- limit: params.dailyEthLimit ?? parseEther("1"),
220
- window: WINDOW_1_DAY
221
- },
222
- ...params.stablecoinLimits ?? []
223
- ]
224
- },
225
- // Only include allowlist if specific DEXes are provided;
226
- // an empty allowlist in "allow" mode would block all transactions.
227
- ...params.allowedDexes?.length ? [
228
- {
229
- type: "allowlist",
230
- mode: "allow",
231
- targets: params.allowedDexes.map((addr) => ({
232
- address: addr,
233
- selector: "0x00000000"
234
- }))
235
- }
236
- ] : [],
237
- {
238
- type: "emergency-pause",
239
- guardian: params.guardian ?? owner,
240
- autoUnpauseAfter: WINDOW_1_DAY
241
- }
242
- ],
243
- /**
244
- * Treasury Agent preset:
245
- * - Lower spending limits with longer windows
246
- * - Emergency pause (manual only)
247
- */
248
- "treasury-agent": (owner, params = {}) => [
249
- {
250
- type: "spending-limit",
251
- limits: [
252
- {
253
- token: NATIVE_TOKEN,
254
- limit: params.weeklyEthLimit ?? parseEther("5"),
255
- window: WINDOW_1_WEEK
256
- }
257
- ]
258
- },
259
- {
260
- type: "emergency-pause",
261
- guardian: params.guardian ?? owner,
262
- autoUnpauseAfter: 0
263
- }
264
- ],
265
- /**
266
- * Payment Agent preset:
267
- * - Strict spending limits
268
- * - Allowlist of approved recipients only
269
- * - Emergency pause
270
- */
271
- "payment-agent": (owner, params = {}) => [
272
- {
273
- type: "spending-limit",
274
- limits: [
275
- {
276
- token: NATIVE_TOKEN,
277
- limit: params.dailyLimit ?? parseEther("0.1"),
278
- window: WINDOW_1_DAY
279
- }
280
- ]
281
- },
282
- // Only include allowlist if specific recipients are provided;
283
- // an empty allowlist in "allow" mode would block all transactions.
284
- ...params.approvedRecipients?.length ? [
285
- {
286
- type: "allowlist",
287
- mode: "allow",
288
- targets: params.approvedRecipients.map(
289
- (addr) => ({
290
- address: addr
291
- })
292
- )
293
- }
294
- ] : [],
295
- {
296
- type: "emergency-pause",
297
- guardian: params.guardian ?? owner,
298
- autoUnpauseAfter: 3600
299
- }
300
- ],
301
- /**
302
- * Minimal preset:
303
- * - Just emergency pause
304
- * - For agents that need maximum flexibility with a kill switch
305
- */
306
- minimal: (owner, params = {}) => [
307
- {
308
- type: "emergency-pause",
309
- guardian: params.guardian ?? owner,
310
- autoUnpauseAfter: 0
311
- }
312
- ]
313
- };
314
-
315
- // src/policies.ts
316
- import { encodeAbiParameters, parseAbiParameters } from "viem";
317
-
318
199
  // src/errors.ts
319
200
  var SmartAgentKitError = class extends Error {
320
201
  constructor(message, options) {
@@ -365,7 +246,174 @@ var SessionError = class extends SmartAgentKitError {
365
246
  }
366
247
  };
367
248
 
249
+ // src/presets.ts
250
+ function validatePresetParams(presetName, params, spec) {
251
+ for (const [key, expectedType] of Object.entries(spec)) {
252
+ if (params[key] === void 0) continue;
253
+ const value = params[key];
254
+ switch (expectedType) {
255
+ case "bigint":
256
+ if (typeof value !== "bigint") {
257
+ throw new PolicyConfigError(
258
+ `Preset "${presetName}": parameter "${key}" must be a bigint, got ${typeof value}`
259
+ );
260
+ }
261
+ break;
262
+ case "Address":
263
+ if (typeof value !== "string" || !isAddress(value)) {
264
+ throw new PolicyConfigError(
265
+ `Preset "${presetName}": parameter "${key}" must be a valid Ethereum address`
266
+ );
267
+ }
268
+ break;
269
+ case "Address[]":
270
+ if (!Array.isArray(value) || !value.every((v) => typeof v === "string" && isAddress(v))) {
271
+ throw new PolicyConfigError(
272
+ `Preset "${presetName}": parameter "${key}" must be an array of valid Ethereum addresses`
273
+ );
274
+ }
275
+ break;
276
+ }
277
+ }
278
+ }
279
+ var PRESETS = {
280
+ /**
281
+ * DeFi Trader preset:
282
+ * - Daily spending limit on ETH and stablecoins
283
+ * - Allowlist of approved DEX contracts
284
+ * - Emergency pause with owner as guardian
285
+ */
286
+ "defi-trader": (owner, params = {}) => {
287
+ validatePresetParams("defi-trader", params, {
288
+ dailyEthLimit: "bigint",
289
+ guardian: "Address",
290
+ allowedDexes: "Address[]"
291
+ });
292
+ return [
293
+ {
294
+ type: "spending-limit",
295
+ limits: [
296
+ {
297
+ token: NATIVE_TOKEN,
298
+ limit: params.dailyEthLimit ?? parseEther("1"),
299
+ window: WINDOW_1_DAY
300
+ },
301
+ ...params.stablecoinLimits ?? []
302
+ ]
303
+ },
304
+ // Only include allowlist if specific DEXes are provided;
305
+ // an empty allowlist in "allow" mode would block all transactions.
306
+ ...params.allowedDexes?.length ? [
307
+ {
308
+ type: "allowlist",
309
+ mode: "allow",
310
+ targets: params.allowedDexes.map((addr) => ({
311
+ address: addr
312
+ // Omit selector to use the wildcard (0x431e2cf5) — allows all
313
+ // function calls, not just ETH transfers. 0x00000000 is NOT a
314
+ // wildcard; it only matches empty calldata.
315
+ }))
316
+ }
317
+ ] : [],
318
+ {
319
+ type: "emergency-pause",
320
+ guardian: params.guardian ?? owner,
321
+ autoUnpauseAfter: WINDOW_1_DAY
322
+ }
323
+ ];
324
+ },
325
+ /**
326
+ * Treasury Agent preset:
327
+ * - Lower spending limits with longer windows
328
+ * - Emergency pause (manual only)
329
+ */
330
+ "treasury-agent": (owner, params = {}) => {
331
+ validatePresetParams("treasury-agent", params, {
332
+ weeklyEthLimit: "bigint",
333
+ guardian: "Address"
334
+ });
335
+ return [
336
+ {
337
+ type: "spending-limit",
338
+ limits: [
339
+ {
340
+ token: NATIVE_TOKEN,
341
+ limit: params.weeklyEthLimit ?? parseEther("5"),
342
+ window: WINDOW_1_WEEK
343
+ }
344
+ ]
345
+ },
346
+ {
347
+ type: "emergency-pause",
348
+ guardian: params.guardian ?? owner,
349
+ autoUnpauseAfter: 0
350
+ }
351
+ ];
352
+ },
353
+ /**
354
+ * Payment Agent preset:
355
+ * - Strict spending limits
356
+ * - Allowlist of approved recipients only
357
+ * - Emergency pause
358
+ */
359
+ "payment-agent": (owner, params = {}) => {
360
+ validatePresetParams("payment-agent", params, {
361
+ dailyLimit: "bigint",
362
+ guardian: "Address",
363
+ approvedRecipients: "Address[]"
364
+ });
365
+ return [
366
+ {
367
+ type: "spending-limit",
368
+ limits: [
369
+ {
370
+ token: NATIVE_TOKEN,
371
+ limit: params.dailyLimit ?? parseEther("0.1"),
372
+ window: WINDOW_1_DAY
373
+ }
374
+ ]
375
+ },
376
+ // Only include allowlist if specific recipients are provided;
377
+ // an empty allowlist in "allow" mode would block all transactions.
378
+ ...params.approvedRecipients?.length ? [
379
+ {
380
+ type: "allowlist",
381
+ mode: "allow",
382
+ targets: params.approvedRecipients.map(
383
+ (addr) => ({
384
+ address: addr
385
+ })
386
+ )
387
+ }
388
+ ] : [],
389
+ {
390
+ type: "emergency-pause",
391
+ guardian: params.guardian ?? owner,
392
+ autoUnpauseAfter: 3600
393
+ }
394
+ ];
395
+ },
396
+ /**
397
+ * Minimal preset:
398
+ * - Just emergency pause
399
+ * - For agents that need maximum flexibility with a kill switch
400
+ */
401
+ minimal: (owner, params = {}) => {
402
+ validatePresetParams("minimal", params, {
403
+ guardian: "Address"
404
+ });
405
+ return [
406
+ {
407
+ type: "emergency-pause",
408
+ guardian: params.guardian ?? owner,
409
+ autoUnpauseAfter: 0
410
+ }
411
+ ];
412
+ }
413
+ };
414
+
368
415
  // src/policies.ts
416
+ import { encodeAbiParameters, parseAbiParameters } from "viem";
369
417
  function encodePolicyInitData(policy, moduleAddresses, trustedForwarder = "0x0000000000000000000000000000000000000000") {
370
418
  const zeroAddress = "0x0000000000000000000000000000000000000000";
371
419
  switch (policy.type) {
@@ -480,11 +528,11 @@ var base_sepolia_default = {
480
528
  safe7579Launchpad: "0x7579011aB74c46090561ea277Ba79D510c6C00ff",
481
529
  rhinestoneAttester: "0x000000333034E9f539ce08819E12c1b8Cb29084d",
482
530
  hookMultiPlexer: "0xF6782ed057F95f334D04F0Af1Af4D14fb84DE549",
483
- spendingLimitHook: "0x0ea97ef2fc52700d1628110a8f411fefb0c0aa8b",
484
- allowlistHook: "0x61a2100072d03f66de6f7dd0dfc2f7aa5c91e777",
485
- emergencyPauseHook: "0xb8fdc9ee56cfb4077e132eff631b546fe6e79fec",
486
- automationExecutor: "0x729c29b35c396b907ed118f00fbe4d4bcc3a7f46",
487
- moduleSetupHelper: "0x4f1555baf4b3221c373094d47419596118828e41"
531
+ spendingLimitHook: "0xd67a5E6784A66Ddf4C09EA7bbf81Ab89532624E4",
532
+ allowlistHook: "0x7899b03386A3874393A3769D637977d947A9Fc66",
533
+ emergencyPauseHook: "0xAc0cE5672aED1Bfe5549b885f883a770Acd8cAE3",
534
+ automationExecutor: "0x795DB663b70bAe110C10694DF3f5D801b76E2b20",
535
+ moduleSetupHelper: "0xcBb12Eb2CF324B1C34160F72E037589CEEC35Fa0"
488
536
  };
489
537
 
490
538
  // src/deployments/sepolia.json
@@ -660,6 +708,11 @@ function getRemoveAction(permissionId) {
660
708
  // src/client.ts
661
709
  function resolveAccount(key) {
662
710
  if (typeof key === "string") {
711
+ if (!/^0x[0-9a-fA-F]{64}$/.test(key)) {
712
+ throw new WalletCreationError(
713
+ "Invalid private key format. Expected a 0x-prefixed 32-byte hex string."
714
+ );
715
+ }
663
716
  return privateKeyToAccount(key);
664
717
  }
665
718
  return mnemonicToAccount(key.mnemonic, {
@@ -710,7 +763,7 @@ var SmartAgentKitClient = class {
710
763
  const ownerAccount = resolveAccount(ownerKey);
711
764
  if (ownerAccount.address.toLowerCase() !== params.owner.toLowerCase()) {
712
765
  throw new WalletCreationError(
713
- `Owner address mismatch: key derives ${ownerAccount.address} but params.owner is ${params.owner}`
766
+ "Owner address does not match the provided signing key. Verify that the private key or mnemonic corresponds to the specified owner address."
714
767
  );
715
768
  }
716
769
  const hookModule = getHookMultiPlexer({
@@ -767,7 +820,7 @@ var SmartAgentKitClient = class {
767
820
  } catch (error) {
768
821
  if (error instanceof WalletCreationError) throw error;
769
822
  throw new WalletCreationError(
770
- error instanceof Error ? error.message : String(error),
823
+ "Wallet deployment failed. Check your RPC/bundler configuration and that the owner key is correct.",
771
824
  error
772
825
  );
773
826
  }
@@ -821,7 +874,13 @@ var SmartAgentKitClient = class {
821
874
  * the smart account via Smart Sessions. The session is scoped to
822
875
  * specific target contracts, function selectors, and time window.
823
876
  *
824
- * @returns The session key address, private key, and permission ID.
877
+ * @returns The session key address and permission ID.
878
+ *
879
+ * SECURITY: The session private key is intentionally NOT returned or stored
880
+ * by the SDK. The caller should use the `sessionKey` address to identify
881
+ * the session on-chain, and manage key material externally via a secure
882
+ * key management system. To use a pre-generated key pair, provide the
883
+ * session key address in `params.sessionKey`.
825
884
  */
826
885
  async createSession(wallet, params, ownerKey) {
827
886
  const client = this.getWalletClient(wallet.address);
@@ -874,7 +933,6 @@ var SmartAgentKitClient = class {
874
933
  walletSessions.push({
875
934
  permissionId,
876
935
  sessionKeyAddress: sessionAccount.address,
877
- sessionKeyPrivateKey: sessionPrivateKey,
878
936
  expiresAt: params.expiresAt,
879
937
  actions: params.actions.map((a) => ({
880
938
  target: a.target,
@@ -884,13 +942,12 @@ var SmartAgentKitClient = class {
884
942
  this.sessions.set(wallet.address, walletSessions);
885
943
  return {
886
944
  sessionKey: sessionAccount.address,
887
- privateKey: sessionPrivateKey,
888
945
  permissionId
889
946
  };
890
947
  } catch (error) {
891
948
  if (error instanceof SessionError) throw error;
892
949
  throw new SessionError(
893
- error instanceof Error ? error.message : String(error)
950
+ "Session creation failed. Check that the wallet is deployed and the owner key is correct."
894
951
  );
895
952
  }
896
953
  }
@@ -911,7 +968,7 @@ var SmartAgentKitClient = class {
911
968
  );
912
969
  } catch (error) {
913
970
  throw new SessionError(
914
- error instanceof Error ? error.message : String(error)
971
+ "Session revocation failed. Check the permission ID and wallet connection."
915
972
  );
916
973
  }
917
974
  }
@@ -935,6 +992,7 @@ var SmartAgentKitClient = class {
935
992
  * Hooks (spending limits, allowlist, pause) are enforced on-chain.
936
993
  */
937
994
  async execute(wallet, params) {
995
+ this.validateTransaction(params, wallet.address);
938
996
  const client = this.getWalletClient(wallet.address);
939
997
  try {
940
998
  const hash = await client.sendTransaction({
@@ -948,8 +1006,9 @@ var SmartAgentKitClient = class {
948
1006
  });
949
1007
  return hash;
950
1008
  } catch (error) {
1009
+ if (error instanceof ExecutionError) throw error;
951
1010
  throw new ExecutionError(
952
- error instanceof Error ? error.message : String(error),
1011
+ "Transaction failed. Check that the target, value, and calldata are correct.",
953
1012
  error
954
1013
  );
955
1014
  }
@@ -959,6 +1018,9 @@ var SmartAgentKitClient = class {
959
1018
  * All calls are encoded into a single UserOperation with batch mode.
960
1019
  */
961
1020
  async executeBatch(wallet, params) {
1021
+ for (const call of params.calls) {
1022
+ this.validateTransaction(call, wallet.address);
1023
+ }
962
1024
  const client = this.getWalletClient(wallet.address);
963
1025
  try {
964
1026
  const calls = params.calls.map((call) => ({
@@ -971,8 +1033,9 @@ var SmartAgentKitClient = class {
971
1033
  });
972
1034
  return hash;
973
1035
  } catch (error) {
1036
+ if (error instanceof ExecutionError) throw error;
974
1037
  throw new ExecutionError(
975
- error instanceof Error ? error.message : String(error),
1038
+ "Batch transaction failed. Check that all targets, values, and calldata are correct.",
976
1039
  error
977
1040
  );
978
1041
  }
@@ -1075,10 +1138,58 @@ var SmartAgentKitClient = class {
1075
1138
  }
1076
1139
  // ─── Private Helpers ──────────────────────────────────────────
1077
1140
  resolvePolicies(params) {
1141
+ let policies;
1078
1142
  if (params.preset) {
1079
- return PRESETS[params.preset](params.owner, params.presetParams);
1143
+ policies = PRESETS[params.preset](params.owner, params.presetParams);
1144
+ } else {
1145
+ policies = params.policies ?? [];
1080
1146
  }
1081
- return params.policies ?? [];
1147
+ if (policies.length > 0 && this.config.moduleAddresses) {
1148
+ const moduleAddresses = this.config.moduleAddresses;
1149
+ const infrastructureAddresses = [];
1150
+ if (moduleAddresses.spendingLimitHook) {
1151
+ infrastructureAddresses.push(moduleAddresses.spendingLimitHook);
1152
+ }
1153
+ if (moduleAddresses.allowlistHook) {
1154
+ infrastructureAddresses.push(moduleAddresses.allowlistHook);
1155
+ }
1156
+ if (moduleAddresses.emergencyPauseHook) {
1157
+ infrastructureAddresses.push(moduleAddresses.emergencyPauseHook);
1158
+ }
1159
+ if (moduleAddresses.automationExecutor) {
1160
+ infrastructureAddresses.push(moduleAddresses.automationExecutor);
1161
+ }
1162
+ infrastructureAddresses.push(HOOK_MULTIPLEXER_ADDRESS);
1163
+ infrastructureAddresses.push(SMART_SESSIONS_VALIDATOR);
1164
+ let hasAllowlist = false;
1165
+ for (const policy of policies) {
1166
+ if (policy.type === "allowlist") {
1167
+ hasAllowlist = true;
1168
+ const existing = new Set(
1169
+ (policy.protectedAddresses ?? []).map((a) => a.toLowerCase())
1170
+ );
1171
+ const merged = [...policy.protectedAddresses ?? []];
1172
+ for (const addr of infrastructureAddresses) {
1173
+ if (!existing.has(addr.toLowerCase())) {
1174
+ merged.push(addr);
1175
+ }
1176
+ }
1177
+ policy.protectedAddresses = merged;
1178
+ }
1179
+ }
1180
+ const hasHooks = policies.some(
1181
+ (p) => p.type === "spending-limit" || p.type === "emergency-pause"
1182
+ );
1183
+ if (hasHooks && !hasAllowlist) {
1184
+ policies.push({
1185
+ type: "allowlist",
1186
+ mode: "block",
1187
+ targets: [],
1188
+ protectedAddresses: infrastructureAddresses
1189
+ });
1190
+ }
1191
+ }
1192
+ return policies;
1082
1193
  }
1083
1194
  requireModuleAddresses(policies) {
1084
1195
  if (policies.length === 0) return void 0;
@@ -1136,7 +1247,7 @@ var SmartAgentKitClient = class {
1136
1247
  switch (policy.type) {
1137
1248
  case "spending-limit": {
1138
1249
  const hookAddress = moduleAddresses.spendingLimitHook;
1139
- const initData = encodeSpendingLimitInitData(policy);
1250
+ const initData = encodeSpendingLimitInitData(policy, hookMultiPlexerAddress);
1140
1251
  this.pushSubHookInitCalls(
1141
1252
  calls,
1142
1253
  hookAddress,
@@ -1147,7 +1258,7 @@ var SmartAgentKitClient = class {
1147
1258
  }
1148
1259
  case "allowlist": {
1149
1260
  const hookAddress = moduleAddresses.allowlistHook;
1150
- const initData = encodeAllowlistInitData(policy);
1261
+ const initData = encodeAllowlistInitData(policy, hookMultiPlexerAddress);
1151
1262
  this.pushSubHookInitCalls(
1152
1263
  calls,
1153
1264
  hookAddress,
@@ -1158,7 +1269,7 @@ var SmartAgentKitClient = class {
1158
1269
  }
1159
1270
  case "emergency-pause": {
1160
1271
  const hookAddress = moduleAddresses.emergencyPauseHook;
1161
- const initData = encodeEmergencyPauseInitData(policy);
1272
+ const initData = encodeEmergencyPauseInitData(policy, hookMultiPlexerAddress);
1162
1273
  this.pushSubHookInitCalls(
1163
1274
  calls,
1164
1275
  hookAddress,
@@ -1177,10 +1288,13 @@ var SmartAgentKitClient = class {
1177
1288
  });
1178
1289
  }
1179
1290
  /**
1180
- * Push the 3 calls needed to initialize a sub-hook:
1181
- * 1. onInstall(initData) on the sub-hook
1182
- * 2. setTrustedForwarder(multiplexer) on the sub-hook
1183
- * 3. addHook(hookAddr, GLOBAL) on the HookMultiPlexer
1291
+ * Push the 2 calls needed to initialize a sub-hook:
1292
+ * 1. onInstall(initData) on the sub-hook — sets trusted forwarder from init data
1293
+ * 2. addHook(hookAddr, GLOBAL) on the HookMultiPlexer
1294
+ *
1295
+ * Note: The trusted forwarder is now set during onInstall via the encoded init data
1296
+ * (passed as the first parameter). A separate setTrustedForwarder call is no longer
1297
+ * needed, reducing gas cost and batch size.
1184
1298
  */
1185
1299
  pushSubHookInitCalls(calls, hookAddress, initData, hookMultiPlexerAddress) {
1186
1300
  calls.push({
@@ -1192,15 +1306,6 @@ var SmartAgentKitClient = class {
1192
1306
  args: [initData]
1193
1307
  })
1194
1308
  });
1195
- calls.push({
1196
- to: hookAddress,
1197
- value: 0n,
1198
- data: encodeFunctionData({
1199
- abi: SET_TRUSTED_FORWARDER_ABI,
1200
- functionName: "setTrustedForwarder",
1201
- args: [hookMultiPlexerAddress]
1202
- })
1203
- });
1204
1309
  calls.push({
1205
1310
  to: hookMultiPlexerAddress,
1206
1311
  value: 0n,
@@ -1255,6 +1360,69 @@ var SmartAgentKitClient = class {
1255
1360
  }
1256
1361
  });
1257
1362
  }
1363
+ /**
1364
+ * Collect all known infrastructure addresses that must never be
1365
+ * targeted by agent-initiated transactions. This prevents an AI agent
1366
+ * from calling hook admin functions (setGuardian, clearTrustedForwarder,
1367
+ * removeSpendingLimit, removeHook, etc.) to weaken its own policy constraints.
1368
+ */
1369
+ getProtectedAddresses() {
1370
+ const addresses = /* @__PURE__ */ new Set();
1371
+ addresses.add(ENTRYPOINT_V07.toLowerCase());
1372
+ addresses.add(HOOK_MULTIPLEXER_ADDRESS.toLowerCase());
1373
+ addresses.add(SAFE_7579_MODULE.toLowerCase());
1374
+ addresses.add(SAFE_7579_LAUNCHPAD.toLowerCase());
1375
+ addresses.add(SMART_SESSIONS_VALIDATOR.toLowerCase());
1376
+ const moduleAddresses = this.config.moduleAddresses;
1377
+ if (moduleAddresses) {
1378
+ if (moduleAddresses.spendingLimitHook) {
1379
+ addresses.add(moduleAddresses.spendingLimitHook.toLowerCase());
1380
+ }
1381
+ if (moduleAddresses.allowlistHook) {
1382
+ addresses.add(moduleAddresses.allowlistHook.toLowerCase());
1383
+ }
1384
+ if (moduleAddresses.emergencyPauseHook) {
1385
+ addresses.add(moduleAddresses.emergencyPauseHook.toLowerCase());
1386
+ }
1387
+ if (moduleAddresses.automationExecutor) {
1388
+ addresses.add(moduleAddresses.automationExecutor.toLowerCase());
1389
+ }
1390
+ }
1391
+ return addresses;
1392
+ }
1393
+ /**
1394
+ * Validate a transaction before submission. Blocks calls to infrastructure
1395
+ * addresses and validates input parameters.
1396
+ *
1397
+ * @throws ExecutionError if the transaction targets a protected address
1398
+ * or has invalid parameters.
1399
+ */
1400
+ validateTransaction(params, walletAddress) {
1401
+ if (!isAddress2(params.target)) {
1402
+ throw new ExecutionError(
1403
+ `Invalid target address: "${params.target}". Expected a 0x-prefixed 20-byte hex address.`
1404
+ );
1405
+ }
1406
+ const protectedAddresses = this.getProtectedAddresses();
1407
+ if (protectedAddresses.has(params.target.toLowerCase())) {
1408
+ throw new ExecutionError(
1409
+ `Transaction blocked: target ${params.target} is a protected infrastructure contract. Agent wallets cannot call hook, multiplexer, or EntryPoint contracts directly.`
1410
+ );
1411
+ }
1412
+ if (walletAddress && params.target.toLowerCase() === walletAddress.toLowerCase()) {
1413
+ throw new ExecutionError(
1414
+ `Transaction blocked: target ${params.target} is the wallet's own address. Self-calls could be used to uninstall security modules.`
1415
+ );
1416
+ }
1417
+ if (params.value !== void 0 && params.value < 0n) {
1418
+ throw new ExecutionError("Transaction value cannot be negative.");
1419
+ }
1420
+ if (params.data && params.data !== "0x" && !isHex(params.data)) {
1421
+ throw new ExecutionError(
1422
+ `Invalid calldata: "${params.data}". Expected a 0x-prefixed hex string.`
1423
+ );
1424
+ }
1425
+ }
1258
1426
  getWalletClient(walletAddress) {
1259
1427
  const client = this.walletClients.get(walletAddress);
1260
1428
  if (!client) {
@@ -1309,4 +1477,3 @@ export {
1309
1477
  getRemoveAction as getRemoveSessionAction,
1310
1478
  getSmartSessionsModule
1311
1479
  };
1312
- //# sourceMappingURL=index.mjs.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartagentkit/sdk",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Deploy and manage policy-governed smart wallets for AI agents",
5
5
  "license": "MIT",
6
6
  "author": "SmartAgentKit Contributors",