@dev.sail.money/sailor 0.0.2-19 → 0.0.2-21

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 (193) hide show
  1. package/examples/permissions/BoundedApproveAndCallBatch.sol +179 -0
  2. package/examples/permissions/BoundedBet_Limitless_Base.sol +8 -7
  3. package/examples/permissions/BoundedBorrow_AaveV3_Arbitrum.sol +13 -13
  4. package/examples/permissions/BoundedPerp_GMXv2_Arbitrum.sol +50 -39
  5. package/examples/permissions/BoundedStake_Venice_Base.sol +85 -0
  6. package/examples/permissions/BoundedSupply_AaveV3_Arbitrum.sol +84 -0
  7. package/examples/permissions/BoundedSwap_UniswapV3_Base.sol +11 -9
  8. package/examples/permissions/BoundedSwap_UniswapV4_Unichain.sol +10 -8
  9. package/examples/permissions/BoundedTransfer_ERC20_Ethereum.sol +6 -6
  10. package/examples/permissions/BoundedVault_ERC4626_Base.sol +97 -0
  11. package/examples/permissions/README.md +29 -2
  12. package/examples/permissions/interfaces/IBatchPermission.sol +38 -0
  13. package/package.json +1 -1
  14. package/packages/cli/dist/index.cjs +1172 -588
  15. package/packages/cli/dist/server.cjs +139 -948
  16. package/packages/sdk/dist/index.d.ts +1 -1
  17. package/packages/sdk/dist/index.d.ts.map +1 -1
  18. package/packages/sdk/dist/index.js +1 -1
  19. package/packages/sdk/dist/index.js.map +1 -1
  20. package/packages/sdk/dist/intelligence.d.ts +1 -1
  21. package/packages/sdk/dist/intelligence.js +1 -1
  22. package/packages/sdk/dist/safe.d.ts +83 -0
  23. package/packages/sdk/dist/safe.d.ts.map +1 -1
  24. package/packages/sdk/dist/safe.js +92 -1
  25. package/packages/sdk/dist/safe.js.map +1 -1
  26. package/packages/ui/dist/assets/Arc-VDBY7LNS-BChRXCXW.js +1 -0
  27. package/packages/ui/dist/assets/Brave-BRAKJXDS-mq-Xo37j.js +1 -0
  28. package/packages/ui/dist/assets/Browser-76IHF3Y2-BMhRaC5Z.js +1 -0
  29. package/packages/ui/dist/assets/Chrome-65Q5P54Y-DR9MQEVr.js +1 -0
  30. package/packages/ui/dist/assets/Edge-XSPUTORV-DEoZslQE.js +1 -0
  31. package/packages/ui/dist/assets/Firefox-AAHGJQIP-Bp_Hm04m.js +1 -0
  32. package/packages/ui/dist/assets/Linux-OO4TNCLJ-B0aw93n9.js +1 -0
  33. package/packages/ui/dist/assets/Macos-MW4AE7LN-Vvm8Drw3.js +1 -0
  34. package/packages/ui/dist/assets/Opera-KQZLSACL-Cwv5MDFy.js +1 -0
  35. package/packages/ui/dist/assets/Safari-ZPL37GXR-C4Ggg6rz.js +1 -0
  36. package/packages/ui/dist/assets/Windows-PPTHQER6-BlyV2p7Y.js +1 -0
  37. package/packages/ui/dist/assets/add-BzRDG6go.js +15 -0
  38. package/packages/ui/dist/assets/all-wallets-C6juL2cm.js +6 -0
  39. package/packages/ui/dist/assets/apechain-SX5YFU6N-q5qBv-mp.js +1 -0
  40. package/packages/ui/dist/assets/app-store-DSJ1ow5G.js +17 -0
  41. package/packages/ui/dist/assets/apple-CUEgIX9k.js +18 -0
  42. package/packages/ui/dist/assets/ar_AR-LIPSOZP5-BQrIDibT.js +1519 -0
  43. package/packages/ui/dist/assets/arbitrum-WURIBY6W-CqVkHBr5.js +1 -0
  44. package/packages/ui/dist/assets/arrow-bottom-BOhBj1Je.js +8 -0
  45. package/packages/ui/dist/assets/arrow-bottom-circle-C4CzuHiR.js +11 -0
  46. package/packages/ui/dist/assets/arrow-left-BTD8zZ12.js +8 -0
  47. package/packages/ui/dist/assets/arrow-right-qcOukPwm.js +8 -0
  48. package/packages/ui/dist/assets/arrow-top-C0f2945G.js +8 -0
  49. package/packages/ui/dist/assets/assets-Q6ZU7ZJ5-P8HioiAD.js +1 -0
  50. package/packages/ui/dist/assets/avalanche-KOMJD3XY-Dsn_JPR4.js +1 -0
  51. package/packages/ui/dist/assets/bank-BSWzLP3R.js +14 -0
  52. package/packages/ui/dist/assets/base-OAXLRA4F-CoYTVIiL.js +1 -0
  53. package/packages/ui/dist/assets/base-QS6CYWIN-CsjdbWCf.js +1 -0
  54. package/packages/ui/dist/assets/basic-DGTBsmnG.js +2128 -0
  55. package/packages/ui/dist/assets/berachain-NJECWIVC-DumxnFvf.js +1 -0
  56. package/packages/ui/dist/assets/blast-V555OVXZ-BbhJh1tj.js +1 -0
  57. package/packages/ui/dist/assets/browser-DJNepafc.js +14 -0
  58. package/packages/ui/dist/assets/bsc-N647EYR2-B2nLKXWV.js +1 -0
  59. package/packages/ui/dist/assets/card-u08LJx43.js +14 -0
  60. package/packages/ui/dist/assets/ccip-DcWZjG37.js +1 -0
  61. package/packages/ui/dist/assets/celo-GEP4TUHG-CenIBYLU.js +1 -0
  62. package/packages/ui/dist/assets/checkmark-DVD-obDl.js +11 -0
  63. package/packages/ui/dist/assets/checkmark-bold-CSXDqIAx.js +8 -0
  64. package/packages/ui/dist/assets/chevron-bottom-Be7f0gi2.js +8 -0
  65. package/packages/ui/dist/assets/chevron-left-v8cgKRhQ.js +8 -0
  66. package/packages/ui/dist/assets/chevron-right-B0ux2X-3.js +8 -0
  67. package/packages/ui/dist/assets/chevron-top-De-a8tmA.js +8 -0
  68. package/packages/ui/dist/assets/chrome-store-BIIIRGPA.js +61 -0
  69. package/packages/ui/dist/assets/clock-8d5kvRPQ.js +8 -0
  70. package/packages/ui/dist/assets/close-BSSJkFv0.js +8 -0
  71. package/packages/ui/dist/assets/coinPlaceholder-CLJaQiUO.js +8 -0
  72. package/packages/ui/dist/assets/compass-CR1zP0b-.js +8 -0
  73. package/packages/ui/dist/assets/connect-UA7M4XW6-IY3X6Bmr.js +1 -0
  74. package/packages/ui/dist/assets/copy-CGkuIFo6.js +15 -0
  75. package/packages/ui/dist/assets/core-ea860JM2.js +907 -0
  76. package/packages/ui/dist/assets/create-FASO7PVG-D_rvSpre.js +1 -0
  77. package/packages/ui/dist/assets/cronos-HJPAQTAE-BEOvlOC4.js +1 -0
  78. package/packages/ui/dist/assets/cursor-DCRoxgSY.js +3 -0
  79. package/packages/ui/dist/assets/cursor-transparent-C8s5LY_P.js +12 -0
  80. package/packages/ui/dist/assets/de_DE-YE3KOFHU-BRt5ztUe.js +1519 -0
  81. package/packages/ui/dist/assets/degen-FQQ4XGHB-CeHTs88l.js +1 -0
  82. package/packages/ui/dist/assets/desktop-CQyixryE.js +9 -0
  83. package/packages/ui/dist/assets/disconnect-Ct0234l0.js +8 -0
  84. package/packages/ui/dist/assets/discord-haYPGMDl.js +17 -0
  85. package/packages/ui/dist/assets/es_419-7LMPU7G4-DH7rM0yQ.js +1519 -0
  86. package/packages/ui/dist/assets/ethereum-RGGVA4PY-SWGOlkuk.js +1 -0
  87. package/packages/ui/dist/assets/etherscan-BTLrS1KK.js +6 -0
  88. package/packages/ui/dist/assets/events-wdo_D3Zy.js +1 -0
  89. package/packages/ui/dist/assets/exclamation-triangle-CMlYpOat.js +4 -0
  90. package/packages/ui/dist/assets/extension-CNGBCbo8.js +8 -0
  91. package/packages/ui/dist/assets/external-link-DmYJSKcL.js +8 -0
  92. package/packages/ui/dist/assets/facebook-F0iMVTem.js +26 -0
  93. package/packages/ui/dist/assets/fallback-BqeFDEuW.js +1 -0
  94. package/packages/ui/dist/assets/farcaster-CYr9I6UA.js +12 -0
  95. package/packages/ui/dist/assets/filters-CxE97nqU.js +8 -0
  96. package/packages/ui/dist/assets/flow-5FQJFCTK-CUie2reO.js +1 -0
  97. package/packages/ui/dist/assets/fr_FR-VBJP3ZLL-B-_ocunw.js +1519 -0
  98. package/packages/ui/dist/assets/github-CjRht-Wv.js +18 -0
  99. package/packages/ui/dist/assets/gnosis-37ZC4RBL-B137OtHZ.js +1 -0
  100. package/packages/ui/dist/assets/google-3G0o4pR9.js +18 -0
  101. package/packages/ui/dist/assets/gravity-J5YQHTYH-Bj6B0uod.js +1 -0
  102. package/packages/ui/dist/assets/hardhat-TX56IT5N-CV1FY-wE.js +1 -0
  103. package/packages/ui/dist/assets/help-circle-C5gNChqZ.js +12 -0
  104. package/packages/ui/dist/assets/hi_IN-WBVD5XYI-D73g2UFs.js +1519 -0
  105. package/packages/ui/dist/assets/hyperevm-VKPAA4SA-CHwraEsx.js +1 -0
  106. package/packages/ui/dist/assets/id-C6_zK0Tb.js +12 -0
  107. package/packages/ui/dist/assets/id_ID-SBYANJ7G-Cjpa4ay6.js +1519 -0
  108. package/packages/ui/dist/assets/image-DsU8Irlu.js +4 -0
  109. package/packages/ui/dist/assets/index-BCb0Nju4.js +1775 -0
  110. package/packages/ui/dist/assets/index-CjvcQefO.js +1 -0
  111. package/packages/ui/dist/assets/index-D1lgDFZV.js +16 -0
  112. package/packages/ui/dist/assets/index-DDKDa0s2.css +1 -0
  113. package/packages/ui/dist/assets/index-DQ44LBvq.js +395 -0
  114. package/packages/ui/dist/assets/index-Drc17uEc.js +1 -0
  115. package/packages/ui/dist/assets/index-c8ZmMTds.js +1 -0
  116. package/packages/ui/dist/assets/index.es-BhDQmlR4.js +26 -0
  117. package/packages/ui/dist/assets/info-CAKKH6T2.js +3 -0
  118. package/packages/ui/dist/assets/info-circle-CHV1idfy.js +12 -0
  119. package/packages/ui/dist/assets/ink-FZMYZWHG-62p-5IK5.js +1 -0
  120. package/packages/ui/dist/assets/ja_JP-ZRMWJV3I-DXbifiMm.js +1519 -0
  121. package/packages/ui/dist/assets/kaia-65D2U3PU-JmuLQ4gC.js +1 -0
  122. package/packages/ui/dist/assets/ko_KR-FR54RFUG-upinSHjQ.js +1519 -0
  123. package/packages/ui/dist/assets/lightbulb-ew10LUMl.js +3 -0
  124. package/packages/ui/dist/assets/linea-QRMVQ5DY-DuI3vv0d.js +1 -0
  125. package/packages/ui/dist/assets/login-UP3DZBGS-Db_wM5oQ.js +1 -0
  126. package/packages/ui/dist/assets/mail-CACYeWXj.js +8 -0
  127. package/packages/ui/dist/assets/manta-SI27YFEJ-CpVOKa06.js +1 -0
  128. package/packages/ui/dist/assets/mantle-CKIUT334-DR2WgqzU.js +1 -0
  129. package/packages/ui/dist/assets/metaMaskWallet-EI6MED72-D5HFOsnz.js +1 -0
  130. package/packages/ui/dist/assets/metamask-sdk-BUYu4RDE.js +542 -0
  131. package/packages/ui/dist/assets/mobile-DX601q1y.js +9 -0
  132. package/packages/ui/dist/assets/monad-4KWC6TSS-DVXSkpiz.js +1 -0
  133. package/packages/ui/dist/assets/more-CQGeX45N.js +11 -0
  134. package/packages/ui/dist/assets/ms_MY-EZSGYYYQ-4cPLK-3L.js +1519 -0
  135. package/packages/ui/dist/assets/native-CJ5et6AR.js +1 -0
  136. package/packages/ui/dist/assets/network-placeholder-BQw8E4X-.js +14 -0
  137. package/packages/ui/dist/assets/nftPlaceholder-CifJ2CzA.js +8 -0
  138. package/packages/ui/dist/assets/off-B6oUArCZ.js +8 -0
  139. package/packages/ui/dist/assets/optimism-HAF2GUT7-ec6Nqxs9.js +1 -0
  140. package/packages/ui/dist/assets/parseSignature-DjWIdHkx.js +1 -0
  141. package/packages/ui/dist/assets/play-store-Kq51oh3r.js +32 -0
  142. package/packages/ui/dist/assets/plus-BxZxVpff.js +13 -0
  143. package/packages/ui/dist/assets/polygon-WW6ZI7PM-DXlmm4L1.js +1 -0
  144. package/packages/ui/dist/assets/pt_BR-JQFQ3P4L-DOHfdcA2.js +1519 -0
  145. package/packages/ui/dist/assets/qr-code-CSBFpyhP.js +6 -0
  146. package/packages/ui/dist/assets/rainbowWallet-O26YNBMX-DUhYus-9.js +1 -0
  147. package/packages/ui/dist/assets/recycle-horizontal-D0HyLkot.js +9 -0
  148. package/packages/ui/dist/assets/refresh-BAQo228i.js +8 -0
  149. package/packages/ui/dist/assets/refresh-S4T5V5GX-CwqIaaxK.js +1 -0
  150. package/packages/ui/dist/assets/reown-logo-DeUbwRp6.js +12 -0
  151. package/packages/ui/dist/assets/ronin-EMCPYXZT-N-QBHZdV.js +1 -0
  152. package/packages/ui/dist/assets/ru_RU-Z42UEJBP-Cvb2oWxQ.js +1519 -0
  153. package/packages/ui/dist/assets/safeWallet-5MNKTR5Z-D-5imDLD.js +1 -0
  154. package/packages/ui/dist/assets/sanko-RHQYXGM5-OX010CbN.js +1 -0
  155. package/packages/ui/dist/assets/scan-4UYSQ56Q-CjMz6-XC.js +1 -0
  156. package/packages/ui/dist/assets/scroll-5OBGQVOV-DJFECiai.js +1 -0
  157. package/packages/ui/dist/assets/search-C8Sd0Mpz.js +8 -0
  158. package/packages/ui/dist/assets/secp256k1-BujG3JoP.js +1 -0
  159. package/packages/ui/dist/assets/send-B0JwUp6Q.js +15 -0
  160. package/packages/ui/dist/assets/sign-A7IJEUT5-CGsRnPrd.js +1 -0
  161. package/packages/ui/dist/assets/superposition-HG6MMR2Y-bRkgatRO.js +1 -0
  162. package/packages/ui/dist/assets/swapHorizontal-B2k5yDqc.js +8 -0
  163. package/packages/ui/dist/assets/swapHorizontalBold-yQqq0yPi.js +8 -0
  164. package/packages/ui/dist/assets/swapHorizontalMedium-NafYmdDj.js +16 -0
  165. package/packages/ui/dist/assets/swapHorizontalRoundedBold-BUn0GwEK.js +8 -0
  166. package/packages/ui/dist/assets/swapVertical-Be1m6suD.js +8 -0
  167. package/packages/ui/dist/assets/telegram-D-u7QHT5.js +16 -0
  168. package/packages/ui/dist/assets/th_TH-4YB4VSB2-BUipNP-V.js +1519 -0
  169. package/packages/ui/dist/assets/three-dots-B6Y4DFOR.js +5 -0
  170. package/packages/ui/dist/assets/tr_TR-5FKHPPIO-D5jTpIm9.js +1519 -0
  171. package/packages/ui/dist/assets/twitch-DSy1rhWQ.js +18 -0
  172. package/packages/ui/dist/assets/twitterIcon-tac1plSa.js +6 -0
  173. package/packages/ui/dist/assets/uk_UA-ZD4IBC52-DgnQrpzl.js +1519 -0
  174. package/packages/ui/dist/assets/unichain-C5BWO2ZY-BfguYsnu.js +1 -0
  175. package/packages/ui/dist/assets/verify-6MylluBY.js +8 -0
  176. package/packages/ui/dist/assets/verify-filled-CZc0otb8.js +8 -0
  177. package/packages/ui/dist/assets/vi_VN-5EVRZKLY-x078672g.js +1519 -0
  178. package/packages/ui/dist/assets/w3m-modal-DbT03Pyz.js +642 -0
  179. package/packages/ui/dist/assets/wallet-473-ObZE.js +8 -0
  180. package/packages/ui/dist/assets/wallet-placeholder-B9h3WvTk.js +14 -0
  181. package/packages/ui/dist/assets/walletConnectWallet-YHWKVTDY-D3lyiczV.js +1 -0
  182. package/packages/ui/dist/assets/walletconnect-5GHIf5FR.js +30 -0
  183. package/packages/ui/dist/assets/warning-circle-8A009Dx3.js +12 -0
  184. package/packages/ui/dist/assets/x-B4jK8e8X.js +12 -0
  185. package/packages/ui/dist/assets/xdc-KJ3TDBYO-DNV6zchh.js +1 -0
  186. package/packages/ui/dist/assets/zetachain-TLDS5IPW-Udhyw16T.js +1 -0
  187. package/packages/ui/dist/assets/zh_CN-4XK5YJPR-Bt6Yz5Ek.js +1519 -0
  188. package/packages/ui/dist/assets/zh_HK-N4YN2WSI-Cvzl1V16.js +1519 -0
  189. package/packages/ui/dist/assets/zh_TW-CNCRXH6Z-BNelatfN.js +1519 -0
  190. package/packages/ui/dist/assets/zksync-DH7HK5U4-Dt4usFw6.js +1 -0
  191. package/packages/ui/dist/assets/zora-FYL5H3IO-iB4wygST.js +1 -0
  192. package/packages/ui/dist/index.html +14 -0
  193. package/templates/default/AGENTS.md +18 -1
@@ -35278,11 +35278,6 @@ function getChain(chainId) {
35278
35278
  return config;
35279
35279
  }
35280
35280
 
35281
- // src/lib/io.ts
35282
- var import_node_fs2 = __toESM(require("node:fs"), 1);
35283
- var import_node_path = __toESM(require("node:path"), 1);
35284
- var import_node_readline = require("node:readline");
35285
-
35286
35281
  // ../sdk/dist/client.js
35287
35282
  init_esm2();
35288
35283
 
@@ -37727,6 +37722,46 @@ function buildSafeSetupInitializer(params) {
37727
37722
  function buildApprovedHashSignature(owner2) {
37728
37723
  return encodePacked(["bytes32", "bytes32", "uint8"], [pad(owner2, { size: 32 }), pad("0x", { size: 32 }), 1]);
37729
37724
  }
37725
+ var safeProxyFactoryAbi = [
37726
+ {
37727
+ type: "function",
37728
+ name: "proxyCreationCode",
37729
+ stateMutability: "pure",
37730
+ inputs: [],
37731
+ outputs: [{ name: "", type: "bytes" }]
37732
+ }
37733
+ ];
37734
+ function computeSafeProxyAddress(params) {
37735
+ const { initializer, saltNonce, proxyCreationCode } = params;
37736
+ const initCodeHash = keccak256(concat([
37737
+ proxyCreationCode,
37738
+ encodeAbiParameters([{ type: "address" }], [SAFE_V141.singletonL2])
37739
+ ]));
37740
+ const salt = keccak256(encodePacked(["bytes32", "uint256"], [keccak256(initializer), saltNonce]));
37741
+ return getCreate2Address({
37742
+ from: SAFE_V141.proxyFactory,
37743
+ salt,
37744
+ bytecodeHash: initCodeHash
37745
+ });
37746
+ }
37747
+ function computeKernelBoundSalt(params) {
37748
+ const { saltNonce, deployer, permissionSigner, manager, feePolicy } = params;
37749
+ return BigInt(keccak256(encodeAbiParameters([
37750
+ { type: "uint256" },
37751
+ { type: "address" },
37752
+ { type: "address" },
37753
+ { type: "address" },
37754
+ { type: "address" }
37755
+ ], [saltNonce, deployer, permissionSigner, manager, feePolicy])));
37756
+ }
37757
+ function computeSailSmaAddress(params) {
37758
+ const boundSalt = computeKernelBoundSalt(params);
37759
+ return computeSafeProxyAddress({
37760
+ initializer: params.initializer,
37761
+ saltNonce: boundSalt,
37762
+ proxyCreationCode: params.proxyCreationCode
37763
+ });
37764
+ }
37730
37765
  function encodeSetManager(newManager) {
37731
37766
  return encodeFunctionData({
37732
37767
  abi: setManagerAbi,
@@ -37832,195 +37867,551 @@ async function estimatePermissionFee(publicClient, governance, permission) {
37832
37867
  var SAIL_INTELLIGENCE_BASE_URL = "https://api.sail.money";
37833
37868
  var SAIL_INTELLIGENCE_DOCS_URL = "https://api.sail.money/docs";
37834
37869
 
37835
- // src/lib/io.ts
37870
+ // src/commands/account.ts
37836
37871
  init_esm2();
37837
- function sailDir() {
37838
- return import_node_path.default.join(process.cwd(), ".sail");
37839
- }
37840
- function sailPath(...segments) {
37841
- return import_node_path.default.join(sailDir(), ...segments);
37842
- }
37843
- function readJsonFile(filePath) {
37844
- try {
37845
- return JSON.parse(import_node_fs2.default.readFileSync(filePath, "utf-8"));
37846
- } catch {
37847
- return null;
37872
+
37873
+ // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/op-stack/contracts.js
37874
+ var contracts = {
37875
+ gasPriceOracle: { address: "0x420000000000000000000000000000000000000F" },
37876
+ l1Block: { address: "0x4200000000000000000000000000000000000015" },
37877
+ l2CrossDomainMessenger: {
37878
+ address: "0x4200000000000000000000000000000000000007"
37879
+ },
37880
+ l2Erc721Bridge: { address: "0x4200000000000000000000000000000000000014" },
37881
+ l2StandardBridge: { address: "0x4200000000000000000000000000000000000010" },
37882
+ l2ToL1MessagePasser: {
37883
+ address: "0x4200000000000000000000000000000000000016"
37848
37884
  }
37885
+ };
37886
+
37887
+ // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/op-stack/formatters.js
37888
+ init_fromHex();
37889
+ init_block2();
37890
+ init_transaction2();
37891
+ init_transactionReceipt();
37892
+ var formatters = {
37893
+ block: /* @__PURE__ */ defineBlock({
37894
+ format(args) {
37895
+ const transactions = args.transactions?.map((transaction) => {
37896
+ if (typeof transaction === "string")
37897
+ return transaction;
37898
+ const formatted = formatTransaction(transaction);
37899
+ if (formatted.typeHex === "0x7e") {
37900
+ formatted.isSystemTx = transaction.isSystemTx;
37901
+ formatted.mint = transaction.mint ? hexToBigInt(transaction.mint) : void 0;
37902
+ formatted.sourceHash = transaction.sourceHash;
37903
+ formatted.type = "deposit";
37904
+ }
37905
+ return formatted;
37906
+ });
37907
+ return {
37908
+ transactions,
37909
+ stateRoot: args.stateRoot
37910
+ };
37911
+ }
37912
+ }),
37913
+ transaction: /* @__PURE__ */ defineTransaction({
37914
+ format(args) {
37915
+ const transaction = {};
37916
+ if (args.type === "0x7e") {
37917
+ transaction.isSystemTx = args.isSystemTx;
37918
+ transaction.mint = args.mint ? hexToBigInt(args.mint) : void 0;
37919
+ transaction.sourceHash = args.sourceHash;
37920
+ transaction.type = "deposit";
37921
+ }
37922
+ return transaction;
37923
+ }
37924
+ }),
37925
+ transactionReceipt: /* @__PURE__ */ defineTransactionReceipt({
37926
+ format(args) {
37927
+ return {
37928
+ l1GasPrice: args.l1GasPrice ? hexToBigInt(args.l1GasPrice) : null,
37929
+ l1GasUsed: args.l1GasUsed ? hexToBigInt(args.l1GasUsed) : null,
37930
+ l1Fee: args.l1Fee ? hexToBigInt(args.l1Fee) : null,
37931
+ l1FeeScalar: args.l1FeeScalar ? Number(args.l1FeeScalar) : null
37932
+ };
37933
+ }
37934
+ })
37935
+ };
37936
+
37937
+ // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/op-stack/serializers.js
37938
+ init_address();
37939
+ init_isAddress();
37940
+ init_concat();
37941
+ init_toHex();
37942
+ init_toRlp();
37943
+ init_serializeTransaction();
37944
+ function serializeTransaction2(transaction, signature) {
37945
+ if (isDeposit(transaction))
37946
+ return serializeTransactionDeposit(transaction);
37947
+ return serializeTransaction(transaction, signature);
37849
37948
  }
37850
- function writeJsonFile(filePath, data) {
37851
- import_node_fs2.default.mkdirSync(import_node_path.default.dirname(filePath), { recursive: true });
37852
- import_node_fs2.default.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}
37853
- `);
37949
+ var serializers = {
37950
+ transaction: serializeTransaction2
37951
+ };
37952
+ function serializeTransactionDeposit(transaction) {
37953
+ assertTransactionDeposit(transaction);
37954
+ const { sourceHash, data, from: from14, gas, isSystemTx, mint, to, value } = transaction;
37955
+ const serializedTransaction = [
37956
+ sourceHash,
37957
+ from14,
37958
+ to ?? "0x",
37959
+ mint ? toHex(mint) : "0x",
37960
+ value ? toHex(value) : "0x",
37961
+ gas ? toHex(gas) : "0x",
37962
+ isSystemTx ? "0x1" : "0x",
37963
+ data ?? "0x"
37964
+ ];
37965
+ return concatHex([
37966
+ "0x7e",
37967
+ toRlp(serializedTransaction)
37968
+ ]);
37854
37969
  }
37855
- function fileExists(filePath) {
37856
- return import_node_fs2.default.existsSync(filePath);
37970
+ function isDeposit(transaction) {
37971
+ if (transaction.type === "deposit")
37972
+ return true;
37973
+ if (typeof transaction.sourceHash !== "undefined")
37974
+ return true;
37975
+ return false;
37857
37976
  }
37858
- function nowIso() {
37859
- return `${(/* @__PURE__ */ new Date()).toISOString().slice(0, 19)}Z`;
37977
+ function assertTransactionDeposit(transaction) {
37978
+ const { from: from14, to } = transaction;
37979
+ if (from14 && !isAddress(from14))
37980
+ throw new InvalidAddressError({ address: from14 });
37981
+ if (to && !isAddress(to))
37982
+ throw new InvalidAddressError({ address: to });
37860
37983
  }
37861
- function appendActivity(event, baseSailDir = sailDir()) {
37862
- const filePath = import_node_path.default.join(baseSailDir, "activity.jsonl");
37863
- import_node_fs2.default.mkdirSync(import_node_path.default.dirname(filePath), { recursive: true });
37864
- let tagged = event;
37865
- if (!("safe" in event)) {
37866
- try {
37867
- const account2 = JSON.parse(
37868
- import_node_fs2.default.readFileSync(import_node_path.default.join(baseSailDir, "account.json"), "utf-8")
37869
- );
37870
- if (account2?.safe) tagged = { ...event, safe: account2.safe };
37871
- } catch {
37984
+
37985
+ // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/op-stack/chainConfig.js
37986
+ var chainConfig = {
37987
+ blockTime: 2e3,
37988
+ contracts,
37989
+ formatters,
37990
+ serializers
37991
+ };
37992
+
37993
+ // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/chains/definitions/arbitrum.js
37994
+ init_defineChain();
37995
+ var arbitrum = /* @__PURE__ */ defineChain({
37996
+ id: 42161,
37997
+ name: "Arbitrum One",
37998
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
37999
+ blockTime: 250,
38000
+ rpcUrls: {
38001
+ default: {
38002
+ http: ["https://arb1.arbitrum.io/rpc"]
37872
38003
  }
37873
- }
37874
- import_node_fs2.default.appendFileSync(filePath, `${JSON.stringify(tagged)}
37875
- `);
37876
- }
37877
- function parseEnvFile(filePath) {
37878
- const out = {};
37879
- if (!import_node_fs2.default.existsSync(filePath)) return out;
37880
- for (const rawLine of import_node_fs2.default.readFileSync(filePath, "utf-8").split("\n")) {
37881
- const line = rawLine.trim();
37882
- if (line === "" || line.startsWith("#")) continue;
37883
- const eq = line.indexOf("=");
37884
- if (eq < 0) continue;
37885
- const key = line.slice(0, eq).trim();
37886
- let value = line.slice(eq + 1).trim();
37887
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
37888
- value = value.slice(1, -1);
38004
+ },
38005
+ blockExplorers: {
38006
+ default: {
38007
+ name: "Arbiscan",
38008
+ url: "https://arbiscan.io",
38009
+ apiUrl: "https://api.arbiscan.io/api"
38010
+ }
38011
+ },
38012
+ contracts: {
38013
+ multicall3: {
38014
+ address: "0xca11bde05977b3631167028862be2a173976ca11",
38015
+ blockCreated: 7654707
37889
38016
  }
37890
- out[key] = value;
37891
- }
37892
- return out;
37893
- }
37894
- function checksum4(address) {
37895
- return getAddress(address);
37896
- }
37897
- function makeClient(chainId) {
37898
- const env = parseEnvFile(sailPath(".env.local"));
37899
- const rpcUrl = env["RPC_URL"] ?? process.env["RPC_URL"] ?? "http://localhost:8545";
37900
- return new SailorClient({ rpcUrl, chainId });
37901
- }
37902
- var sharedRl = null;
37903
- var muted = false;
37904
- var lineQueue = [];
37905
- var waiters = [];
37906
- var inputClosed = false;
37907
- function getRl() {
37908
- if (sharedRl) return sharedRl;
37909
- const isTty = process.stdin.isTTY === true;
37910
- const rl = (0, import_node_readline.createInterface)({ input: process.stdin, output: process.stdout, terminal: isTty });
37911
- if (isTty) {
37912
- const muteable = rl;
37913
- const original = muteable._writeToOutput.bind(rl);
37914
- muteable._writeToOutput = (text) => {
37915
- if (!muted) original(text);
37916
- };
37917
38017
  }
37918
- rl.on("line", (line) => {
37919
- const waiter = waiters.shift();
37920
- if (waiter) waiter(line);
37921
- else lineQueue.push(line);
37922
- });
37923
- rl.on("close", () => {
37924
- inputClosed = true;
37925
- while (waiters.length > 0) {
37926
- const waiter = waiters.shift();
37927
- waiter?.("");
38018
+ });
38019
+
38020
+ // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/chains/definitions/base.js
38021
+ init_defineChain();
38022
+ var sourceId = 1;
38023
+ var base = /* @__PURE__ */ defineChain({
38024
+ ...chainConfig,
38025
+ id: 8453,
38026
+ name: "Base",
38027
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
38028
+ rpcUrls: {
38029
+ default: {
38030
+ http: ["https://mainnet.base.org"]
37928
38031
  }
37929
- });
37930
- sharedRl = rl;
37931
- return rl;
37932
- }
37933
- function ask(query) {
37934
- getRl();
37935
- process.stdout.write(query);
37936
- return new Promise((resolve3) => {
37937
- const buffered = lineQueue.shift();
37938
- if (buffered !== void 0) {
37939
- resolve3(buffered);
37940
- } else if (inputClosed) {
37941
- resolve3("");
37942
- } else {
37943
- waiters.push(resolve3);
38032
+ },
38033
+ blockExplorers: {
38034
+ default: {
38035
+ name: "Basescan",
38036
+ url: "https://basescan.org",
38037
+ apiUrl: "https://api.basescan.org/api"
38038
+ }
38039
+ },
38040
+ contracts: {
38041
+ ...chainConfig.contracts,
38042
+ disputeGameFactory: {
38043
+ [sourceId]: {
38044
+ address: "0x43edB88C4B80fDD2AdFF2412A7BebF9dF42cB40e"
38045
+ }
38046
+ },
38047
+ l2OutputOracle: {
38048
+ [sourceId]: {
38049
+ address: "0x56315b90c40730925ec5485cf004d835058518A0"
38050
+ }
38051
+ },
38052
+ multicall3: {
38053
+ address: "0xca11bde05977b3631167028862be2a173976ca11",
38054
+ blockCreated: 5022
38055
+ },
38056
+ portal: {
38057
+ [sourceId]: {
38058
+ address: "0x49048044D57e1C92A77f79988d21Fa8fAF74E97e",
38059
+ blockCreated: 17482143
38060
+ }
38061
+ },
38062
+ l1StandardBridge: {
38063
+ [sourceId]: {
38064
+ address: "0x3154Cf16ccdb4C6d922629664174b904d80F2C35",
38065
+ blockCreated: 17482143
38066
+ }
38067
+ }
38068
+ },
38069
+ sourceId
38070
+ });
38071
+ var basePreconf = /* @__PURE__ */ defineChain({
38072
+ ...base,
38073
+ experimental_preconfirmationTime: 200,
38074
+ rpcUrls: {
38075
+ default: {
38076
+ http: ["https://mainnet-preconf.base.org"]
37944
38077
  }
37945
- });
37946
- }
37947
- function closePrompts() {
37948
- if (sharedRl) {
37949
- sharedRl.close();
37950
- sharedRl = null;
37951
- }
37952
- }
37953
- async function prompt(question, def) {
37954
- const suffix = def !== void 0 ? ` (${def})` : "";
37955
- const answer = await ask(`${question}${suffix}: `);
37956
- const trimmed = answer.trim();
37957
- return trimmed === "" && def !== void 0 ? def : trimmed;
37958
- }
37959
- async function confirm(question) {
37960
- const answer = (await prompt(`${question} (y/N)`)).toLowerCase();
37961
- return answer === "y" || answer === "yes";
37962
- }
37963
- async function promptAddress(label, def) {
37964
- for (let attempt = 0; attempt < 5; attempt++) {
37965
- const raw = await prompt(label, def);
37966
- if (isAddress(raw)) return getAddress(raw);
37967
- console.log(` "${raw}" is not a valid EVM address \u2014 try again.`);
37968
- }
37969
- throw new Error(`No valid address provided for: ${label}`);
37970
- }
37971
- async function promptHidden(question) {
37972
- const isTty = process.stdin.isTTY === true;
37973
- if (isTty) muted = true;
37974
- const answer = await ask(`${question}: `);
37975
- if (isTty) {
37976
- muted = false;
37977
- process.stdout.write("\n");
37978
38078
  }
37979
- return answer;
37980
- }
38079
+ });
37981
38080
 
37982
- // src/lib/keys.ts
37983
- var ROLES = ["manager", "permissionSigner"];
37984
- function normalizeRole(input) {
37985
- const n = input.trim().toLowerCase().replace(/[-_\s]/g, "");
37986
- if (n === "manager" || n === "mgr" || n === "m" || n === "agent" || n === "agentwallet") {
37987
- return "manager";
37988
- }
37989
- if (n === "permissionsigner" || n === "signer" || n === "ps" || n === "permission" || n === "mandatesigner" || n === "mandate") {
37990
- return "permissionSigner";
38081
+ // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/chains/definitions/baseSepolia.js
38082
+ init_defineChain();
38083
+ var sourceId2 = 11155111;
38084
+ var baseSepolia = /* @__PURE__ */ defineChain({
38085
+ ...chainConfig,
38086
+ id: 84532,
38087
+ network: "base-sepolia",
38088
+ name: "Base Sepolia",
38089
+ nativeCurrency: { name: "Sepolia Ether", symbol: "ETH", decimals: 18 },
38090
+ rpcUrls: {
38091
+ default: {
38092
+ http: ["https://sepolia.base.org"]
38093
+ }
38094
+ },
38095
+ blockExplorers: {
38096
+ default: {
38097
+ name: "Basescan",
38098
+ url: "https://sepolia.basescan.org",
38099
+ apiUrl: "https://api-sepolia.basescan.org/api"
38100
+ }
38101
+ },
38102
+ contracts: {
38103
+ ...chainConfig.contracts,
38104
+ disputeGameFactory: {
38105
+ [sourceId2]: {
38106
+ address: "0xd6E6dBf4F7EA0ac412fD8b65ED297e64BB7a06E1"
38107
+ }
38108
+ },
38109
+ l2OutputOracle: {
38110
+ [sourceId2]: {
38111
+ address: "0x84457ca9D0163FbC4bbfe4Dfbb20ba46e48DF254"
38112
+ }
38113
+ },
38114
+ portal: {
38115
+ [sourceId2]: {
38116
+ address: "0x49f53e41452c74589e85ca1677426ba426459e85",
38117
+ blockCreated: 4446677
38118
+ }
38119
+ },
38120
+ l1StandardBridge: {
38121
+ [sourceId2]: {
38122
+ address: "0xfd0Bf71F60660E2f608ed56e1659C450eB113120",
38123
+ blockCreated: 4446677
38124
+ }
38125
+ },
38126
+ multicall3: {
38127
+ address: "0xca11bde05977b3631167028862be2a173976ca11",
38128
+ blockCreated: 1059647
38129
+ }
38130
+ },
38131
+ testnet: true,
38132
+ sourceId: sourceId2
38133
+ });
38134
+ var baseSepoliaPreconf = /* @__PURE__ */ defineChain({
38135
+ ...baseSepolia,
38136
+ experimental_preconfirmationTime: 200,
38137
+ rpcUrls: {
38138
+ default: {
38139
+ http: ["https://sepolia-preconf.base.org"]
38140
+ }
37991
38141
  }
37992
- return null;
37993
- }
37994
- function roleLabel(role) {
37995
- return role === "manager" ? "agent wallet" : "mandate signer";
38142
+ });
38143
+
38144
+ // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/chains/definitions/unichain.js
38145
+ init_defineChain();
38146
+ var sourceId3 = 1;
38147
+ var unichain = /* @__PURE__ */ defineChain({
38148
+ ...chainConfig,
38149
+ id: 130,
38150
+ name: "Unichain",
38151
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
38152
+ blockTime: 1e3,
38153
+ rpcUrls: {
38154
+ default: {
38155
+ http: ["https://mainnet.unichain.org/"]
38156
+ }
38157
+ },
38158
+ blockExplorers: {
38159
+ default: {
38160
+ name: "Uniscan",
38161
+ url: "https://uniscan.xyz",
38162
+ apiUrl: "https://api.uniscan.xyz/api"
38163
+ }
38164
+ },
38165
+ contracts: {
38166
+ ...chainConfig.contracts,
38167
+ multicall3: {
38168
+ address: "0xca11bde05977b3631167028862be2a173976ca11",
38169
+ blockCreated: 0
38170
+ },
38171
+ disputeGameFactory: {
38172
+ [sourceId3]: {
38173
+ address: "0x2F12d621a16e2d3285929C9996f478508951dFe4"
38174
+ }
38175
+ },
38176
+ portal: {
38177
+ [sourceId3]: {
38178
+ address: "0x0bd48f6B86a26D3a217d0Fa6FfE2B491B956A7a2"
38179
+ }
38180
+ },
38181
+ l1StandardBridge: {
38182
+ [sourceId3]: {
38183
+ address: "0x81014F44b0a345033bB2b3B21C7a1A308B35fEeA"
38184
+ }
38185
+ }
38186
+ },
38187
+ sourceId: sourceId3
38188
+ });
38189
+
38190
+ // src/lib/io.ts
38191
+ var import_node_fs2 = __toESM(require("node:fs"), 1);
38192
+ var import_node_path = __toESM(require("node:path"), 1);
38193
+ var import_node_readline = require("node:readline");
38194
+ init_esm2();
38195
+ function sailDir() {
38196
+ return import_node_path.default.join(process.cwd(), ".sail");
37996
38197
  }
37997
- function safeHex(safe) {
37998
- return safe.toLowerCase().replace(/^0x/, "").replace(/[^0-9a-f]/g, "");
38198
+ function sailPath(...segments) {
38199
+ return import_node_path.default.join(sailDir(), ...segments);
37999
38200
  }
38000
- function keyPath(role, safe) {
38001
- if (safe) {
38002
- return sailPath("keys", `${role}-0x${safeHex(safe)}.json`);
38201
+ function readJsonFile(filePath) {
38202
+ try {
38203
+ return JSON.parse(import_node_fs2.default.readFileSync(filePath, "utf-8"));
38204
+ } catch {
38205
+ return null;
38003
38206
  }
38004
- return sailPath("keys", `${role}.json`);
38005
38207
  }
38006
- function legacyKeyPath(role, safe) {
38007
- return sailPath("keys", `${role}-${safeHex(safe)}.json`);
38208
+ function writeJsonFile(filePath, data) {
38209
+ import_node_fs2.default.mkdirSync(import_node_path.default.dirname(filePath), { recursive: true });
38210
+ import_node_fs2.default.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}
38211
+ `);
38008
38212
  }
38009
- function resolveKeyPath(role, safe) {
38010
- if (safe) {
38011
- const perSma = keyPath(role, safe);
38012
- if (fileExists(perSma)) return perSma;
38013
- const legacy = legacyKeyPath(role, safe);
38014
- if (fileExists(legacy)) return legacy;
38015
- }
38016
- return keyPath(role);
38213
+ function fileExists(filePath) {
38214
+ return import_node_fs2.default.existsSync(filePath);
38017
38215
  }
38018
- function keyExists(role, safe) {
38019
- return fileExists(resolveKeyPath(role, safe));
38216
+ function nowIso() {
38217
+ return `${(/* @__PURE__ */ new Date()).toISOString().slice(0, 19)}Z`;
38020
38218
  }
38021
- async function loadKeyring(role, safe) {
38022
- const keystore = readJsonFile(resolveKeyPath(role, safe));
38023
- if (!keystore) {
38219
+ function appendActivity(event, baseSailDir = sailDir()) {
38220
+ const filePath = import_node_path.default.join(baseSailDir, "activity.jsonl");
38221
+ import_node_fs2.default.mkdirSync(import_node_path.default.dirname(filePath), { recursive: true });
38222
+ let tagged = event;
38223
+ if (!("safe" in event)) {
38224
+ try {
38225
+ const account2 = JSON.parse(
38226
+ import_node_fs2.default.readFileSync(import_node_path.default.join(baseSailDir, "account.json"), "utf-8")
38227
+ );
38228
+ if (account2?.safe) tagged = { ...event, safe: account2.safe };
38229
+ } catch {
38230
+ }
38231
+ }
38232
+ import_node_fs2.default.appendFileSync(filePath, `${JSON.stringify(tagged)}
38233
+ `);
38234
+ }
38235
+ function parseEnvFile(filePath) {
38236
+ const out = {};
38237
+ if (!import_node_fs2.default.existsSync(filePath)) return out;
38238
+ for (const rawLine of import_node_fs2.default.readFileSync(filePath, "utf-8").split("\n")) {
38239
+ const line = rawLine.trim();
38240
+ if (line === "" || line.startsWith("#")) continue;
38241
+ const eq = line.indexOf("=");
38242
+ if (eq < 0) continue;
38243
+ const key = line.slice(0, eq).trim();
38244
+ let value = line.slice(eq + 1).trim();
38245
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
38246
+ value = value.slice(1, -1);
38247
+ }
38248
+ out[key] = value;
38249
+ }
38250
+ return out;
38251
+ }
38252
+ function checksum4(address) {
38253
+ return getAddress(address);
38254
+ }
38255
+ function makeClient(chainId) {
38256
+ const env = parseEnvFile(sailPath(".env.local"));
38257
+ const rpcUrl = env["RPC_URL"] ?? process.env["RPC_URL"] ?? "http://localhost:8545";
38258
+ return new SailorClient({ rpcUrl, chainId });
38259
+ }
38260
+ var sharedRl = null;
38261
+ var muted = false;
38262
+ var lineQueue = [];
38263
+ var waiters = [];
38264
+ var inputClosed = false;
38265
+ function getRl() {
38266
+ if (sharedRl) return sharedRl;
38267
+ const isTty = process.stdin.isTTY === true;
38268
+ const rl = (0, import_node_readline.createInterface)({ input: process.stdin, output: process.stdout, terminal: isTty });
38269
+ if (isTty) {
38270
+ const muteable = rl;
38271
+ const original = muteable._writeToOutput.bind(rl);
38272
+ muteable._writeToOutput = (text) => {
38273
+ if (!muted) original(text);
38274
+ };
38275
+ }
38276
+ rl.on("line", (line) => {
38277
+ const waiter = waiters.shift();
38278
+ if (waiter) waiter(line);
38279
+ else lineQueue.push(line);
38280
+ });
38281
+ rl.on("close", () => {
38282
+ inputClosed = true;
38283
+ while (waiters.length > 0) {
38284
+ const waiter = waiters.shift();
38285
+ waiter?.("");
38286
+ }
38287
+ });
38288
+ sharedRl = rl;
38289
+ return rl;
38290
+ }
38291
+ function ask(query) {
38292
+ getRl();
38293
+ process.stdout.write(query);
38294
+ return new Promise((resolve3) => {
38295
+ const buffered = lineQueue.shift();
38296
+ if (buffered !== void 0) {
38297
+ resolve3(buffered);
38298
+ } else if (inputClosed) {
38299
+ resolve3("");
38300
+ } else {
38301
+ waiters.push(resolve3);
38302
+ }
38303
+ });
38304
+ }
38305
+ function closePrompts() {
38306
+ if (sharedRl) {
38307
+ sharedRl.close();
38308
+ sharedRl = null;
38309
+ }
38310
+ }
38311
+ async function prompt(question, def) {
38312
+ const suffix = def !== void 0 ? ` (${def})` : "";
38313
+ const answer = await ask(`${question}${suffix}: `);
38314
+ const trimmed = answer.trim();
38315
+ return trimmed === "" && def !== void 0 ? def : trimmed;
38316
+ }
38317
+ async function confirm(question) {
38318
+ const answer = (await prompt(`${question} (y/N)`)).toLowerCase();
38319
+ return answer === "y" || answer === "yes";
38320
+ }
38321
+ async function promptAddress(label, def) {
38322
+ for (let attempt = 0; attempt < 5; attempt++) {
38323
+ const raw = await prompt(label, def);
38324
+ if (isAddress(raw)) return getAddress(raw);
38325
+ console.log(` "${raw}" is not a valid EVM address \u2014 try again.`);
38326
+ }
38327
+ throw new Error(`No valid address provided for: ${label}`);
38328
+ }
38329
+ async function promptHidden(question) {
38330
+ const isTty = process.stdin.isTTY === true;
38331
+ if (isTty) muted = true;
38332
+ const answer = await ask(`${question}: `);
38333
+ if (isTty) {
38334
+ muted = false;
38335
+ process.stdout.write("\n");
38336
+ }
38337
+ return answer;
38338
+ }
38339
+
38340
+ // src/lib/chain.ts
38341
+ var CHAINS = {
38342
+ 8453: base,
38343
+ 84532: baseSepolia,
38344
+ 42161: arbitrum,
38345
+ 130: unichain
38346
+ };
38347
+ function getChainById(chainId) {
38348
+ const chain2 = CHAINS[chainId];
38349
+ if (!chain2) {
38350
+ throw new Error(
38351
+ `Unsupported chainId: ${chainId}. Supported: 8453 (Base), 84532 (Base Sepolia), 42161 (Arbitrum), 130 (Unichain)`
38352
+ );
38353
+ }
38354
+ return chain2;
38355
+ }
38356
+ var RPC_ENV_VARS = {
38357
+ 8453: "BASE_RPC_URL",
38358
+ 84532: "BASE_SEPOLIA_RPC_URL",
38359
+ 42161: "ARBITRUM_RPC_URL",
38360
+ 130: "UNICHAIN_RPC_URL"
38361
+ };
38362
+ function getRpcUrl(chainId) {
38363
+ const env = parseEnvFile(sailPath(".env.local"));
38364
+ const fromProject = env.RPC_URL;
38365
+ if (fromProject?.trim()) return fromProject.trim();
38366
+ const perChain = RPC_ENV_VARS[chainId];
38367
+ const fromPerChain = perChain ? process.env[perChain] : void 0;
38368
+ if (fromPerChain?.trim()) return fromPerChain.trim();
38369
+ const fromEnv = process.env.RPC_URL;
38370
+ return fromEnv?.trim() ? fromEnv.trim() : void 0;
38371
+ }
38372
+
38373
+ // src/lib/keys.ts
38374
+ var ROLES = ["manager", "permissionSigner"];
38375
+ function normalizeRole(input) {
38376
+ const n = input.trim().toLowerCase().replace(/[-_\s]/g, "");
38377
+ if (n === "manager" || n === "mgr" || n === "m" || n === "agent" || n === "agentwallet") {
38378
+ return "manager";
38379
+ }
38380
+ if (n === "permissionsigner" || n === "signer" || n === "ps" || n === "permission" || n === "mandatesigner" || n === "mandate") {
38381
+ return "permissionSigner";
38382
+ }
38383
+ return null;
38384
+ }
38385
+ function roleLabel(role) {
38386
+ return role === "manager" ? "agent wallet" : "mandate signer";
38387
+ }
38388
+ function safeHex(safe) {
38389
+ return safe.toLowerCase().replace(/^0x/, "").replace(/[^0-9a-f]/g, "");
38390
+ }
38391
+ function keyPath(role, safe) {
38392
+ if (safe) {
38393
+ return sailPath("keys", `${role}-0x${safeHex(safe)}.json`);
38394
+ }
38395
+ return sailPath("keys", `${role}.json`);
38396
+ }
38397
+ function legacyKeyPath(role, safe) {
38398
+ return sailPath("keys", `${role}-${safeHex(safe)}.json`);
38399
+ }
38400
+ function resolveKeyPath(role, safe) {
38401
+ if (safe) {
38402
+ const perSma = keyPath(role, safe);
38403
+ if (fileExists(perSma)) return perSma;
38404
+ const legacy = legacyKeyPath(role, safe);
38405
+ if (fileExists(legacy)) return legacy;
38406
+ }
38407
+ return keyPath(role);
38408
+ }
38409
+ function keyExists(role, safe) {
38410
+ return fileExists(resolveKeyPath(role, safe));
38411
+ }
38412
+ async function loadKeyring(role, safe) {
38413
+ const keystore = readJsonFile(resolveKeyPath(role, safe));
38414
+ if (!keystore) {
38024
38415
  throw new Error(
38025
38416
  `No ${roleLabel(role)} found.
38026
38417
  Run "sailor keys generate" and choose "${roleLabel(role)}" first.`
@@ -38157,360 +38548,136 @@ SMA created. Address: ${stored.safe}`);
38157
38548
  console.log(
38158
38549
  "\nOn-chain account creation is not wired up in this build yet \u2014\nclient.account.create is a stub until SailKernel is deployed and the\nSDK is connected. Nothing was created on-chain."
38159
38550
  );
38160
- return;
38161
- }
38162
- throw err;
38163
- }
38164
- }
38165
-
38166
- // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/op-stack/contracts.js
38167
- var contracts = {
38168
- gasPriceOracle: { address: "0x420000000000000000000000000000000000000F" },
38169
- l1Block: { address: "0x4200000000000000000000000000000000000015" },
38170
- l2CrossDomainMessenger: {
38171
- address: "0x4200000000000000000000000000000000000007"
38172
- },
38173
- l2Erc721Bridge: { address: "0x4200000000000000000000000000000000000014" },
38174
- l2StandardBridge: { address: "0x4200000000000000000000000000000000000010" },
38175
- l2ToL1MessagePasser: {
38176
- address: "0x4200000000000000000000000000000000000016"
38177
- }
38178
- };
38179
-
38180
- // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/op-stack/formatters.js
38181
- init_fromHex();
38182
- init_block2();
38183
- init_transaction2();
38184
- init_transactionReceipt();
38185
- var formatters = {
38186
- block: /* @__PURE__ */ defineBlock({
38187
- format(args) {
38188
- const transactions = args.transactions?.map((transaction) => {
38189
- if (typeof transaction === "string")
38190
- return transaction;
38191
- const formatted = formatTransaction(transaction);
38192
- if (formatted.typeHex === "0x7e") {
38193
- formatted.isSystemTx = transaction.isSystemTx;
38194
- formatted.mint = transaction.mint ? hexToBigInt(transaction.mint) : void 0;
38195
- formatted.sourceHash = transaction.sourceHash;
38196
- formatted.type = "deposit";
38197
- }
38198
- return formatted;
38199
- });
38200
- return {
38201
- transactions,
38202
- stateRoot: args.stateRoot
38203
- };
38204
- }
38205
- }),
38206
- transaction: /* @__PURE__ */ defineTransaction({
38207
- format(args) {
38208
- const transaction = {};
38209
- if (args.type === "0x7e") {
38210
- transaction.isSystemTx = args.isSystemTx;
38211
- transaction.mint = args.mint ? hexToBigInt(args.mint) : void 0;
38212
- transaction.sourceHash = args.sourceHash;
38213
- transaction.type = "deposit";
38214
- }
38215
- return transaction;
38216
- }
38217
- }),
38218
- transactionReceipt: /* @__PURE__ */ defineTransactionReceipt({
38219
- format(args) {
38220
- return {
38221
- l1GasPrice: args.l1GasPrice ? hexToBigInt(args.l1GasPrice) : null,
38222
- l1GasUsed: args.l1GasUsed ? hexToBigInt(args.l1GasUsed) : null,
38223
- l1Fee: args.l1Fee ? hexToBigInt(args.l1Fee) : null,
38224
- l1FeeScalar: args.l1FeeScalar ? Number(args.l1FeeScalar) : null
38225
- };
38226
- }
38227
- })
38228
- };
38229
-
38230
- // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/op-stack/serializers.js
38231
- init_address();
38232
- init_isAddress();
38233
- init_concat();
38234
- init_toHex();
38235
- init_toRlp();
38236
- init_serializeTransaction();
38237
- function serializeTransaction2(transaction, signature) {
38238
- if (isDeposit(transaction))
38239
- return serializeTransactionDeposit(transaction);
38240
- return serializeTransaction(transaction, signature);
38241
- }
38242
- var serializers = {
38243
- transaction: serializeTransaction2
38244
- };
38245
- function serializeTransactionDeposit(transaction) {
38246
- assertTransactionDeposit(transaction);
38247
- const { sourceHash, data, from: from14, gas, isSystemTx, mint, to, value } = transaction;
38248
- const serializedTransaction = [
38249
- sourceHash,
38250
- from14,
38251
- to ?? "0x",
38252
- mint ? toHex(mint) : "0x",
38253
- value ? toHex(value) : "0x",
38254
- gas ? toHex(gas) : "0x",
38255
- isSystemTx ? "0x1" : "0x",
38256
- data ?? "0x"
38257
- ];
38258
- return concatHex([
38259
- "0x7e",
38260
- toRlp(serializedTransaction)
38261
- ]);
38262
- }
38263
- function isDeposit(transaction) {
38264
- if (transaction.type === "deposit")
38265
- return true;
38266
- if (typeof transaction.sourceHash !== "undefined")
38267
- return true;
38268
- return false;
38269
- }
38270
- function assertTransactionDeposit(transaction) {
38271
- const { from: from14, to } = transaction;
38272
- if (from14 && !isAddress(from14))
38273
- throw new InvalidAddressError({ address: from14 });
38274
- if (to && !isAddress(to))
38275
- throw new InvalidAddressError({ address: to });
38276
- }
38277
-
38278
- // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/op-stack/chainConfig.js
38279
- var chainConfig = {
38280
- blockTime: 2e3,
38281
- contracts,
38282
- formatters,
38283
- serializers
38284
- };
38285
-
38286
- // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/chains/definitions/arbitrum.js
38287
- init_defineChain();
38288
- var arbitrum = /* @__PURE__ */ defineChain({
38289
- id: 42161,
38290
- name: "Arbitrum One",
38291
- nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
38292
- blockTime: 250,
38293
- rpcUrls: {
38294
- default: {
38295
- http: ["https://arb1.arbitrum.io/rpc"]
38296
- }
38297
- },
38298
- blockExplorers: {
38299
- default: {
38300
- name: "Arbiscan",
38301
- url: "https://arbiscan.io",
38302
- apiUrl: "https://api.arbiscan.io/api"
38303
- }
38304
- },
38305
- contracts: {
38306
- multicall3: {
38307
- address: "0xca11bde05977b3631167028862be2a173976ca11",
38308
- blockCreated: 7654707
38309
- }
38310
- }
38311
- });
38312
-
38313
- // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/chains/definitions/base.js
38314
- init_defineChain();
38315
- var sourceId = 1;
38316
- var base = /* @__PURE__ */ defineChain({
38317
- ...chainConfig,
38318
- id: 8453,
38319
- name: "Base",
38320
- nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
38321
- rpcUrls: {
38322
- default: {
38323
- http: ["https://mainnet.base.org"]
38324
- }
38325
- },
38326
- blockExplorers: {
38327
- default: {
38328
- name: "Basescan",
38329
- url: "https://basescan.org",
38330
- apiUrl: "https://api.basescan.org/api"
38331
- }
38332
- },
38333
- contracts: {
38334
- ...chainConfig.contracts,
38335
- disputeGameFactory: {
38336
- [sourceId]: {
38337
- address: "0x43edB88C4B80fDD2AdFF2412A7BebF9dF42cB40e"
38338
- }
38339
- },
38340
- l2OutputOracle: {
38341
- [sourceId]: {
38342
- address: "0x56315b90c40730925ec5485cf004d835058518A0"
38343
- }
38344
- },
38345
- multicall3: {
38346
- address: "0xca11bde05977b3631167028862be2a173976ca11",
38347
- blockCreated: 5022
38348
- },
38349
- portal: {
38350
- [sourceId]: {
38351
- address: "0x49048044D57e1C92A77f79988d21Fa8fAF74E97e",
38352
- blockCreated: 17482143
38353
- }
38354
- },
38355
- l1StandardBridge: {
38356
- [sourceId]: {
38357
- address: "0x3154Cf16ccdb4C6d922629664174b904d80F2C35",
38358
- blockCreated: 17482143
38359
- }
38360
- }
38361
- },
38362
- sourceId
38363
- });
38364
- var basePreconf = /* @__PURE__ */ defineChain({
38365
- ...base,
38366
- experimental_preconfirmationTime: 200,
38367
- rpcUrls: {
38368
- default: {
38369
- http: ["https://mainnet-preconf.base.org"]
38370
- }
38371
- }
38372
- });
38373
-
38374
- // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/chains/definitions/baseSepolia.js
38375
- init_defineChain();
38376
- var sourceId2 = 11155111;
38377
- var baseSepolia = /* @__PURE__ */ defineChain({
38378
- ...chainConfig,
38379
- id: 84532,
38380
- network: "base-sepolia",
38381
- name: "Base Sepolia",
38382
- nativeCurrency: { name: "Sepolia Ether", symbol: "ETH", decimals: 18 },
38383
- rpcUrls: {
38384
- default: {
38385
- http: ["https://sepolia.base.org"]
38386
- }
38387
- },
38388
- blockExplorers: {
38389
- default: {
38390
- name: "Basescan",
38391
- url: "https://sepolia.basescan.org",
38392
- apiUrl: "https://api-sepolia.basescan.org/api"
38393
- }
38394
- },
38395
- contracts: {
38396
- ...chainConfig.contracts,
38397
- disputeGameFactory: {
38398
- [sourceId2]: {
38399
- address: "0xd6E6dBf4F7EA0ac412fD8b65ED297e64BB7a06E1"
38400
- }
38401
- },
38402
- l2OutputOracle: {
38403
- [sourceId2]: {
38404
- address: "0x84457ca9D0163FbC4bbfe4Dfbb20ba46e48DF254"
38405
- }
38406
- },
38407
- portal: {
38408
- [sourceId2]: {
38409
- address: "0x49f53e41452c74589e85ca1677426ba426459e85",
38410
- blockCreated: 4446677
38411
- }
38412
- },
38413
- l1StandardBridge: {
38414
- [sourceId2]: {
38415
- address: "0xfd0Bf71F60660E2f608ed56e1659C450eB113120",
38416
- blockCreated: 4446677
38417
- }
38418
- },
38419
- multicall3: {
38420
- address: "0xca11bde05977b3631167028862be2a173976ca11",
38421
- blockCreated: 1059647
38422
- }
38423
- },
38424
- testnet: true,
38425
- sourceId: sourceId2
38426
- });
38427
- var baseSepoliaPreconf = /* @__PURE__ */ defineChain({
38428
- ...baseSepolia,
38429
- experimental_preconfirmationTime: 200,
38430
- rpcUrls: {
38431
- default: {
38432
- http: ["https://sepolia-preconf.base.org"]
38433
- }
38434
- }
38435
- });
38436
-
38437
- // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@4.4.3/node_modules/viem/_esm/chains/definitions/unichain.js
38438
- init_defineChain();
38439
- var sourceId3 = 1;
38440
- var unichain = /* @__PURE__ */ defineChain({
38441
- ...chainConfig,
38442
- id: 130,
38443
- name: "Unichain",
38444
- nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
38445
- blockTime: 1e3,
38446
- rpcUrls: {
38447
- default: {
38448
- http: ["https://mainnet.unichain.org/"]
38551
+ return;
38449
38552
  }
38450
- },
38451
- blockExplorers: {
38452
- default: {
38453
- name: "Uniscan",
38454
- url: "https://uniscan.xyz",
38455
- apiUrl: "https://api.uniscan.xyz/api"
38553
+ throw err;
38554
+ }
38555
+ }
38556
+ var SAIL_MAINNET_CHAINS = [8453, 42161, 130];
38557
+ async function fetchProxyCreationCode(preferredChainId) {
38558
+ const rpcUrl = getRpcUrl(preferredChainId) ?? void 0;
38559
+ const publicClient = createPublicClient({
38560
+ chain: getChainById(preferredChainId),
38561
+ transport: http(rpcUrl)
38562
+ });
38563
+ return await publicClient.readContract({
38564
+ address: SAFE_V141.proxyFactory,
38565
+ abi: safeProxyFactoryAbi,
38566
+ functionName: "proxyCreationCode"
38567
+ });
38568
+ }
38569
+ async function accountPredict(options) {
38570
+ const stored = readJsonFile(sailPath("account.json"));
38571
+ let ownerAddr;
38572
+ if (options.owner) {
38573
+ if (!isAddress(options.owner, { strict: false })) {
38574
+ throw new Error(`Invalid --owner address: ${options.owner}`);
38456
38575
  }
38457
- },
38458
- contracts: {
38459
- ...chainConfig.contracts,
38460
- multicall3: {
38461
- address: "0xca11bde05977b3631167028862be2a173976ca11",
38462
- blockCreated: 0
38463
- },
38464
- disputeGameFactory: {
38465
- [sourceId3]: {
38466
- address: "0x2F12d621a16e2d3285929C9996f478508951dFe4"
38467
- }
38468
- },
38469
- portal: {
38470
- [sourceId3]: {
38471
- address: "0x0bd48f6B86a26D3a217d0Fa6FfE2B491B956A7a2"
38472
- }
38473
- },
38474
- l1StandardBridge: {
38475
- [sourceId3]: {
38476
- address: "0x81014F44b0a345033bB2b3B21C7a1A308B35fEeA"
38477
- }
38576
+ ownerAddr = getAddress(options.owner);
38577
+ } else {
38578
+ if (!stored?.owner) {
38579
+ throw new Error(
38580
+ "No owner found in .sail/account.json. Pass --owner <address> or run sailor onboard first."
38581
+ );
38478
38582
  }
38479
- },
38480
- sourceId: sourceId3
38481
- });
38482
-
38483
- // src/lib/chain.ts
38484
- var CHAINS = {
38485
- 8453: base,
38486
- 84532: baseSepolia,
38487
- 42161: arbitrum,
38488
- 130: unichain
38489
- };
38490
- function getChainById(chainId) {
38491
- const chain2 = CHAINS[chainId];
38492
- if (!chain2) {
38583
+ ownerAddr = getAddress(stored.owner);
38584
+ }
38585
+ let managerAddr;
38586
+ if (options.manager) {
38587
+ if (!isAddress(options.manager, { strict: false })) {
38588
+ throw new Error(`Invalid --manager address: ${options.manager}`);
38589
+ }
38590
+ managerAddr = getAddress(options.manager);
38591
+ } else if (stored?.manager) {
38592
+ managerAddr = getAddress(stored.manager);
38593
+ } else {
38493
38594
  throw new Error(
38494
- `Unsupported chainId: ${chainId}. Supported: 8453 (Base), 84532 (Base Sepolia), 42161 (Arbitrum), 130 (Unichain)`
38595
+ "The predicted address depends on the agent (manager) wallet, which is mixed into the kernel's CREATE2 salt.\nPass --manager <agent address> (create one first with `sailor keys`), or run after onboarding so it can be read from .sail/account.json."
38495
38596
  );
38496
38597
  }
38497
- return chain2;
38498
- }
38499
- var RPC_ENV_VARS = {
38500
- 8453: "BASE_RPC_URL",
38501
- 84532: "BASE_SEPOLIA_RPC_URL",
38502
- 42161: "ARBITRUM_RPC_URL",
38503
- 130: "UNICHAIN_RPC_URL"
38504
- };
38505
- function getRpcUrl(chainId) {
38506
- const env = parseEnvFile(sailPath(".env.local"));
38507
- const fromProject = env.RPC_URL;
38508
- if (fromProject?.trim()) return fromProject.trim();
38509
- const perChain = RPC_ENV_VARS[chainId];
38510
- const fromPerChain = perChain ? process.env[perChain] : void 0;
38511
- if (fromPerChain?.trim()) return fromPerChain.trim();
38512
- const fromEnv = process.env.RPC_URL;
38513
- return fromEnv?.trim() ? fromEnv.trim() : void 0;
38598
+ const saltNonce = options.salt != null ? BigInt(options.salt) : 0n;
38599
+ let chainIds;
38600
+ if (options.chain) {
38601
+ const chainId = Number(options.chain);
38602
+ if (!(chainId in sailDeployments)) {
38603
+ throw new Error(
38604
+ `Chain ${chainId} has no Sail Protocol deployment. Supported: ${Object.keys(sailDeployments).join(", ")}`
38605
+ );
38606
+ }
38607
+ chainIds = [chainId];
38608
+ } else {
38609
+ chainIds = SAIL_MAINNET_CHAINS;
38610
+ }
38611
+ const firstChain = chainIds[0];
38612
+ const proxyCreationCode = await fetchProxyCreationCode(firstChain);
38613
+ const results = chainIds.map((chainId) => {
38614
+ const deployment = sailDeployments[chainId];
38615
+ const viemChain = getChainById(chainId);
38616
+ const initializer = buildSafeSetupInitializer({
38617
+ owners: [ownerAddr],
38618
+ threshold: 1n,
38619
+ kernel: deployment.kernel,
38620
+ safeModuleEnabler: deployment.safeModuleEnabler
38621
+ });
38622
+ const predictedAddress = computeSailSmaAddress({
38623
+ initializer,
38624
+ saltNonce,
38625
+ deployer: ownerAddr,
38626
+ permissionSigner: ownerAddr,
38627
+ manager: managerAddr,
38628
+ feePolicy: deployment.standardFeePolicy,
38629
+ proxyCreationCode
38630
+ });
38631
+ return {
38632
+ chainId,
38633
+ name: viemChain.name,
38634
+ predictedAddress,
38635
+ kernel: deployment.kernel,
38636
+ safeModuleEnabler: deployment.safeModuleEnabler
38637
+ };
38638
+ });
38639
+ const uniqueAddresses = new Set(results.map((r) => r.predictedAddress.toLowerCase()));
38640
+ const allSame = uniqueAddresses.size === 1;
38641
+ if (options.json) {
38642
+ console.log(
38643
+ JSON.stringify(
38644
+ {
38645
+ salt: saltNonce.toString(),
38646
+ owner: ownerAddr,
38647
+ manager: managerAddr,
38648
+ chains: results.map(({ chainId, name, predictedAddress }) => ({
38649
+ chainId,
38650
+ name,
38651
+ predictedAddress
38652
+ })),
38653
+ allSame,
38654
+ note: allSame ? "All chains produce the same address with this salt, owner, and manager." : "Addresses differ per chain because the kernel salt binds the chain-specific fee policy and the Safe initializer encodes chain-specific contract addresses (kernel, safeModuleEnabler). Cross-chain same-address requires deterministic protocol deployment or a registerExisting() flow."
38655
+ },
38656
+ null,
38657
+ 2
38658
+ )
38659
+ );
38660
+ return;
38661
+ }
38662
+ console.log("\nPredicted Safe addresses");
38663
+ console.log(` Owner: ${ownerAddr}`);
38664
+ console.log(` Manager: ${managerAddr}`);
38665
+ console.log(` Salt: ${saltNonce}`);
38666
+ console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
38667
+ for (const { chainId, name, predictedAddress } of results) {
38668
+ console.log(` ${name.padEnd(14)} (${String(chainId).padEnd(5)}): ${predictedAddress}`);
38669
+ }
38670
+ console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
38671
+ if (allSame) {
38672
+ console.log("\u2713 All chains produce the same address.");
38673
+ } else {
38674
+ console.log("\n\u26A0 Addresses differ across chains.");
38675
+ console.log(
38676
+ " Root cause: SailKernel.createAccount binds the CREATE2 salt as\n keccak256(saltNonce, deployer, permissionSigner, manager, feePolicy),\n then SafeProxyFactory derives the address from that bound salt and the\n Safe initializer. Both the fee policy and the initializer (kernel,\n safeModuleEnabler) are chain-specific, so each chain yields a different\n address even with the same owner, manager, and salt.\n\n For cross-chain same-address the Sail Protocol needs one of:\n A) Deterministic (CREATE2) deployment of kernel + safeModuleEnabler +\n fee policy so they land at the same address on every chain.\n B) A registerExisting() path allowing a plain Safe (deployed with a\n chain-agnostic initializer) to be registered with the kernel."
38677
+ );
38678
+ }
38679
+ console.log(`
38680
+ To deploy on this chain: sailor onboard --new-sma --salt ${saltNonce}`);
38514
38681
  }
38515
38682
 
38516
38683
  // src/lib/output.ts
@@ -38714,6 +38881,16 @@ async function capabilities(options = {}) {
38714
38881
  // src/commands/doctor.ts
38715
38882
  init_esm2();
38716
38883
 
38884
+ // src/lib/contract-check.ts
38885
+ async function checkContractExists(pc, address) {
38886
+ try {
38887
+ const code = await pc.getCode({ address });
38888
+ return { address, hasCode: !!code && code !== "0x" };
38889
+ } catch (err) {
38890
+ return { address, hasCode: false, error: err.message.split("\n")[0] };
38891
+ }
38892
+ }
38893
+
38717
38894
  // src/lib/permission-resolver.ts
38718
38895
  var IPERMISSION_ABI = [
38719
38896
  {
@@ -38740,20 +38917,39 @@ var IPERMISSION_ABI = [
38740
38917
  outputs: [{ type: "bool" }]
38741
38918
  }
38742
38919
  ];
38743
- async function resolvePermissionForCall(params) {
38744
- const { publicClient, account: account2, manager, call: call2, registeredPermissions, blockInfo } = params;
38920
+ function buildPermissionContext(params) {
38921
+ const { account: account2, manager, call: call2, blockInfo } = params;
38745
38922
  const selector = call2.data.length >= 10 ? call2.data.slice(0, 10) : "0x00000000";
38746
- const ctx = {
38923
+ return {
38747
38924
  account: account2,
38748
38925
  manager,
38749
38926
  submitter: manager,
38750
- // conservative: submitter = manager for off-chain probe
38927
+ // runner submits dispatches from the manager (agent) wallet
38751
38928
  target: call2.target,
38752
38929
  selector,
38753
38930
  value: call2.value,
38754
38931
  blockTimestamp: blockInfo.timestamp,
38755
38932
  blockNumber: blockInfo.number
38756
38933
  };
38934
+ }
38935
+ async function probePermissionForCall(params) {
38936
+ const { publicClient, permission, account: account2, manager, call: call2, blockInfo } = params;
38937
+ const ctx = buildPermissionContext({ account: account2, manager, call: call2, blockInfo });
38938
+ try {
38939
+ const accepted = await publicClient.readContract({
38940
+ address: permission,
38941
+ abi: IPERMISSION_ABI,
38942
+ functionName: "evaluate",
38943
+ args: [call2.data, ctx]
38944
+ });
38945
+ return { accepted: Boolean(accepted), reverted: false };
38946
+ } catch (err) {
38947
+ return { accepted: false, reverted: true, error: err.message.split("\n")[0] };
38948
+ }
38949
+ }
38950
+ async function resolvePermissionForCall(params) {
38951
+ const { publicClient, account: account2, manager, call: call2, registeredPermissions, blockInfo } = params;
38952
+ const ctx = buildPermissionContext({ account: account2, manager, call: call2, blockInfo });
38757
38953
  for (const permission of registeredPermissions) {
38758
38954
  try {
38759
38955
  const accepted = await publicClient.readContract({
@@ -38845,9 +39041,14 @@ async function doctor(options = {}) {
38845
39041
  const safe = options.account ? getAddress(options.account) : stored?.safe ? getAddress(stored.safe) : null;
38846
39042
  let permissions = [];
38847
39043
  let checks = [];
39044
+ let permsNoCode = [];
38848
39045
  if (safe) {
38849
39046
  const mandates = await client.mandate.list(safe);
38850
39047
  permissions = mandates.map((m) => getAddress(m.permission));
39048
+ if (permissions.length > 0) {
39049
+ const codeChecks = await Promise.all(permissions.map((p) => checkContractExists(pc, p)));
39050
+ permsNoCode = codeChecks.filter((c) => !c.hasCode && !c.error).map((c) => c.address);
39051
+ }
38851
39052
  if (caps.dispatchModel === "conjunctive" && permissions.length > 0) {
38852
39053
  checks = await Promise.all(permissions.map((p) => probePassThrough(pc, p, safe)));
38853
39054
  }
@@ -38884,7 +39085,9 @@ async function doctor(options = {}) {
38884
39085
  manager: managerAddr ? { address: managerAddr, ...managerBal ?? {} } : null
38885
39086
  },
38886
39087
  account: safe,
39088
+ saltNonce: stored?.saltNonce ?? null,
38887
39089
  permissions,
39090
+ permissionsWithoutCode: permsNoCode,
38888
39091
  conjunctivePassThrough: caps.dispatchModel === "conjunctive" ? checks.map((c) => ({
38889
39092
  permission: c.permission,
38890
39093
  passesThrough: c.passesThrough,
@@ -38948,6 +39151,56 @@ async function doctor(options = {}) {
38948
39151
  return;
38949
39152
  }
38950
39153
  console.log(`Account: ${safe}`);
39154
+ if (stored?.saltNonce != null) {
39155
+ const saltNonce = BigInt(stored.saltNonce);
39156
+ const MAINNET_CHAINS = [8453, 42161, 130];
39157
+ try {
39158
+ const proxyCreationCode = await pc.readContract({
39159
+ address: SAFE_V141.proxyFactory,
39160
+ abi: safeProxyFactoryAbi,
39161
+ functionName: "proxyCreationCode"
39162
+ });
39163
+ const ownerAddr2 = stored.owner ? getAddress(stored.owner) : null;
39164
+ const managerAddr2 = stored.manager ? getAddress(stored.manager) : null;
39165
+ if (ownerAddr2 && managerAddr2) {
39166
+ console.log(
39167
+ `
39168
+ Multi-chain addresses (salt ${saltNonce}, owner ${ownerAddr2}, manager ${managerAddr2}):`
39169
+ );
39170
+ const CHAIN_NAMES = { 8453: "Base", 42161: "Arbitrum", 130: "Unichain" };
39171
+ for (const cid of MAINNET_CHAINS) {
39172
+ const dep = sailDeployments[cid];
39173
+ const initializer = buildSafeSetupInitializer({
39174
+ owners: [ownerAddr2],
39175
+ threshold: 1n,
39176
+ kernel: dep.kernel,
39177
+ safeModuleEnabler: dep.safeModuleEnabler
39178
+ });
39179
+ const predicted = computeSailSmaAddress({
39180
+ initializer,
39181
+ saltNonce,
39182
+ deployer: ownerAddr2,
39183
+ permissionSigner: ownerAddr2,
39184
+ manager: managerAddr2,
39185
+ feePolicy: dep.standardFeePolicy,
39186
+ proxyCreationCode
39187
+ });
39188
+ const isDeployed = predicted.toLowerCase() === safe.toLowerCase() && cid === chainId;
39189
+ const label = isDeployed ? "deployed (this account)" : predicted;
39190
+ console.log(` ${CHAIN_NAMES[cid].padEnd(12)} (${cid}): ${label}`);
39191
+ }
39192
+ console.log(
39193
+ ' \u26A0 Addresses differ per chain (chain-specific kernel salt + initializer). Run "sailor account predict" for details.'
39194
+ );
39195
+ }
39196
+ } catch {
39197
+ }
39198
+ } else if (stored) {
39199
+ console.log(
39200
+ "\nMulti-chain addresses: saltNonce not stored (deployed before salt tracking)."
39201
+ );
39202
+ console.log(" To enable: re-deploy with sailor onboard --new-sma --salt 0");
39203
+ }
38951
39204
  if (permissions.length === 0) {
38952
39205
  console.log(
38953
39206
  "\n\u26A0 No permissions registered \u2014 every dispatch will be denied (NoPermissionsRegistered)."
@@ -38957,6 +39210,13 @@ async function doctor(options = {}) {
38957
39210
  }
38958
39211
  console.log(`
38959
39212
  Registered permissions (${permissions.length}):`);
39213
+ if (permsNoCode.length > 0) {
39214
+ console.log(
39215
+ `
39216
+ \u26A0 ${permsNoCode.length} registered permission(s) have NO contract code on chain ${chainId} \u2014 dispatches naming them will fail. Verify the address (wrong chain?) or revoke:`
39217
+ );
39218
+ permsNoCode.forEach((p) => console.log(` ${p}`));
39219
+ }
38960
39220
  if (caps.dispatchModel === "selective") {
38961
39221
  permissions.forEach((p, i) => console.log(` ${i + 1}. ${p}`));
38962
39222
  console.log("\nEach dispatch names one permission, so pass-through is not required.");
@@ -40295,18 +40555,21 @@ Project: ${project.name}`));
40295
40555
  say(
40296
40556
  () => console.log(
40297
40557
  `
40298
- \u2192 Signing station:
40299
- Open ${channel.url} in your browser and connect your wallet
40558
+ \u2192 Open the Sailor dashboard to approve signing requests:
40559
+ http://localhost:${projectPort(process.cwd())}/#/station
40300
40560
  `
40301
40561
  )
40302
40562
  );
40303
40563
  let smaAddress;
40304
40564
  let justCreated = false;
40305
40565
  let ownerAddress = project.getOwner();
40566
+ let deployedSaltNonce;
40306
40567
  if (smaChoice.kind === "new") {
40307
- const created = await createSma(project, channel, publicClient, agentAddress, json);
40568
+ const saltNonce = options.salt != null ? BigInt(options.salt) : 0n;
40569
+ const created = await createSma(project, channel, publicClient, agentAddress, json, saltNonce);
40308
40570
  smaAddress = created.sma;
40309
40571
  ownerAddress = created.owner;
40572
+ deployedSaltNonce = created.saltNonce;
40310
40573
  justCreated = true;
40311
40574
  } else {
40312
40575
  smaAddress = smaChoice.address;
@@ -40349,7 +40612,8 @@ Project: ${project.name}`));
40349
40612
  owner: ownerAddress ?? permissionSigner,
40350
40613
  permissionSigner,
40351
40614
  manager: onChainManager,
40352
- chainId: project.chainId
40615
+ chainId: project.chainId,
40616
+ saltNonce: deployedSaltNonce
40353
40617
  });
40354
40618
  if (options.skipMandate) {
40355
40619
  return { sma: smaAddress, agent: agentAddress, mandates: [], created: justCreated };
@@ -40500,7 +40764,7 @@ async function waitForRegistration(publicClient, kernel, account2, attempts) {
40500
40764
  }
40501
40765
  return false;
40502
40766
  }
40503
- async function createSma(project, channel, publicClient, agentAddress, json = false) {
40767
+ async function createSma(project, channel, publicClient, agentAddress, json = false, saltNonce = 0n) {
40504
40768
  const say = (fn) => {
40505
40769
  if (!json) fn();
40506
40770
  };
@@ -40514,7 +40778,6 @@ async function createSma(project, channel, publicClient, agentAddress, json = fa
40514
40778
  kernel: project.contracts.kernel,
40515
40779
  safeModuleEnabler: deployment.safeModuleEnabler
40516
40780
  });
40517
- const saltNonce = BigInt(Date.now());
40518
40781
  const createAccountData = encodeFunctionData({
40519
40782
  abi: SailKernelAbi,
40520
40783
  functionName: "createAccount",
@@ -40565,6 +40828,7 @@ async function createSma(project, channel, publicClient, agentAddress, json = fa
40565
40828
  }
40566
40829
  const safeAddress = registered.args.account;
40567
40830
  say(() => console.log("\u2713", `SMA created at ${safeAddress}`));
40831
+ say(() => console.log(" Salt:", saltNonce.toString(), "(stored in .sail/account.json \u2014 use for sailor account predict)"));
40568
40832
  appendActivity({
40569
40833
  ts: nowIso(),
40570
40834
  actor: "owner",
@@ -40573,9 +40837,10 @@ async function createSma(project, channel, publicClient, agentAddress, json = fa
40573
40837
  owner: ownerAddress,
40574
40838
  manager: agentAddress,
40575
40839
  txHash: response.txHash,
40576
- chainId: project.chainId
40840
+ chainId: project.chainId,
40841
+ saltNonce: saltNonce.toString()
40577
40842
  });
40578
- return { sma: safeAddress, owner: ownerAddress };
40843
+ return { sma: safeAddress, owner: ownerAddress, saltNonce };
40579
40844
  }
40580
40845
  async function attachMandate(project, channel, publicClient, agentSigner, smaAddress, permissionSigner, template, opts = {}) {
40581
40846
  const say = (fn) => {
@@ -40723,7 +40988,8 @@ async function persistAccount(publicClient, account2) {
40723
40988
  permissionSigner: checksum4(account2.permissionSigner),
40724
40989
  manager: checksum4(account2.manager),
40725
40990
  chainId: account2.chainId,
40726
- createdAtBlock
40991
+ createdAtBlock,
40992
+ ...account2.saltNonce != null ? { saltNonce: account2.saltNonce.toString() } : {}
40727
40993
  };
40728
40994
  upsertAccountInList(stored);
40729
40995
  writeJsonFile(sailPath("account.json"), stored);
@@ -40834,8 +41100,8 @@ async function runDeploy(project, channel, options) {
40834
41100
  say(() => {
40835
41101
  console.log(
40836
41102
  `
40837
- \u2192 Signing station:
40838
- Open ${channel.url} in your browser and connect your wallet
41103
+ \u2192 Open the Sailor dashboard to approve signing requests:
41104
+ http://localhost:${projectPort(process.cwd())}/#/station
40839
41105
  `
40840
41106
  );
40841
41107
  console.log(`Pushing deploy request for "${contractName}"\u2026`);
@@ -41057,8 +41323,8 @@ ${spec.label} clone (${options.template})`);
41057
41323
  console.log(` SMA: ${sma}`);
41058
41324
  for (const d of spec.describe(initParams)) console.log(` ${d.label}: ${d.value}`);
41059
41325
  console.log(`
41060
- \u2192 Signing station:
41061
- Open ${channel.url} and connect your Owner wallet
41326
+ \u2192 Open the Sailor dashboard to approve signing requests:
41327
+ http://localhost:${projectPort(process.cwd())}/#/station
41062
41328
  `);
41063
41329
  });
41064
41330
  const nonce = await publicClient.readContract({
@@ -41221,8 +41487,8 @@ async function runAttach(project, channel, options) {
41221
41487
  if (!json) {
41222
41488
  console.log(
41223
41489
  `
41224
- \u2192 Signing station:
41225
- Open ${channel.url} in your browser and connect your wallet
41490
+ \u2192 Open the Sailor dashboard to approve signing requests:
41491
+ http://localhost:${projectPort(process.cwd())}/#/station
41226
41492
  `
41227
41493
  );
41228
41494
  }
@@ -41304,8 +41570,8 @@ Revoking ${targets.length} permission(s) from ${sma}:`);
41304
41570
  for (const p of targets) console.log(` ${nameFor(p) ?? p} ${p}`);
41305
41571
  console.log(
41306
41572
  `
41307
- \u2192 Signing station:
41308
- Open ${channel.url} in your browser and connect your Owner wallet
41573
+ \u2192 Open the Sailor dashboard to approve signing requests:
41574
+ http://localhost:${projectPort(process.cwd())}/#/station
41309
41575
  `
41310
41576
  );
41311
41577
  });
@@ -41606,6 +41872,88 @@ function runForgeBuild() {
41606
41872
 
41607
41873
  // src/commands/mandate.ts
41608
41874
  init_esm2();
41875
+
41876
+ // src/lib/permission-explainer.ts
41877
+ var import_node_fs11 = require("node:fs");
41878
+ var import_node_path10 = require("node:path");
41879
+ function explainPermission(name, sourcePath) {
41880
+ const resolved = sourcePath ?? (0, import_node_path10.join)(process.cwd(), "mandates", `${name}.sol`);
41881
+ let src;
41882
+ try {
41883
+ src = (0, import_node_fs11.readFileSync)(resolved, "utf8");
41884
+ } catch {
41885
+ return null;
41886
+ }
41887
+ return parseStructured(src) ?? parseNatSpec(src) ?? parseRequires(src);
41888
+ }
41889
+ function parseStructured(src) {
41890
+ if (!src.includes("ENFORCED ON-CHAIN")) return null;
41891
+ const lines = src.split("\n");
41892
+ const result = {
41893
+ source: "structured",
41894
+ enforced: [],
41895
+ notEnforced: []
41896
+ };
41897
+ const headerLines = [];
41898
+ for (const line of lines) {
41899
+ const t = line.trim();
41900
+ if (t.startsWith("import ") || t.startsWith("contract ") || t.startsWith("abstract contract ")) break;
41901
+ if (t.startsWith("//")) headerLines.push(t.slice(2));
41902
+ }
41903
+ let section = null;
41904
+ for (const raw of headerLines) {
41905
+ const trimmed = raw.trim();
41906
+ if (/^─+$/.test(trimmed)) continue;
41907
+ if (trimmed.includes("ENFORCED ON-CHAIN")) {
41908
+ section = "enforced";
41909
+ continue;
41910
+ }
41911
+ if (trimmed.includes("NOT ENFORCED")) {
41912
+ section = "notEnforced";
41913
+ continue;
41914
+ }
41915
+ if (trimmed.includes("VERIFY BEFORE USE")) {
41916
+ section = null;
41917
+ continue;
41918
+ }
41919
+ if (section === null) {
41920
+ const kv = trimmed.match(/^(Protocol|Version|Chain|Target)\s*:\s*(.+)$/i);
41921
+ if (kv) {
41922
+ const key = kv[1].toLowerCase();
41923
+ result[key] = kv[2].trim();
41924
+ }
41925
+ }
41926
+ if (section && trimmed.startsWith("\u2022")) {
41927
+ const bullet = trimmed.slice(1).trim();
41928
+ if (bullet) result[section].push(bullet);
41929
+ }
41930
+ }
41931
+ if (result.enforced.length === 0 && !result.protocol) return null;
41932
+ return result;
41933
+ }
41934
+ function parseNatSpec(src) {
41935
+ const enforced = [];
41936
+ const notEnforced = [];
41937
+ for (const line of src.split("\n")) {
41938
+ const t = line.trim();
41939
+ const notice = t.match(/^\/\/\/\s*@notice\s+(.+)$/);
41940
+ if (notice) enforced.push(notice[1].trim());
41941
+ const dev = t.match(/^\/\/\/\s*@dev\s+(.+)$/);
41942
+ if (dev) notEnforced.push(dev[1].trim());
41943
+ }
41944
+ if (enforced.length === 0 && notEnforced.length === 0) return null;
41945
+ return { source: "natspec", enforced, notEnforced };
41946
+ }
41947
+ function parseRequires(src) {
41948
+ const enforced = [];
41949
+ const re = /require\s*\([^,)]+,\s*["']([^"']+)["']\)/g;
41950
+ let m;
41951
+ while ((m = re.exec(src)) !== null) enforced.push(m[1]);
41952
+ if (enforced.length === 0) return null;
41953
+ return { source: "require-only", enforced, notEnforced: [] };
41954
+ }
41955
+
41956
+ // src/commands/mandate.ts
41609
41957
  async function fetchOnChainPermissions(account2) {
41610
41958
  try {
41611
41959
  const project = new ProjectContext();
@@ -41672,11 +42020,15 @@ ${permissions.length} permission(s) tracked for SMA ${account2.safe}:
41672
42020
  console.log(` ${p.address}`);
41673
42021
  console.log(` ${status2}`);
41674
42022
  }
42023
+ const store = new MandateStore();
41675
42024
  const draft = {
41676
42025
  account: account2.safe,
41677
42026
  chainId: account2.chainId,
41678
- // Exclude permissions revoked on-chain the draft reflects the live set.
41679
- permissions: permissions.filter((p) => !p.revokedOnChain).map((p) => ({ address: p.address, label: p.label })),
42027
+ permissions: permissions.filter((p) => !p.revokedOnChain).map((p) => {
42028
+ const mandate2 = store.find(p.address);
42029
+ const explanation = explainPermission(p.label, mandate2?.sourcePath) ?? void 0;
42030
+ return { address: p.address, label: p.label, explanation };
42031
+ }),
41680
42032
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
41681
42033
  };
41682
42034
  writeJsonFile(sailPath("mandate-draft.json"), draft);
@@ -41739,8 +42091,231 @@ ${unregistered.length} permission(s) are not yet registered on this SMA. Initiat
41739
42091
  \u2713 Saved to .sail/mandate.json \u2014 agent is ready to run.`);
41740
42092
  }
41741
42093
 
42094
+ // src/commands/mandate-simulate.ts
42095
+ var import_node_fs12 = require("node:fs");
42096
+ init_esm2();
42097
+ function parseExpect(raw, where) {
42098
+ if (raw === void 0) return void 0;
42099
+ const v = raw.toLowerCase();
42100
+ if (v === "pass" || v === "fail") return v;
42101
+ throw new Error(`${where}: "expect" must be "pass" or "fail" \u2014 got "${raw}".`);
42102
+ }
42103
+ function resolveSampleCalls(options) {
42104
+ const hasInline = options.target !== void 0 || options.calldata !== void 0;
42105
+ if (options.calls && hasInline) {
42106
+ throw new Error("Provide EITHER --calls <file> OR inline --target/--calldata, not both.");
42107
+ }
42108
+ if (options.calls) {
42109
+ let raw;
42110
+ try {
42111
+ raw = JSON.parse((0, import_node_fs12.readFileSync)(options.calls, "utf8"));
42112
+ } catch (err) {
42113
+ throw new Error(`Could not read --calls file "${options.calls}": ${err.message}`);
42114
+ }
42115
+ if (!Array.isArray(raw) || raw.length === 0) {
42116
+ throw new Error(`--calls file must be a non-empty JSON array of { target, calldata, ... }.`);
42117
+ }
42118
+ return raw.map((entry, i) => parseCallEntry(entry, i));
42119
+ }
42120
+ if (!hasInline) {
42121
+ throw new Error(
42122
+ "No sample calls. Pass --target <addr> --calldata <hex> for one call, or --calls <file.json> for a batch."
42123
+ );
42124
+ }
42125
+ if (!options.target || !options.calldata) {
42126
+ throw new Error("Inline call requires BOTH --target <addr> and --calldata <hex>.");
42127
+ }
42128
+ return [
42129
+ parseCallEntry(
42130
+ {
42131
+ target: options.target,
42132
+ calldata: options.calldata,
42133
+ value: options.value,
42134
+ expect: options.expect,
42135
+ label: options.label
42136
+ },
42137
+ 0
42138
+ )
42139
+ ];
42140
+ }
42141
+ function parseCallEntry(entry, index2) {
42142
+ const where = `call[${index2}]`;
42143
+ const target = entry.target;
42144
+ if (typeof target !== "string" || !isAddress(target, { strict: false })) {
42145
+ throw new Error(`${where}: "target" must be a valid address \u2014 got ${JSON.stringify(target)}.`);
42146
+ }
42147
+ const data = entry.calldata ?? entry.data;
42148
+ if (typeof data !== "string" || !isHex(data)) {
42149
+ throw new Error(`${where}: "calldata" must be a 0x-prefixed hex string.`);
42150
+ }
42151
+ let value = 0n;
42152
+ if (entry.value !== void 0 && entry.value !== null && entry.value !== "") {
42153
+ try {
42154
+ value = BigInt(entry.value);
42155
+ } catch {
42156
+ throw new Error(`${where}: "value" must be an integer (wei) \u2014 got ${JSON.stringify(entry.value)}.`);
42157
+ }
42158
+ }
42159
+ const label = typeof entry.label === "string" && entry.label.trim() ? entry.label.trim() : `call ${index2 + 1}`;
42160
+ return {
42161
+ label,
42162
+ target: getAddress(target),
42163
+ data,
42164
+ value,
42165
+ expect: parseExpect(entry.expect, where)
42166
+ };
42167
+ }
42168
+ function managerAddress(safe) {
42169
+ const stored = readJsonFile(sailPath("account.json"));
42170
+ if (stored?.manager && isAddress(stored.manager, { strict: false })) {
42171
+ return getAddress(stored.manager);
42172
+ }
42173
+ const ks = readJsonFile(resolveKeyPath("manager", safe));
42174
+ return ks?.address ? getAddress(`0x${ks.address.replace(/^0x/, "")}`) : null;
42175
+ }
42176
+ async function mandateSimulate(options) {
42177
+ const json = !!options.json;
42178
+ const project = new ProjectContext();
42179
+ const chainId = project.chainId;
42180
+ const rpcUrl = getRpcUrl(chainId) ?? getChainById(chainId).rpcUrls.default.http[0];
42181
+ const pc = createPublicClient({ chain: getChainById(chainId), transport: http(rpcUrl) });
42182
+ const store = new MandateStore();
42183
+ const tracked = store.find(options.address);
42184
+ const rawPermission = tracked?.address ?? options.address;
42185
+ if (!isAddress(rawPermission, { strict: false })) {
42186
+ throw new Error(`--address must be a permission address or a tracked name: ${options.address}`);
42187
+ }
42188
+ const permission = getAddress(rawPermission);
42189
+ const stored = readJsonFile(sailPath("account.json"));
42190
+ if (options.sma && !isAddress(options.sma, { strict: false })) {
42191
+ throw new Error(`Invalid --sma address: ${options.sma}`);
42192
+ }
42193
+ const accountRaw = options.sma ?? stored?.safe;
42194
+ if (!accountRaw) {
42195
+ throw new Error("No SMA. Pass --sma <address> or create one (.sail/account.json).");
42196
+ }
42197
+ const account2 = getAddress(accountRaw);
42198
+ const resolvedManager = managerAddress(options.sma ?? stored?.safe);
42199
+ const manager = resolvedManager ?? account2;
42200
+ const managerIsStandIn = resolvedManager === null;
42201
+ const calls = resolveSampleCalls(options);
42202
+ let blockInfo = { number: 0n, timestamp: 0n };
42203
+ let blockStale = false;
42204
+ try {
42205
+ const block = await pc.getBlock();
42206
+ blockInfo = { number: block.number ?? 0n, timestamp: block.timestamp ?? 0n };
42207
+ } catch {
42208
+ blockStale = true;
42209
+ }
42210
+ const results = await Promise.all(
42211
+ calls.map(async (c, i) => {
42212
+ const [probe, codeCheck] = await Promise.all([
42213
+ probePermissionForCall({
42214
+ publicClient: pc,
42215
+ permission,
42216
+ account: account2,
42217
+ manager,
42218
+ call: { target: c.target, value: c.value, data: c.data },
42219
+ blockInfo
42220
+ }),
42221
+ checkContractExists(pc, c.target)
42222
+ ]);
42223
+ const result = probe.accepted ? "pass" : "fail";
42224
+ return {
42225
+ index: i,
42226
+ label: c.label,
42227
+ target: c.target,
42228
+ value: c.value.toString(),
42229
+ result,
42230
+ reverted: probe.reverted,
42231
+ revertReason: probe.error,
42232
+ expect: c.expect ?? null,
42233
+ match: c.expect ? c.expect === result : null,
42234
+ targetHasCode: codeCheck.hasCode,
42235
+ targetCheckError: codeCheck.error
42236
+ };
42237
+ })
42238
+ );
42239
+ const mismatches = results.filter((r) => r.match === false);
42240
+ const noCodeTargets = results.filter((r) => !r.targetHasCode && !r.targetCheckError);
42241
+ const ok = mismatches.length === 0;
42242
+ if (json) {
42243
+ emit(true, () => {
42244
+ }, {
42245
+ status: ok ? "ok" : "mismatch",
42246
+ spendsGas: false,
42247
+ probe: "off-chain eth_call (evaluate)",
42248
+ chainId,
42249
+ permission,
42250
+ sma: account2,
42251
+ submitter: manager,
42252
+ submitterIsStandIn: managerIsStandIn,
42253
+ blockNumber: blockInfo.number.toString(),
42254
+ blockContextStale: blockStale,
42255
+ results: results.map((r) => ({
42256
+ index: r.index,
42257
+ label: r.label,
42258
+ target: r.target,
42259
+ value: r.value,
42260
+ result: r.result,
42261
+ reverted: r.reverted,
42262
+ revertReason: r.revertReason,
42263
+ expect: r.expect,
42264
+ match: r.match,
42265
+ targetHasCode: r.targetHasCode,
42266
+ targetCheckError: r.targetCheckError
42267
+ })),
42268
+ mismatches: mismatches.length,
42269
+ noCodeTargets: noCodeTargets.map((r) => r.target),
42270
+ ok
42271
+ });
42272
+ if (!ok) process.exit(1);
42273
+ return;
42274
+ }
42275
+ console.log("\nsailor mandate simulate \u2014 off-chain probe (eth_call). Spends NO gas, signs nothing.");
42276
+ console.log(
42277
+ "Shows what the permission's evaluate() returns for these calls. It does NOT guarantee\nthe permission is correct \u2014 only what it does for exactly these inputs."
42278
+ );
42279
+ console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
42280
+ console.log(`Permission: ${permission}`);
42281
+ console.log(`SMA: ${account2}`);
42282
+ console.log(
42283
+ `Submitter: ${manager}${managerIsStandIn ? " (stand-in: no manager key found locally)" : " (agent wallet)"}`
42284
+ );
42285
+ console.log(`Chain: ${chainId} block ${blockInfo.number}${blockStale ? " \u26A0 could not fetch block \u2014 time/block-gated permissions may show false negatives" : ""}`);
42286
+ console.log("");
42287
+ for (const r of results) {
42288
+ const verdict = r.result === "pass" ? "PASS " : r.reverted ? "REVERT" : "FAIL ";
42289
+ const expectStr = r.expect === null ? "" : r.match ? ` expected ${r.expect} \u2713 MATCH` : ` expected ${r.expect} \u2717 MISMATCH`;
42290
+ console.log(`[${r.index + 1}] ${verdict} ${r.label}${expectStr}`);
42291
+ const codeNote = r.targetCheckError ? `\u26A0 could not verify contract code (${r.targetCheckError})` : r.targetHasCode ? "\u2713 contract present" : `\u26A0 NO contract code on chain ${chainId} \u2014 this call would fail on-chain regardless of the permission`;
42292
+ console.log(` target ${r.target} ${codeNote}`);
42293
+ if (r.reverted && r.revertReason) {
42294
+ console.log(` evaluate() reverted: ${r.revertReason}`);
42295
+ }
42296
+ }
42297
+ console.log("");
42298
+ if (results.every((r) => r.expect === null)) {
42299
+ console.log(`Probed ${results.length} call(s). No expectations were supplied (informational only).`);
42300
+ } else {
42301
+ const matched = results.filter((r) => r.match === true).length;
42302
+ const checked = results.filter((r) => r.expect !== null).length;
42303
+ if (ok) {
42304
+ console.log(`Result: ${matched}/${checked} matched expectations. \u2713`);
42305
+ } else {
42306
+ console.log(`Result: ${matched}/${checked} matched, ${mismatches.length} MISMATCH. \u2717`);
42307
+ }
42308
+ }
42309
+ if (noCodeTargets.length > 0) {
42310
+ console.log(
42311
+ `\u26A0 ${noCodeTargets.length} target(s) have no contract code on chain ${chainId} \u2014 likely a wrong or wrong-chain address.`
42312
+ );
42313
+ }
42314
+ if (!ok) process.exit(1);
42315
+ }
42316
+
41742
42317
  // src/commands/rotate-signer.ts
41743
- var import_node_fs11 = require("node:fs");
42318
+ var import_node_fs13 = require("node:fs");
41744
42319
  init_esm2();
41745
42320
  var PENDING_REATTACH_FILE = ["state", "pending-reattach.json"];
41746
42321
  async function rotateSigner(options) {
@@ -41851,8 +42426,8 @@ SMA: ${smaAddress}`);
41851
42426
  }
41852
42427
  console.log(
41853
42428
  `
41854
- \u2192 Signing station:
41855
- Open ${channel.url} in your browser and connect the owner wallet (${owner2}).
42429
+ \u2192 Open the Sailor dashboard to approve signing requests:
42430
+ http://localhost:${projectPort(process.cwd())}/#/station
41856
42431
  `
41857
42432
  );
41858
42433
  });
@@ -42127,7 +42702,7 @@ function writePending(pending) {
42127
42702
  }
42128
42703
  function clearPending() {
42129
42704
  try {
42130
- (0, import_node_fs11.rmSync)(sailPath(...PENDING_REATTACH_FILE), { force: true });
42705
+ (0, import_node_fs13.rmSync)(sailPath(...PENDING_REATTACH_FILE), { force: true });
42131
42706
  } catch {
42132
42707
  }
42133
42708
  }
@@ -42167,8 +42742,8 @@ async function ownerConnect(options) {
42167
42742
  try {
42168
42743
  await channel.start();
42169
42744
  if (!options.json) {
42170
- console.log("\u2192 Open this in your browser and connect your wallet:");
42171
- console.log(` ${channel.url}/#/station`);
42745
+ console.log("\u2192 Open the Sailor dashboard and connect your wallet:");
42746
+ console.log(` http://localhost:${projectPort(projectRoot)}/#/station`);
42172
42747
  if (channel.remote) console.log(" (using the running signing station)");
42173
42748
  console.log("\nWaiting for a wallet connection\u2026");
42174
42749
  }
@@ -42213,30 +42788,30 @@ function ownerShow(options) {
42213
42788
  }
42214
42789
 
42215
42790
  // src/commands/run.ts
42216
- var import_node_fs13 = __toESM(require("node:fs"), 1);
42217
- var import_node_path10 = __toESM(require("node:path"), 1);
42791
+ var import_node_fs15 = __toESM(require("node:fs"), 1);
42792
+ var import_node_path11 = __toESM(require("node:path"), 1);
42218
42793
  var import_node_url = require("node:url");
42219
42794
  init_esm2();
42220
42795
 
42221
42796
  // src/lib/process.ts
42222
- var import_node_fs12 = __toESM(require("node:fs"), 1);
42797
+ var import_node_fs14 = __toESM(require("node:fs"), 1);
42223
42798
  function agentPidPath() {
42224
42799
  return sailPath("agent.pid");
42225
42800
  }
42226
42801
  function writeAgentPid() {
42227
- import_node_fs12.default.mkdirSync(sailPath(), { recursive: true });
42228
- import_node_fs12.default.writeFileSync(agentPidPath(), `${process.pid}
42802
+ import_node_fs14.default.mkdirSync(sailPath(), { recursive: true });
42803
+ import_node_fs14.default.writeFileSync(agentPidPath(), `${process.pid}
42229
42804
  `);
42230
42805
  }
42231
42806
  function clearAgentPid() {
42232
42807
  try {
42233
- import_node_fs12.default.rmSync(agentPidPath());
42808
+ import_node_fs14.default.rmSync(agentPidPath());
42234
42809
  } catch {
42235
42810
  }
42236
42811
  }
42237
42812
  function readAgentPid() {
42238
42813
  try {
42239
- const pid = Number.parseInt(import_node_fs12.default.readFileSync(agentPidPath(), "utf-8").trim(), 10);
42814
+ const pid = Number.parseInt(import_node_fs14.default.readFileSync(agentPidPath(), "utf-8").trim(), 10);
42240
42815
  return Number.isNaN(pid) ? null : pid;
42241
42816
  } catch {
42242
42817
  return null;
@@ -42266,7 +42841,7 @@ var ERC20_BALANCE_ABI = [
42266
42841
  function loadAgentData(filePath) {
42267
42842
  if (!filePath) return {};
42268
42843
  try {
42269
- const parsed = JSON.parse(import_node_fs13.default.readFileSync(filePath, "utf-8"));
42844
+ const parsed = JSON.parse(import_node_fs15.default.readFileSync(filePath, "utf-8"));
42270
42845
  return parsed && typeof parsed === "object" ? parsed : {};
42271
42846
  } catch {
42272
42847
  return {};
@@ -42275,8 +42850,8 @@ function loadAgentData(filePath) {
42275
42850
  async function loadAgent() {
42276
42851
  const candidates = ["src/agent.ts", "src/agent.js", "dist/agent.js", "dist/src/agent.js"];
42277
42852
  for (const rel of candidates) {
42278
- const abs = import_node_path10.default.join(process.cwd(), rel);
42279
- if (!import_node_fs13.default.existsSync(abs)) continue;
42853
+ const abs = import_node_path11.default.join(process.cwd(), rel);
42854
+ if (!import_node_fs15.default.existsSync(abs)) continue;
42280
42855
  let mod2;
42281
42856
  if (abs.endsWith(".ts")) {
42282
42857
  const { tsImport } = await import("tsx/esm/api");
@@ -42763,14 +43338,14 @@ async function sessionResume() {
42763
43338
  }
42764
43339
 
42765
43340
  // src/commands/station.ts
42766
- var import_node_fs14 = require("node:fs");
42767
- var import_node_path11 = require("node:path");
42768
- var RUNTIME_SERVER_FILE2 = (0, import_node_path11.join)(".sail", "runtime", "server.json");
43341
+ var import_node_fs16 = require("node:fs");
43342
+ var import_node_path12 = require("node:path");
43343
+ var RUNTIME_SERVER_FILE2 = (0, import_node_path12.join)(".sail", "runtime", "server.json");
42769
43344
  function readState(projectRoot) {
42770
- const file = (0, import_node_path11.join)(projectRoot, RUNTIME_SERVER_FILE2);
42771
- if (!(0, import_node_fs14.existsSync)(file)) return null;
43345
+ const file = (0, import_node_path12.join)(projectRoot, RUNTIME_SERVER_FILE2);
43346
+ if (!(0, import_node_fs16.existsSync)(file)) return null;
42772
43347
  try {
42773
- return JSON.parse((0, import_node_fs14.readFileSync)(file, "utf8"));
43348
+ return JSON.parse((0, import_node_fs16.readFileSync)(file, "utf8"));
42774
43349
  } catch {
42775
43350
  return null;
42776
43351
  }
@@ -42842,9 +43417,9 @@ async function stationStop(options) {
42842
43417
  }
42843
43418
  const daemon = await discoverDaemon(projectRoot);
42844
43419
  if (!daemon) {
42845
- const file = (0, import_node_path11.join)(projectRoot, RUNTIME_SERVER_FILE2);
43420
+ const file = (0, import_node_path12.join)(projectRoot, RUNTIME_SERVER_FILE2);
42846
43421
  try {
42847
- if ((0, import_node_fs14.existsSync)(file)) (0, import_node_fs14.rmSync)(file);
43422
+ if ((0, import_node_fs16.existsSync)(file)) (0, import_node_fs16.rmSync)(file);
42848
43423
  } catch {
42849
43424
  }
42850
43425
  emit(options.json, () => console.log("Station process not found; cleared stale state."), {
@@ -42860,9 +43435,9 @@ async function stationStop(options) {
42860
43435
  pid: state.pid
42861
43436
  });
42862
43437
  } catch {
42863
- const file = (0, import_node_path11.join)(projectRoot, RUNTIME_SERVER_FILE2);
43438
+ const file = (0, import_node_path12.join)(projectRoot, RUNTIME_SERVER_FILE2);
42864
43439
  try {
42865
- if ((0, import_node_fs14.existsSync)(file)) (0, import_node_fs14.rmSync)(file);
43440
+ if ((0, import_node_fs16.existsSync)(file)) (0, import_node_fs16.rmSync)(file);
42866
43441
  } catch {
42867
43442
  }
42868
43443
  emit(options.json, () => console.log("Station process not found; cleared stale state."), {
@@ -42917,15 +43492,15 @@ async function status() {
42917
43492
 
42918
43493
  // src/commands/ui.ts
42919
43494
  var import_node_child_process2 = require("node:child_process");
42920
- var import_node_fs15 = __toESM(require("node:fs"), 1);
43495
+ var import_node_fs17 = __toESM(require("node:fs"), 1);
42921
43496
  var import_node_net2 = __toESM(require("node:net"), 1);
42922
- var import_node_path12 = __toESM(require("node:path"), 1);
42923
- var UI_STATE_FILE = import_node_path12.default.join(".sail", "runtime", "ui.json");
43497
+ var import_node_path13 = __toESM(require("node:path"), 1);
43498
+ var UI_STATE_FILE = import_node_path13.default.join(".sail", "runtime", "ui.json");
42924
43499
  function readState2(projectRoot) {
42925
- const file = import_node_path12.default.join(projectRoot, UI_STATE_FILE);
42926
- if (!import_node_fs15.default.existsSync(file)) return null;
43500
+ const file = import_node_path13.default.join(projectRoot, UI_STATE_FILE);
43501
+ if (!import_node_fs17.default.existsSync(file)) return null;
42927
43502
  try {
42928
- return JSON.parse(import_node_fs15.default.readFileSync(file, "utf-8"));
43503
+ return JSON.parse(import_node_fs17.default.readFileSync(file, "utf-8"));
42929
43504
  } catch {
42930
43505
  return null;
42931
43506
  }
@@ -42948,15 +43523,15 @@ function findFreePort(from14) {
42948
43523
  }
42949
43524
  async function uiCommand() {
42950
43525
  const distDir = cliDistDir();
42951
- const uiDistDir = import_node_path12.default.join(packageRoot(), "packages", "ui", "dist");
42952
- const serverBundle = import_node_path12.default.resolve(distDir, "server.cjs");
43526
+ const uiDistDir = import_node_path13.default.join(packageRoot(), "packages", "ui", "dist");
43527
+ const serverBundle = import_node_path13.default.resolve(distDir, "server.cjs");
42953
43528
  const projectRoot = process.cwd();
42954
- const sailDir2 = import_node_path12.default.join(projectRoot, ".sail");
43529
+ const sailDir2 = import_node_path13.default.join(projectRoot, ".sail");
42955
43530
  const port = await findFreePort(projectPort(projectRoot));
42956
- if (!import_node_fs15.default.existsSync(serverBundle)) {
43531
+ if (!import_node_fs17.default.existsSync(serverBundle)) {
42957
43532
  throw new Error(`Server bundle not found at ${serverBundle}. Re-run the sailor build.`);
42958
43533
  }
42959
- if (!import_node_fs15.default.existsSync(import_node_path12.default.join(uiDistDir, "index.html"))) {
43534
+ if (!import_node_fs17.default.existsSync(import_node_path13.default.join(uiDistDir, "index.html"))) {
42960
43535
  throw new Error(`UI dist not found at ${uiDistDir}. Re-run the sailor build.`);
42961
43536
  }
42962
43537
  const existing = readState2(projectRoot);
@@ -42974,9 +43549,9 @@ async function uiCommand() {
42974
43549
  if (!isAlive(child.pid)) {
42975
43550
  throw new Error(`Sailor UI process exited immediately. Check that the server bundle is intact.`);
42976
43551
  }
42977
- import_node_fs15.default.mkdirSync(import_node_path12.default.join(projectRoot, ".sail", "runtime"), { recursive: true });
42978
- import_node_fs15.default.writeFileSync(
42979
- import_node_path12.default.join(projectRoot, UI_STATE_FILE),
43552
+ import_node_fs17.default.mkdirSync(import_node_path13.default.join(projectRoot, ".sail", "runtime"), { recursive: true });
43553
+ import_node_fs17.default.writeFileSync(
43554
+ import_node_path13.default.join(projectRoot, UI_STATE_FILE),
42980
43555
  JSON.stringify({ pid: child.pid, port, startedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
42981
43556
  );
42982
43557
  console.log(`Sailor UI started at http://localhost:${port} (pid ${child.pid})`);
@@ -42987,7 +43562,7 @@ function uiStatus() {
42987
43562
  if (state && isAlive(state.pid)) {
42988
43563
  console.log(`\u25CF running http://localhost:${state.port} (pid ${state.pid})`);
42989
43564
  } else {
42990
- if (state) import_node_fs15.default.rmSync(import_node_path12.default.join(process.cwd(), UI_STATE_FILE), { force: true });
43565
+ if (state) import_node_fs17.default.rmSync(import_node_path13.default.join(process.cwd(), UI_STATE_FILE), { force: true });
42991
43566
  console.log("\u25CB Sailor UI is not running");
42992
43567
  }
42993
43568
  }
@@ -42999,12 +43574,12 @@ function uiStop() {
42999
43574
  return;
43000
43575
  }
43001
43576
  if (!isAlive(state.pid)) {
43002
- import_node_fs15.default.rmSync(import_node_path12.default.join(projectRoot, UI_STATE_FILE), { force: true });
43577
+ import_node_fs17.default.rmSync(import_node_path13.default.join(projectRoot, UI_STATE_FILE), { force: true });
43003
43578
  console.log("Sailor UI is not running (stale state file removed).");
43004
43579
  return;
43005
43580
  }
43006
43581
  process.kill(state.pid, "SIGTERM");
43007
- import_node_fs15.default.rmSync(import_node_path12.default.join(projectRoot, UI_STATE_FILE), { force: true });
43582
+ import_node_fs17.default.rmSync(import_node_path13.default.join(projectRoot, UI_STATE_FILE), { force: true });
43008
43583
  console.log(`Stopped Sailor UI (pid ${state.pid}).`);
43009
43584
  }
43010
43585
 
@@ -43058,6 +43633,12 @@ keys.command("export-ci").description(
43058
43633
  ).action(action(keysExportCi));
43059
43634
  var account = program2.command("account").description("Manage the Sail SMA");
43060
43635
  account.command("create").description("Create a new Sail SMA on-chain").action(action(accountCreate));
43636
+ account.command("predict").description(
43637
+ "Compute the deterministic Safe address for a given owner + manager + salt (no gas, no deployment)"
43638
+ ).option("--owner <address>", "Owner EOA address (defaults to .sail/account.json)").option(
43639
+ "--manager <address>",
43640
+ "Agent (manager) wallet \u2014 mixed into the kernel salt (defaults to .sail/account.json)"
43641
+ ).option("--salt <n>", "CREATE2 salt nonce (default: 0)").option("--chain <id>", "Show prediction for one chain only").option("--json", "Emit machine-readable JSON").action(actionWith(accountPredict));
43061
43642
  account.command("rotate-signer").description("Rotate the SMA's delegated signer (agent wallet) and re-approve its mandates").option("--sma <address>", "SMA to rotate (defaults to the active account)").option("--to <address>", "Rotate to an existing agent-wallet address instead of generating one").option("--generate", "Generate a fresh local agent wallet (default when --to is omitted)").option("--skip-reattach", "Do not re-approve the previously-attached mandates").option("--reattach-only", "Skip rotation; only re-approve mandates (resume after funding)").option("--json", "Machine-readable output").action(actionWith(rotateSigner));
43062
43643
  var mandate = program2.command("mandate").description("Manage mandates");
43063
43644
  mandate.command("prepare").description("Prepare a mandate draft for review and signing in the UI (MetaMask)").action(action(mandatePrepare));
@@ -43067,8 +43648,11 @@ mandate.command("attach").description("Register an already-deployed permission o
43067
43648
  mandate.command("deploy-clone").description("Deploy + register a standalone clone permission (e.g. boundedApprove) via the signing UI").requiredOption("--template <key>", "Standalone clone template key (e.g. boundedApprove)").requiredOption("--sma <address>", "SMA to deploy the clone for and register it on").option("--tokens <csv>", "Comma-separated allowed token addresses").option("--spenders <csv>", "Comma-separated allowed spender addresses").option("--max <amount>", "Max amount per tx in base units (default: uint256 max)").option("--label <label>", "Human-readable label to track this permission under").option("--json", "Emit machine-readable JSON").action(actionWith(mandateDeployClone));
43068
43649
  mandate.command("revoke").description("Revoke permission(s) from an SMA (EIP-712 RevokePermissions, owner-authorized)").option("--address <permissionOrName>", "Permission address, or a name tracked locally").requiredOption("--sma <address>", "Safe (SMA) to revoke the permission(s) from").option("--all", "Revoke every permission currently registered on the SMA").option("--json", "Output JSON").action(actionWith(mandateRevoke));
43069
43650
  mandate.command("templates").description("Show how to author your own permission contract (and any community-deployed addresses)").option("--json", "Emit machine-readable JSON").action(actionWith(mandateTemplates));
43651
+ mandate.command("simulate").description(
43652
+ "Probe a permission against sample calls off-chain (eth_call, NO gas) \u2014 prove it accepts the calls you want and rejects the ones you don't, before authorizing on-chain"
43653
+ ).requiredOption("--address <permissionOrName>", "Permission to probe (address or tracked name)").option("--sma <address>", "SMA to probe as (ctx.account; defaults to .sail/account.json)").option("--target <address>", "Inline single call: target contract address").option("--calldata <hex>", "Inline single call: 0x-prefixed calldata").option("--value <wei>", "Inline single call: ETH value in wei (default 0)").option("--expect <pass|fail>", "Inline single call: expected outcome (sets non-zero exit on mismatch)").option("--label <text>", "Inline single call: human-readable label").option("--calls <file>", "Batch: JSON array of { target, calldata, value?, expect?, label? }").option("--json", "Emit machine-readable JSON").action(actionWith(mandateSimulate));
43070
43654
  mandate.command("list").description("List permission contracts deployed from this project").action(action(async () => mandateContractsList()));
43071
- program2.command("onboard").description("Set up an SMA, register a permission, confirm the agent is operational").option("--sma <address>", "Use a specific SMA address instead of prompting").option("--new-sma", "Create a new SMA via SailKernel").option("--template <kindOrAddress>", "Register this permission contract (kind, label, or address)").option("--skip-mandate", "Skip the permission registration step").option("--json", "Emit machine-readable JSON (implies non-interactive)").action(actionWith(onboard));
43655
+ program2.command("onboard").description("Set up an SMA, register a permission, confirm the agent is operational").option("--sma <address>", "Use a specific SMA address instead of prompting").option("--new-sma", "Create a new SMA via SailKernel").option("--salt <n>", "CREATE2 salt for deterministic Safe address (default: 0; use 0 for first SMA, increment for subsequent)").option("--template <kindOrAddress>", "Register this permission contract (kind, label, or address)").option("--skip-mandate", "Skip the permission registration step").option("--json", "Emit machine-readable JSON (implies non-interactive)").action(actionWith(onboard));
43072
43656
  var station = program2.command("station").description("Manage the persistent signing station (browser signing daemon)");
43073
43657
  station.command("start").description("Start the signing station and keep it running (blocks \u2014 run in the background)").option("--json", "Emit machine-readable JSON").action(actionWith(stationStart));
43074
43658
  station.command("status").description("Show whether a signing station is running for this project").option("--json", "Emit machine-readable JSON").action(actionWith(stationStatus));