@dev.sail.money/sailor 1.3.0-218 → 1.3.0-219

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 (115) hide show
  1. package/AGENTS.md +3 -0
  2. package/package.json +1 -1
  3. package/packages/cli/dist/index.cjs +168 -76
  4. package/packages/cli/dist/server.cjs +57 -63
  5. package/packages/sdk/dist/abis/SailGovernance.d.ts +6 -5
  6. package/packages/sdk/dist/abis/SailGovernance.d.ts.map +1 -1
  7. package/packages/sdk/dist/abis/SailGovernance.js +6 -5
  8. package/packages/sdk/dist/abis/SailGovernance.js.map +1 -1
  9. package/packages/sdk/dist/fees.d.ts +58 -10
  10. package/packages/sdk/dist/fees.d.ts.map +1 -1
  11. package/packages/sdk/dist/fees.js +73 -41
  12. package/packages/sdk/dist/fees.js.map +1 -1
  13. package/packages/sdk/dist/index.d.ts +2 -1
  14. package/packages/sdk/dist/index.d.ts.map +1 -1
  15. package/packages/sdk/dist/index.js +1 -1
  16. package/packages/sdk/dist/index.js.map +1 -1
  17. package/packages/sdk/dist/intelligence.d.ts +1 -1
  18. package/packages/sdk/dist/intelligence.js +1 -1
  19. package/packages/sdk/dist/signing.d.ts +14 -0
  20. package/packages/sdk/dist/signing.d.ts.map +1 -1
  21. package/packages/ui/dist/assets/{add-DJjAPrBg.js → add-BX5reKtK.js} +1 -1
  22. package/packages/ui/dist/assets/{all-wallets-Drs0T9oK.js → all-wallets-IZ3PlVF-.js} +1 -1
  23. package/packages/ui/dist/assets/{app-store-DyeC8AHL.js → app-store-ARgRdMdF.js} +1 -1
  24. package/packages/ui/dist/assets/{apple-HIg_c1iA.js → apple-VCaqsI9w.js} +1 -1
  25. package/packages/ui/dist/assets/{arrow-bottom-DtgmHaSZ.js → arrow-bottom-Cwptw8fW.js} +1 -1
  26. package/packages/ui/dist/assets/{arrow-bottom-circle-CTLcFsn4.js → arrow-bottom-circle-h5vFiZ2I.js} +1 -1
  27. package/packages/ui/dist/assets/{arrow-left-CuNmTB1o.js → arrow-left-bM1TRcV9.js} +1 -1
  28. package/packages/ui/dist/assets/{arrow-right-FobP6UHJ.js → arrow-right-BYd_N4Jr.js} +1 -1
  29. package/packages/ui/dist/assets/{arrow-top-DHP1p-pT.js → arrow-top-CLUnwVE2.js} +1 -1
  30. package/packages/ui/dist/assets/{bank-toi-Q_Oj.js → bank-6c28yOM9.js} +1 -1
  31. package/packages/ui/dist/assets/{basic-DzWI9Vfe.js → basic-CJnbTfSd.js} +1 -1
  32. package/packages/ui/dist/assets/{browser-COPWe9NX.js → browser-CDKlxM3D.js} +1 -1
  33. package/packages/ui/dist/assets/{card--9zk2OLW.js → card-DQdED02q.js} +1 -1
  34. package/packages/ui/dist/assets/{ccip-Cj85PpbL.js → ccip-BL7RIPfY.js} +1 -1
  35. package/packages/ui/dist/assets/{checkmark-C_szyJ8W.js → checkmark-JoRNVoFV.js} +1 -1
  36. package/packages/ui/dist/assets/{checkmark-bold-DIH9SQJl.js → checkmark-bold-B6bGHVIs.js} +1 -1
  37. package/packages/ui/dist/assets/{chevron-bottom-OeowgNgU.js → chevron-bottom-BMCelIEu.js} +1 -1
  38. package/packages/ui/dist/assets/{chevron-left-qDAhAvoU.js → chevron-left-ryPLhHCc.js} +1 -1
  39. package/packages/ui/dist/assets/{chevron-right-B9GPX37d.js → chevron-right-CdDNRv7f.js} +1 -1
  40. package/packages/ui/dist/assets/{chevron-top-kKzmmVSf.js → chevron-top-BxWIjjGk.js} +1 -1
  41. package/packages/ui/dist/assets/{chrome-store-wLlhsnbW.js → chrome-store-D-fiJ9qJ.js} +1 -1
  42. package/packages/ui/dist/assets/{clock-BNGsyvtJ.js → clock-xCPkzwjj.js} +1 -1
  43. package/packages/ui/dist/assets/{close-1aknsCNm.js → close-BkqmPpWG.js} +1 -1
  44. package/packages/ui/dist/assets/{coinPlaceholder-By0PtVsf.js → coinPlaceholder-PIvN9oq_.js} +1 -1
  45. package/packages/ui/dist/assets/{compass-DG4TLiMj.js → compass-CW5kWcxW.js} +1 -1
  46. package/packages/ui/dist/assets/{copy-BABFWB1m.js → copy-DUZlAQJJ.js} +1 -1
  47. package/packages/ui/dist/assets/{core-DuAIm6Xv.js → core-von2c1sc.js} +3 -3
  48. package/packages/ui/dist/assets/cursor-BzRVY7BD.js +3 -0
  49. package/packages/ui/dist/assets/{cursor-transparent-gf5lT6qI.js → cursor-transparent-BUAMB0G1.js} +1 -1
  50. package/packages/ui/dist/assets/{desktop-B44_VjZB.js → desktop-BxdlwV8P.js} +1 -1
  51. package/packages/ui/dist/assets/{disconnect-L65dw6Bx.js → disconnect-GelNh3mn.js} +1 -1
  52. package/packages/ui/dist/assets/{discord-C1xMuNsC.js → discord-N0jnxkzC.js} +1 -1
  53. package/packages/ui/dist/assets/{etherscan-6VA7_I9A.js → etherscan-B41u7wF4.js} +1 -1
  54. package/packages/ui/dist/assets/{events-DBlbqBry.js → events-C9icG2YB.js} +1 -1
  55. package/packages/ui/dist/assets/{exclamation-triangle-Gs8XW8RO.js → exclamation-triangle-BWJC56FC.js} +1 -1
  56. package/packages/ui/dist/assets/{extension-D_YB41G_.js → extension-G6I6oogw.js} +1 -1
  57. package/packages/ui/dist/assets/{external-link-rBG5a7MZ.js → external-link-BPaGQf4a.js} +1 -1
  58. package/packages/ui/dist/assets/{facebook-Hn_b-JCP.js → facebook-47twy32b.js} +1 -1
  59. package/packages/ui/dist/assets/{fallback-BSNjxjhh.js → fallback-BTfJeGjh.js} +1 -1
  60. package/packages/ui/dist/assets/{farcaster-BvD_feGw.js → farcaster-DYqnqCqs.js} +1 -1
  61. package/packages/ui/dist/assets/{filters-B6b694Xy.js → filters-dF6xeJnE.js} +1 -1
  62. package/packages/ui/dist/assets/{github-B5zqgRW6.js → github-DbW4Ihz2.js} +1 -1
  63. package/packages/ui/dist/assets/{google-BSGxxnTQ.js → google-TKm7G8LF.js} +1 -1
  64. package/packages/ui/dist/assets/{help-circle-Ce1u0tMv.js → help-circle-CY3qTmsG.js} +1 -1
  65. package/packages/ui/dist/assets/{id-sk5zuKn_.js → id-JpHW4q14.js} +1 -1
  66. package/packages/ui/dist/assets/{image-BEcIv0W7.js → image-Dip2fG98.js} +1 -1
  67. package/packages/ui/dist/assets/{index-BXYreG94.js → index-BGJ_8fMk.js} +1 -1
  68. package/packages/ui/dist/assets/{index-C-9jC0nT.js → index-Bq3PAopp.js} +1 -1
  69. package/packages/ui/dist/assets/{index-iaA95CrO.js → index-C4_cA5XW.js} +1 -1
  70. package/packages/ui/dist/assets/{index-BV_O39t0.js → index-CaDN0gL4.js} +1 -1
  71. package/packages/ui/dist/assets/{index-BFuZ3vJV.js → index-DLElIcBr.js} +40 -40
  72. package/packages/ui/dist/assets/{index-BWJIDuJ2.js → index-q0mGElxZ.js} +3 -3
  73. package/packages/ui/dist/assets/{index.es-DSTQLxbd.js → index.es-BaCKS_tW.js} +4 -4
  74. package/packages/ui/dist/assets/{info-CKyiPci5.js → info-B1enFsqh.js} +1 -1
  75. package/packages/ui/dist/assets/{info-circle-CW0kDaAs.js → info-circle-D4j3FISs.js} +1 -1
  76. package/packages/ui/dist/assets/{lightbulb-bnSgd_F-.js → lightbulb-DZbKr-vS.js} +1 -1
  77. package/packages/ui/dist/assets/{mail-BGmbZV9J.js → mail-D-TO_uVQ.js} +1 -1
  78. package/packages/ui/dist/assets/{metamask-sdk-CzBEUGpm.js → metamask-sdk-DljW3QD1.js} +1 -1
  79. package/packages/ui/dist/assets/{mobile-Cbh9WnoA.js → mobile-Bkmwzav2.js} +1 -1
  80. package/packages/ui/dist/assets/{more-BBoDLkz_.js → more-D1_I_jiB.js} +1 -1
  81. package/packages/ui/dist/assets/{network-placeholder-BaWfPSVc.js → network-placeholder-DUaFjoXh.js} +1 -1
  82. package/packages/ui/dist/assets/{nftPlaceholder-C-o5NTld.js → nftPlaceholder-Cc0iUryh.js} +1 -1
  83. package/packages/ui/dist/assets/{off-DvAzX5ic.js → off-BvX71sse.js} +1 -1
  84. package/packages/ui/dist/assets/{parseSignature-DJKQlxwH.js → parseSignature-BAS-wKoj.js} +1 -1
  85. package/packages/ui/dist/assets/{play-store-BuHRHQMo.js → play-store-XzLbtbTd.js} +1 -1
  86. package/packages/ui/dist/assets/{plus-DP7LMX2W.js → plus-B77_RmYR.js} +1 -1
  87. package/packages/ui/dist/assets/{qr-code-DaotBaRn.js → qr-code-BNeIhB1d.js} +1 -1
  88. package/packages/ui/dist/assets/{recycle-horizontal-BOBLBM5F.js → recycle-horizontal-B9sX-yty.js} +1 -1
  89. package/packages/ui/dist/assets/{refresh-B9Uah1lr.js → refresh-BWRygnjm.js} +1 -1
  90. package/packages/ui/dist/assets/{reown-logo-D-A6gikk.js → reown-logo-DCAEsGFm.js} +1 -1
  91. package/packages/ui/dist/assets/{search-CLf0oNq0.js → search-ZXIqrawS.js} +1 -1
  92. package/packages/ui/dist/assets/{secp256k1-o2-PEMaD.js → secp256k1-Doz05h4g.js} +1 -1
  93. package/packages/ui/dist/assets/{send-zibdNMYZ.js → send-BQcO0jNC.js} +1 -1
  94. package/packages/ui/dist/assets/{swapHorizontal-BKyVtQb4.js → swapHorizontal-4yZEzz2K.js} +1 -1
  95. package/packages/ui/dist/assets/{swapHorizontalBold-D_yV6FhX.js → swapHorizontalBold-DxBKu3sl.js} +1 -1
  96. package/packages/ui/dist/assets/{swapHorizontalMedium-CSG0LNdP.js → swapHorizontalMedium-DAQYJ2JK.js} +1 -1
  97. package/packages/ui/dist/assets/{swapHorizontalRoundedBold-DlYMEubo.js → swapHorizontalRoundedBold-CUlZzFJ6.js} +1 -1
  98. package/packages/ui/dist/assets/{swapVertical-CB0WGXhs.js → swapVertical-CjWHAaDM.js} +1 -1
  99. package/packages/ui/dist/assets/{telegram-B4eWMDad.js → telegram-JBeabiJP.js} +1 -1
  100. package/packages/ui/dist/assets/{three-dots-CSqXQX0E.js → three-dots-D3R8OjSx.js} +1 -1
  101. package/packages/ui/dist/assets/{twitch-Dfpk5rqx.js → twitch-1f9z7xVO.js} +1 -1
  102. package/packages/ui/dist/assets/{twitterIcon-Bngk-UC7.js → twitterIcon-BW7n2xnp.js} +1 -1
  103. package/packages/ui/dist/assets/{verify-CkV7ea2T.js → verify-Cs5owI38.js} +1 -1
  104. package/packages/ui/dist/assets/{verify-filled-CAtmQoCe.js → verify-filled-Ci5sx5qS.js} +1 -1
  105. package/packages/ui/dist/assets/{w3m-modal-CRVkWGCV.js → w3m-modal-DnWtss8u.js} +1 -1
  106. package/packages/ui/dist/assets/{wallet-bjwMa6sZ.js → wallet-MaGNqaW-.js} +1 -1
  107. package/packages/ui/dist/assets/{wallet-placeholder-EXnYyVfX.js → wallet-placeholder-kTOy12_L.js} +1 -1
  108. package/packages/ui/dist/assets/{walletconnect-B8lXOjAB.js → walletconnect-CyuP92jg.js} +1 -1
  109. package/packages/ui/dist/assets/{warning-circle-CmPwRjL_.js → warning-circle-BImUCdpG.js} +1 -1
  110. package/packages/ui/dist/assets/{x-CCLsfIR6.js → x-II45JHpV.js} +1 -1
  111. package/packages/ui/dist/index.html +1 -1
  112. package/templates/default/.agents/skills/sail-mandates/SKILL.md +12 -0
  113. package/templates/default/.agents/skills/sail-onboarding/SKILL.md +1 -1
  114. package/templates/default/.agents/skills/sail-project-info/SKILL.md +2 -0
  115. package/packages/ui/dist/assets/cursor-9u-UhLUH.js +0 -3
package/AGENTS.md CHANGED
@@ -73,8 +73,11 @@ addresses remain. This file is the source of truth this guide mirrors.
73
73
  | `packages/cli/src/commands/onboard.ts` | SMA creation + permission registration flow |
74
74
  | `packages/cli/src/commands/mandate-contracts.ts` | Deploy / attach / revoke permission contracts |
75
75
  | `packages/cli/src/lib/mandates.ts` | `MandateStore` — `.sail/state/mandates.json` source of truth |
76
+ | `packages/sdk/src/fees.ts` | `readPermissionRegistrationFee` (live governance read) + per-permission fee math/disclosure/preflight |
76
77
  | `packages/ui/server.js` | Local API + WebSocket proxy; signing station relay |
77
78
 
79
+ Per-permission registration fee (read live from governance, surfaced at sign time / activity / preflight): see `templates/default/.agents/skills/sail-mandates/SKILL.md` → "Registration fee".
80
+
78
81
  ## Build
79
82
 
80
83
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dev.sail.money/sailor",
3
- "version": "1.3.0-218",
3
+ "version": "1.3.0-219",
4
4
  "description": "Operator toolkit for Sail Protocol",
5
5
  "bin": {
6
6
  "sailor": "packages/cli/dist/index.cjs"
@@ -4581,8 +4581,8 @@ var init_encoding = __esm({
4581
4581
  "../../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/errors/encoding.js"() {
4582
4582
  init_base();
4583
4583
  IntegerOutOfRangeError = class extends BaseError2 {
4584
- constructor({ max, min: min2, signed, size: size5, value }) {
4585
- super(`Number "${value}" is not in safe ${size5 ? `${size5 * 8}-bit ${signed ? "signed" : "unsigned"} ` : ""}integer range ${max ? `(${min2} to ${max})` : `(above ${min2})`}`, { name: "IntegerOutOfRangeError" });
4584
+ constructor({ max, min, signed, size: size5, value }) {
4585
+ super(`Number "${value}" is not in safe ${size5 ? `${size5 * 8}-bit ${signed ? "signed" : "unsigned"} ` : ""}integer range ${max ? `(${min} to ${max})` : `(above ${min})`}`, { name: "IntegerOutOfRangeError" });
4586
4586
  }
4587
4587
  };
4588
4588
  InvalidBytesBooleanError = class extends BaseError2 {
@@ -4945,9 +4945,9 @@ function aexists(instance, checkFinished = true) {
4945
4945
  }
4946
4946
  function aoutput(out, instance) {
4947
4947
  abytes(out);
4948
- const min2 = instance.outputLen;
4949
- if (out.length < min2) {
4950
- throw new Error("digestInto() expects output buffer of length at least " + min2);
4948
+ const min = instance.outputLen;
4949
+ if (out.length < min) {
4950
+ throw new Error("digestInto() expects output buffer of length at least " + min);
4951
4951
  }
4952
4952
  }
4953
4953
  function u32(arr) {
@@ -5751,11 +5751,11 @@ function encodeBool(value) {
5751
5751
  function encodeNumber(value, { signed, size: size5 = 256 }) {
5752
5752
  if (typeof size5 === "number") {
5753
5753
  const max = 2n ** (BigInt(size5) - (signed ? 1n : 0n)) - 1n;
5754
- const min2 = signed ? -max - 1n : 0n;
5755
- if (value > max || value < min2)
5754
+ const min = signed ? -max - 1n : 0n;
5755
+ if (value > max || value < min)
5756
5756
  throw new IntegerOutOfRangeError({
5757
5757
  max: max.toString(),
5758
- min: min2.toString(),
5758
+ min: min.toString(),
5759
5759
  signed,
5760
5760
  size: size5 / 8,
5761
5761
  value: value.toString()
@@ -8574,12 +8574,12 @@ function utf8ToBytes2(str) {
8574
8574
  throw new Error("string expected");
8575
8575
  return new Uint8Array(new TextEncoder().encode(str));
8576
8576
  }
8577
- function inRange(n, min2, max) {
8578
- return isPosBig(n) && isPosBig(min2) && isPosBig(max) && min2 <= n && n < max;
8577
+ function inRange(n, min, max) {
8578
+ return isPosBig(n) && isPosBig(min) && isPosBig(max) && min <= n && n < max;
8579
8579
  }
8580
- function aInRange(title, n, min2, max) {
8581
- if (!inRange(n, min2, max))
8582
- throw new Error("expected valid " + title + ": " + min2 + " <= n < " + max + ", got " + n);
8580
+ function aInRange(title, n, min, max) {
8581
+ if (!inRange(n, min, max))
8582
+ throw new Error("expected valid " + title + ": " + min + " <= n < " + max + ", got " + n);
8583
8583
  }
8584
8584
  function bitLen(n) {
8585
8585
  let len;
@@ -13526,8 +13526,8 @@ var init_Hex = __esm({
13526
13526
  encoder4 = /* @__PURE__ */ new TextEncoder();
13527
13527
  hexes4 = /* @__PURE__ */ Array.from({ length: 256 }, (_v, i) => i.toString(16).padStart(2, "0"));
13528
13528
  IntegerOutOfRangeError2 = class extends BaseError3 {
13529
- constructor({ max, min: min2, signed, size: size5, value }) {
13530
- super(`Number \`${value}\` is not in safe${size5 ? ` ${size5 * 8}-bit` : ""}${signed ? " signed" : " unsigned"} integer range ${max ? `(\`${min2}\` to \`${max}\`)` : `(above \`${min2}\`)`}`);
13529
+ constructor({ max, min, signed, size: size5, value }) {
13530
+ super(`Number \`${value}\` is not in safe${size5 ? ` ${size5 * 8}-bit` : ""}${signed ? " signed" : " unsigned"} integer range ${max ? `(\`${min}\` to \`${max}\`)` : `(above \`${min}\`)`}`);
13531
13531
  Object.defineProperty(this, "name", {
13532
13532
  enumerable: true,
13533
13533
  configurable: true,
@@ -25269,11 +25269,11 @@ function encodeBoolean(value) {
25269
25269
  function encodeNumber2(value, { signed, size: size5 }) {
25270
25270
  if (typeof size5 === "number") {
25271
25271
  const max = 2n ** (BigInt(size5) - (signed ? 1n : 0n)) - 1n;
25272
- const min2 = signed ? -max - 1n : 0n;
25273
- if (value > max || value < min2)
25272
+ const min = signed ? -max - 1n : 0n;
25273
+ if (value > max || value < min)
25274
25274
  throw new IntegerOutOfRangeError2({
25275
25275
  max: max.toString(),
25276
- min: min2.toString(),
25276
+ min: min.toString(),
25277
25277
  signed,
25278
25278
  size: size5 / 8,
25279
25279
  value: value.toString()
@@ -37811,38 +37811,43 @@ async function discoverSafesForOwner(owner2, chainId) {
37811
37811
  }
37812
37812
 
37813
37813
  // ../sdk/dist/fees.js
37814
- function min(a, b) {
37815
- return a < b ? a : b;
37814
+ init_esm2();
37815
+ async function readPermissionRegistrationFee(publicClient, governance) {
37816
+ return publicClient.readContract({
37817
+ address: governance,
37818
+ abi: SailGovernanceAbi,
37819
+ functionName: "permissionRegistrationFee"
37820
+ });
37816
37821
  }
37817
- async function estimatePermissionFee(publicClient, governance, permission) {
37818
- try {
37819
- const [baseFee, complexityRate, cap, code] = await Promise.all([
37820
- publicClient.readContract({
37821
- address: governance,
37822
- abi: SailGovernanceAbi,
37823
- functionName: "baseFee"
37824
- }),
37825
- publicClient.readContract({
37826
- address: governance,
37827
- abi: SailGovernanceAbi,
37828
- functionName: "complexityRate"
37829
- }),
37830
- publicClient.readContract({
37831
- address: governance,
37832
- abi: SailGovernanceAbi,
37833
- functionName: "MAX_PERMISSION_FEE_WEI"
37834
- }),
37835
- publicClient.getBytecode({ address: permission })
37836
- ]);
37837
- const byteLength = code ? BigInt((code.length - 2) / 2) : 0n;
37838
- const fee = min(baseFee, cap) + min(byteLength * complexityRate, cap);
37839
- return min(fee, cap);
37840
- } catch {
37841
- return publicClient.readContract({
37842
- address: governance,
37843
- abi: SailGovernanceAbi,
37844
- functionName: "permissionRegistrationFee"
37845
- });
37822
+ async function estimateMandateRegistrationFee(publicClient, governance, permissions) {
37823
+ const flatFeeWei = await readPermissionRegistrationFee(publicClient, governance);
37824
+ const perPermission = permissions.map((permission) => ({ permission, feeWei: flatFeeWei }));
37825
+ const totalWei = flatFeeWei * BigInt(permissions.length);
37826
+ return { totalWei, perPermission };
37827
+ }
37828
+ function describeMandateFee(estimate) {
37829
+ const count = estimate.perPermission.length;
37830
+ if (count === 0)
37831
+ return "Registration fee: 0 ETH (no new permissions to register)";
37832
+ const noun = count === 1 ? "permission" : "permissions";
37833
+ const perFeeWei = estimate.perPermission[0].feeWei;
37834
+ return `Registration fee: ${formatEther(estimate.totalWei)} ETH (${count} ${noun} \xD7 ${formatEther(perFeeWei)} ETH)`;
37835
+ }
37836
+ var RegistrationFeeError = class extends Error {
37837
+ /** Total fee required (wei). */
37838
+ requiredWei;
37839
+ /** Signer balance available (wei). */
37840
+ balanceWei;
37841
+ constructor(message, requiredWei, balanceWei) {
37842
+ super(message);
37843
+ this.name = "RegistrationFeeError";
37844
+ this.requiredWei = requiredWei;
37845
+ this.balanceWei = balanceWei;
37846
+ }
37847
+ };
37848
+ function assertFeeAffordable(balanceWei, totalFeeWei) {
37849
+ if (balanceWei < totalFeeWei) {
37850
+ throw new RegistrationFeeError(`Insufficient ETH for the ${formatEther(totalFeeWei)} ETH registration fee; signer balance is ${formatEther(balanceWei)} ETH.`, totalFeeWei, balanceWei);
37846
37851
  }
37847
37852
  }
37848
37853
 
@@ -41438,6 +41443,21 @@ var MandateStore = class {
41438
41443
 
41439
41444
  // src/commands/onboard.ts
41440
41445
  init_esm2();
41446
+
41447
+ // src/lib/registration-fee.ts
41448
+ function registrationGate(args) {
41449
+ const { estimate, agentBalanceWei } = args;
41450
+ if (agentBalanceWei !== void 0) {
41451
+ assertFeeAffordable(agentBalanceWei, estimate.totalWei);
41452
+ }
41453
+ return {
41454
+ totalFeeWei: estimate.totalWei,
41455
+ permissionCount: estimate.perPermission.length,
41456
+ disclosure: describeMandateFee(estimate)
41457
+ };
41458
+ }
41459
+
41460
+ // src/commands/onboard.ts
41441
41461
  async function onboard(options) {
41442
41462
  if (!ProjectContext.exists()) {
41443
41463
  emit(options.json, () => console.log('No Sailor project found. Run "sailor init" first.'), {
@@ -41816,6 +41836,17 @@ async function attachMandate(project, channel, publicClient, agentSigner, smaAdd
41816
41836
  hasDeadline: registerPermissionHasDeadline,
41817
41837
  deadline: registrationDeadline
41818
41838
  });
41839
+ const feeEstimate = await estimateMandateRegistrationFee(
41840
+ publicClient,
41841
+ project.contracts.governance,
41842
+ [templateAddress]
41843
+ );
41844
+ const fee = feeEstimate.totalWei;
41845
+ const agentBalanceWei = await publicClient.getBalance({
41846
+ address: agentSigner.viemAccount.address
41847
+ });
41848
+ const gate = registrationGate({ estimate: feeEstimate, agentBalanceWei });
41849
+ say(() => console.log(gate.disclosure));
41819
41850
  say(() => console.log(`
41820
41851
  Pushing signing request for "${template.label}" permission\u2026`));
41821
41852
  say(
@@ -41838,6 +41869,13 @@ Pushing signing request for "${template.label}" permission\u2026`));
41838
41869
  { label: "Mandate signer", value: permissionSigner }
41839
41870
  ],
41840
41871
  explanation: permExplanation,
41872
+ // Disclose the fee in the signing card so the owner sees what the agent
41873
+ // will pay before signing. Only included when non-zero or when there is a
41874
+ // fee to disclose (zero-fee chains pass 0 ETH, still informative).
41875
+ registrationFee: {
41876
+ totalEth: formatEther(fee),
41877
+ permissionCount: 1
41878
+ },
41841
41879
  typedData
41842
41880
  });
41843
41881
  if (response.status === "rejected") {
@@ -41864,12 +41902,6 @@ Connect the owner wallet (mandate signer) in the browser \u2014 the agent wallet
41864
41902
  } catch (err) {
41865
41903
  if (err.message.startsWith("Security:")) throw err;
41866
41904
  }
41867
- say(() => console.log("Estimating permission fee\u2026"));
41868
- const fee = await estimatePermissionFee(
41869
- publicClient,
41870
- project.contracts.governance,
41871
- templateAddress
41872
- );
41873
41905
  say(() => console.log(`Submitting mandate registration (agent pays gas; fee ${fee} wei)\u2026`));
41874
41906
  const walletClient = createWalletClient({
41875
41907
  account: agentSigner.viemAccount,
@@ -41919,7 +41951,10 @@ Connect the owner wallet (mandate signer) in the browser \u2014 the agent wallet
41919
41951
  name: template.label,
41920
41952
  sma: smaAddress,
41921
41953
  txHash,
41922
- chainId: project.chainId
41954
+ chainId: project.chainId,
41955
+ // Registration fee actually paid by the agent for this permission.
41956
+ fee: fee.toString(),
41957
+ feeEth: formatEther(fee)
41923
41958
  });
41924
41959
  return txHash;
41925
41960
  }
@@ -42336,6 +42371,7 @@ ${spec.label} clone (${options.template})`);
42336
42371
  hasDeadline: registerPermissionHasDeadline,
42337
42372
  deadline
42338
42373
  });
42374
+ const fee = await readPermissionRegistrationFee(publicClient, project.contracts.governance);
42339
42375
  const label = options.label ?? `${spec.label} (${options.template})`;
42340
42376
  say(
42341
42377
  () => console.log(
@@ -42355,6 +42391,10 @@ ${spec.label} clone (${options.template})`);
42355
42391
  { label: "Mandate signer", value: permissionSigner },
42356
42392
  ...spec.describe(initParams)
42357
42393
  ],
42394
+ registrationFee: {
42395
+ totalEth: formatEther(fee),
42396
+ permissionCount: 1
42397
+ },
42358
42398
  typedData
42359
42399
  });
42360
42400
  if (response.status === "rejected") {
@@ -42381,7 +42421,6 @@ Connect the owner wallet (mandate signer) in the browser \u2014 the agent wallet
42381
42421
  } catch (err) {
42382
42422
  if (err.message.startsWith("Security:")) throw err;
42383
42423
  }
42384
- const fee = await estimatePermissionFee(publicClient, project.contracts.governance, clone);
42385
42424
  if (!registerPermissionHasDeadline || deadline === void 0) {
42386
42425
  throw new Error(
42387
42426
  "deploy-clone requires a selective kernel (RegisterPermission with deadline). This chain's kernel does not match."
@@ -42434,7 +42473,10 @@ Connect the owner wallet (mandate signer) in the browser \u2014 the agent wallet
42434
42473
  name: storedClone.name,
42435
42474
  sma,
42436
42475
  txHash,
42437
- chainId: project.chainId
42476
+ chainId: project.chainId,
42477
+ // Registration fee actually paid by the agent for this permission.
42478
+ fee: fee.toString(),
42479
+ feeEth: formatEther(fee)
42438
42480
  });
42439
42481
  emit(json, () => {
42440
42482
  }, {
@@ -42753,6 +42795,8 @@ Attaching ${permissions.length} permissions in one signature \u2014 the mandate
42753
42795
  explanation: (rec ? explainPermission(rec.name, rec.sourcePath) : null) ?? void 0
42754
42796
  };
42755
42797
  });
42798
+ const flatFee = await readPermissionRegistrationFee(publicClient, project.contracts.governance);
42799
+ const fee = flatFee * BigInt(permissions.length);
42756
42800
  const response = await channel.requestSignature({
42757
42801
  type: "typed-data",
42758
42802
  kind: "register-permission",
@@ -42765,6 +42809,11 @@ Attaching ${permissions.length} permissions in one signature \u2014 the mandate
42765
42809
  { label: "Mandate signer", value: permissionSigner }
42766
42810
  ],
42767
42811
  permissions: permExplanations,
42812
+ registrationFee: {
42813
+ totalEth: formatEther(fee),
42814
+ permissionCount: permissions.length,
42815
+ ...permissions.length > 1 ? { perPermissionEth: formatEther(flatFee) } : {}
42816
+ },
42768
42817
  typedData
42769
42818
  });
42770
42819
  if (response.status === "rejected") {
@@ -42773,10 +42822,6 @@ Attaching ${permissions.length} permissions in one signature \u2014 the mandate
42773
42822
  if (response.status !== "signature") {
42774
42823
  throw new Error(`Expected an EIP-712 signature response, got: ${response.status}`);
42775
42824
  }
42776
- let fee = 0n;
42777
- for (const permission of permissions) {
42778
- fee += await estimatePermissionFee(publicClient, project.contracts.governance, permission);
42779
- }
42780
42825
  const chain2 = getChainById(project.chainId);
42781
42826
  const walletClient = createWalletClient({
42782
42827
  account: agentSigner.viemAccount,
@@ -42809,7 +42854,10 @@ Attaching ${permissions.length} permissions in one signature \u2014 the mandate
42809
42854
  permission,
42810
42855
  sma,
42811
42856
  txHash,
42812
- chainId: project.chainId
42857
+ chainId: project.chainId,
42858
+ // Each permission in the batch is charged the same flat fee.
42859
+ fee: flatFee.toString(),
42860
+ feeEth: formatEther(flatFee)
42813
42861
  });
42814
42862
  say(() => {
42815
42863
  if (!present) {
@@ -43021,6 +43069,35 @@ function runForgeBuild() {
43021
43069
 
43022
43070
  // src/commands/mandate.ts
43023
43071
  init_esm2();
43072
+ function chargeablePermissions(tracked) {
43073
+ return tracked.filter((p) => !p.revokedOnChain && !p.registeredOnSma);
43074
+ }
43075
+ async function liveMandateFee(chainId, permissionAddresses) {
43076
+ if (permissionAddresses.length === 0) return { totalWei: 0n, perPermission: [] };
43077
+ try {
43078
+ const deployment = getSailDeployment(chainId);
43079
+ const rpcUrl = getRpcUrl(chainId) ?? getChainById(chainId).rpcUrls.default.http[0];
43080
+ const pc = createPublicClient({ chain: getChainById(chainId), transport: http(rpcUrl) });
43081
+ return await estimateMandateRegistrationFee(
43082
+ pc,
43083
+ deployment.governance,
43084
+ permissionAddresses
43085
+ );
43086
+ } catch {
43087
+ return null;
43088
+ }
43089
+ }
43090
+ function draftFeeFromEstimate(estimate) {
43091
+ const perFeeWei = estimate.perPermission[0]?.feeWei ?? 0n;
43092
+ return {
43093
+ perPermissionWei: perFeeWei.toString(),
43094
+ perPermissionEth: formatEther(perFeeWei),
43095
+ totalWei: estimate.totalWei.toString(),
43096
+ totalEth: formatEther(estimate.totalWei),
43097
+ permissionCount: estimate.perPermission.length,
43098
+ disclosure: describeMandateFee(estimate)
43099
+ };
43100
+ }
43024
43101
  function resolveActiveChain(account2) {
43025
43102
  try {
43026
43103
  const project = new ProjectContext();
@@ -43093,19 +43170,28 @@ ${permissions.length} permission(s) tracked for SMA ${account2.safe}:
43093
43170
  console.log(` ${status2}`);
43094
43171
  }
43095
43172
  const store = new MandateStore();
43173
+ const chargeable = chargeablePermissions(permissions);
43174
+ const draftPermissions = chargeable.map((p) => {
43175
+ const mandate2 = store.find(p.address);
43176
+ const explanation = explainPermission(p.label, mandate2?.sourcePath) ?? void 0;
43177
+ return { address: p.address, label: p.label, explanation };
43178
+ });
43179
+ let registrationFee;
43180
+ const unregisteredAddresses = chargeable.map((p) => p.address);
43181
+ if (unregisteredAddresses.length > 0) {
43182
+ const estimate = await liveMandateFee(chainId, unregisteredAddresses);
43183
+ if (estimate !== null) registrationFee = draftFeeFromEstimate(estimate);
43184
+ }
43096
43185
  const draft = {
43097
43186
  account: account2.safe,
43098
43187
  chainId,
43099
- // Only permissions not already registered on this SMA belong in the signing
43100
- // draft re-registering an already-registered permission reverts on-chain.
43101
- permissions: permissions.filter((p) => !p.revokedOnChain && !p.registeredOnSma).map((p) => {
43102
- const mandate2 = store.find(p.address);
43103
- const explanation = explainPermission(p.label, mandate2?.sourcePath) ?? void 0;
43104
- return { address: p.address, label: p.label, explanation };
43105
- }),
43106
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
43188
+ permissions: draftPermissions,
43189
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
43190
+ ...registrationFee ? { registrationFee } : {}
43107
43191
  };
43108
43192
  writeJsonFile(sailPath("mandate-draft.json"), draft);
43193
+ if (registrationFee) console.log(`
43194
+ ${registrationFee.disclosure}`);
43109
43195
  console.log("\nDraft written to .sail/mandate-draft.json for the UI to display.");
43110
43196
  }
43111
43197
  async function mandateSign(opts = {}) {
@@ -43132,6 +43218,15 @@ Permissions tracked for SMA ${account2.safe}:
43132
43218
  "\nNote: `sailor mandate sign` reviews and confirms the permissions attached to your SMA.\nOn-chain registration happens via `sailor mandate attach` (or `sailor mandate deploy --attach`)."
43133
43219
  );
43134
43220
  const activePermissions = permissions.filter((p) => !p.revokedOnChain);
43221
+ const unregistered = chargeablePermissions(permissions);
43222
+ if (unregistered.length > 0) {
43223
+ const estimate = await liveMandateFee(chainId, unregistered.map((p) => p.address));
43224
+ if (estimate !== null) {
43225
+ console.log(`
43226
+ ${describeMandateFee(estimate)}`);
43227
+ console.log(" Paid by the agent wallet on registration, per permission.");
43228
+ }
43229
+ }
43135
43230
  const proceed = opts.yes || await confirm(
43136
43231
  `Confirm these ${activePermissions.length} permission(s) are authorized for your SMA?`
43137
43232
  );
@@ -43139,7 +43234,6 @@ Permissions tracked for SMA ${account2.safe}:
43139
43234
  console.log("No permissions confirmed.");
43140
43235
  return;
43141
43236
  }
43142
- const unregistered = activePermissions.filter((p) => !p.registeredOnSma);
43143
43237
  if (unregistered.length === 0) {
43144
43238
  console.log(`
43145
43239
  \u2713 Confirmed ${activePermissions.length} permission(s) for ${account2.safe}.`);
@@ -43681,10 +43775,8 @@ Re-approving ${permissions.length} mandate(s) \u2014 the mandate signer (${permi
43681
43775
  if (response.status !== "signature") {
43682
43776
  throw new Error(`Expected an EIP-712 signature response, got: ${response.status}`);
43683
43777
  }
43684
- let fee = 0n;
43685
- for (const permission of permissions) {
43686
- fee += await estimatePermissionFee(publicClient, project.contracts.governance, permission);
43687
- }
43778
+ const flatFee = await readPermissionRegistrationFee(publicClient, project.contracts.governance);
43779
+ const fee = flatFee * BigInt(permissions.length);
43688
43780
  const agentSigner = await loadManagerSigner();
43689
43781
  const chain2 = getChainById(project.chainId);
43690
43782
  const walletClient = createWalletClient({
@@ -15943,7 +15943,7 @@ var require_get_intrinsic = __commonJS({
15943
15943
  var abs = require_abs();
15944
15944
  var floor = require_floor();
15945
15945
  var max = require_max();
15946
- var min2 = require_min();
15946
+ var min = require_min();
15947
15947
  var pow = require_pow();
15948
15948
  var round = require_round();
15949
15949
  var sign3 = require_sign();
@@ -16057,7 +16057,7 @@ var require_get_intrinsic = __commonJS({
16057
16057
  "%Math.abs%": abs,
16058
16058
  "%Math.floor%": floor,
16059
16059
  "%Math.max%": max,
16060
- "%Math.min%": min2,
16060
+ "%Math.min%": min,
16061
16061
  "%Math.pow%": pow,
16062
16062
  "%Math.round%": round,
16063
16063
  "%Math.sign%": sign3,
@@ -22055,12 +22055,12 @@ var require_cookie = __commonJS({
22055
22055
  } while (++index2 < max);
22056
22056
  return max;
22057
22057
  }
22058
- function endIndex(str, index2, min2) {
22059
- while (index2 > min2) {
22058
+ function endIndex(str, index2, min) {
22059
+ while (index2 > min) {
22060
22060
  var code = str.charCodeAt(--index2);
22061
22061
  if (code !== 32 && code !== 9) return index2 + 1;
22062
22062
  }
22063
- return min2;
22063
+ return min;
22064
22064
  }
22065
22065
  function serialize(name, val, opt) {
22066
22066
  var enc = opt && opt.encode || encodeURIComponent;
@@ -26953,8 +26953,8 @@ var init_encoding = __esm({
26953
26953
  "../../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/errors/encoding.js"() {
26954
26954
  init_base();
26955
26955
  IntegerOutOfRangeError = class extends BaseError {
26956
- constructor({ max, min: min2, signed, size: size6, value }) {
26957
- super(`Number "${value}" is not in safe ${size6 ? `${size6 * 8}-bit ${signed ? "signed" : "unsigned"} ` : ""}integer range ${max ? `(${min2} to ${max})` : `(above ${min2})`}`, { name: "IntegerOutOfRangeError" });
26956
+ constructor({ max, min, signed, size: size6, value }) {
26957
+ super(`Number "${value}" is not in safe ${size6 ? `${size6 * 8}-bit ${signed ? "signed" : "unsigned"} ` : ""}integer range ${max ? `(${min} to ${max})` : `(above ${min})`}`, { name: "IntegerOutOfRangeError" });
26958
26958
  }
26959
26959
  };
26960
26960
  SizeOverflowError = class extends BaseError {
@@ -27265,9 +27265,9 @@ function aexists(instance, checkFinished = true) {
27265
27265
  }
27266
27266
  function aoutput(out, instance) {
27267
27267
  abytes(out);
27268
- const min2 = instance.outputLen;
27269
- if (out.length < min2) {
27270
- throw new Error("digestInto() expects output buffer of length at least " + min2);
27268
+ const min = instance.outputLen;
27269
+ if (out.length < min) {
27270
+ throw new Error("digestInto() expects output buffer of length at least " + min);
27271
27271
  }
27272
27272
  }
27273
27273
  function u32(arr) {
@@ -28063,11 +28063,11 @@ function encodeBool(value) {
28063
28063
  function encodeNumber(value, { signed, size: size6 = 256 }) {
28064
28064
  if (typeof size6 === "number") {
28065
28065
  const max = 2n ** (BigInt(size6) - (signed ? 1n : 0n)) - 1n;
28066
- const min2 = signed ? -max - 1n : 0n;
28067
- if (value > max || value < min2)
28066
+ const min = signed ? -max - 1n : 0n;
28067
+ if (value > max || value < min)
28068
28068
  throw new IntegerOutOfRangeError({
28069
28069
  max: max.toString(),
28070
- min: min2.toString(),
28070
+ min: min.toString(),
28071
28071
  signed,
28072
28072
  size: size6 / 8,
28073
28073
  value: value.toString()
@@ -29898,12 +29898,12 @@ function utf8ToBytes2(str) {
29898
29898
  throw new Error("string expected");
29899
29899
  return new Uint8Array(new TextEncoder().encode(str));
29900
29900
  }
29901
- function inRange(n, min2, max) {
29902
- return isPosBig(n) && isPosBig(min2) && isPosBig(max) && min2 <= n && n < max;
29901
+ function inRange(n, min, max) {
29902
+ return isPosBig(n) && isPosBig(min) && isPosBig(max) && min <= n && n < max;
29903
29903
  }
29904
- function aInRange(title, n, min2, max) {
29905
- if (!inRange(n, min2, max))
29906
- throw new Error("expected valid " + title + ": " + min2 + " <= n < " + max + ", got " + n);
29904
+ function aInRange(title, n, min, max) {
29905
+ if (!inRange(n, min, max))
29906
+ throw new Error("expected valid " + title + ": " + min + " <= n < " + max + ", got " + n);
29907
29907
  }
29908
29908
  function bitLen(n) {
29909
29909
  let len;
@@ -34260,8 +34260,8 @@ var init_encoding2 = __esm({
34260
34260
  "../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@3.25.76/node_modules/viem/_esm/errors/encoding.js"() {
34261
34261
  init_base2();
34262
34262
  IntegerOutOfRangeError2 = class extends BaseError3 {
34263
- constructor({ max, min: min2, signed, size: size6, value }) {
34264
- super(`Number "${value}" is not in safe ${size6 ? `${size6 * 8}-bit ${signed ? "signed" : "unsigned"} ` : ""}integer range ${max ? `(${min2} to ${max})` : `(above ${min2})`}`, { name: "IntegerOutOfRangeError" });
34263
+ constructor({ max, min, signed, size: size6, value }) {
34264
+ super(`Number "${value}" is not in safe ${size6 ? `${size6 * 8}-bit ${signed ? "signed" : "unsigned"} ` : ""}integer range ${max ? `(${min} to ${max})` : `(above ${min})`}`, { name: "IntegerOutOfRangeError" });
34265
34265
  }
34266
34266
  };
34267
34267
  InvalidBytesBooleanError = class extends BaseError3 {
@@ -34990,11 +34990,11 @@ function encodeBool2(value) {
34990
34990
  function encodeNumber2(value, { signed, size: size6 = 256 }) {
34991
34991
  if (typeof size6 === "number") {
34992
34992
  const max = 2n ** (BigInt(size6) - (signed ? 1n : 0n)) - 1n;
34993
- const min2 = signed ? -max - 1n : 0n;
34994
- if (value > max || value < min2)
34993
+ const min = signed ? -max - 1n : 0n;
34994
+ if (value > max || value < min)
34995
34995
  throw new IntegerOutOfRangeError2({
34996
34996
  max: max.toString(),
34997
- min: min2.toString(),
34997
+ min: min.toString(),
34998
34998
  signed,
34999
34999
  size: size6 / 8,
35000
35000
  value: value.toString()
@@ -38094,8 +38094,8 @@ var init_Hex = __esm({
38094
38094
  encoder6 = /* @__PURE__ */ new TextEncoder();
38095
38095
  hexes5 = /* @__PURE__ */ Array.from({ length: 256 }, (_v, i) => i.toString(16).padStart(2, "0"));
38096
38096
  IntegerOutOfRangeError3 = class extends BaseError4 {
38097
- constructor({ max, min: min2, signed, size: size6, value }) {
38098
- super(`Number \`${value}\` is not in safe${size6 ? ` ${size6 * 8}-bit` : ""}${signed ? " signed" : " unsigned"} integer range ${max ? `(\`${min2}\` to \`${max}\`)` : `(above \`${min2}\`)`}`);
38097
+ constructor({ max, min, signed, size: size6, value }) {
38098
+ super(`Number \`${value}\` is not in safe${size6 ? ` ${size6 * 8}-bit` : ""}${signed ? " signed" : " unsigned"} integer range ${max ? `(\`${min}\` to \`${max}\`)` : `(above \`${min}\`)`}`);
38099
38099
  Object.defineProperty(this, "name", {
38100
38100
  enumerable: true,
38101
38101
  configurable: true,
@@ -40808,39 +40808,12 @@ function buildSafeSetupInitializer(params) {
40808
40808
  }
40809
40809
 
40810
40810
  // ../sdk/dist/fees.js
40811
- function min(a, b) {
40812
- return a < b ? a : b;
40813
- }
40814
- async function estimatePermissionFee(publicClient, governance, permission) {
40815
- try {
40816
- const [baseFee, complexityRate, cap, code] = await Promise.all([
40817
- publicClient.readContract({
40818
- address: governance,
40819
- abi: SailGovernanceAbi,
40820
- functionName: "baseFee"
40821
- }),
40822
- publicClient.readContract({
40823
- address: governance,
40824
- abi: SailGovernanceAbi,
40825
- functionName: "complexityRate"
40826
- }),
40827
- publicClient.readContract({
40828
- address: governance,
40829
- abi: SailGovernanceAbi,
40830
- functionName: "MAX_PERMISSION_FEE_WEI"
40831
- }),
40832
- publicClient.getBytecode({ address: permission })
40833
- ]);
40834
- const byteLength = code ? BigInt((code.length - 2) / 2) : 0n;
40835
- const fee = min(baseFee, cap) + min(byteLength * complexityRate, cap);
40836
- return min(fee, cap);
40837
- } catch {
40838
- return publicClient.readContract({
40839
- address: governance,
40840
- abi: SailGovernanceAbi,
40841
- functionName: "permissionRegistrationFee"
40842
- });
40843
- }
40811
+ async function readPermissionRegistrationFee(publicClient, governance) {
40812
+ return publicClient.readContract({
40813
+ address: governance,
40814
+ abi: SailGovernanceAbi,
40815
+ functionName: "permissionRegistrationFee"
40816
+ });
40844
40817
  }
40845
40818
 
40846
40819
  // ../../node_modules/.pnpm/viem@2.51.3_bufferutil@4.1.0_typescript@5.9.3_utf-8-validate@5.0.10_zod@3.25.76/node_modules/viem/_esm/utils/getAction.js
@@ -46228,11 +46201,11 @@ function encodeBoolean(value) {
46228
46201
  function encodeNumber3(value, { signed, size: size6 }) {
46229
46202
  if (typeof size6 === "number") {
46230
46203
  const max = 2n ** (BigInt(size6) - (signed ? 1n : 0n)) - 1n;
46231
- const min2 = signed ? -max - 1n : 0n;
46232
- if (value > max || value < min2)
46204
+ const min = signed ? -max - 1n : 0n;
46205
+ if (value > max || value < min)
46233
46206
  throw new IntegerOutOfRangeError3({
46234
46207
  max: max.toString(),
46235
- min: min2.toString(),
46208
+ min: min.toString(),
46236
46209
  signed,
46237
46210
  size: size6 / 8,
46238
46211
  value: value.toString()
@@ -50586,8 +50559,8 @@ function startServer(sailDir, { port = PORT } = {}) {
50586
50559
  if (!deployment?.kernel) throw new Error(`No SailKernel deployment for chain ${chainId}`);
50587
50560
  const keyring = await LocalKeyring.fromKeystoreFile(at("keys/manager.json"), password);
50588
50561
  const publicClient = createPublicClient({ transport: http(rpcUrl) });
50589
- let fee = 0n;
50590
- for (const a of addrs) fee += await estimatePermissionFee(publicClient, deployment.governance, a);
50562
+ const flatFee = await readPermissionRegistrationFee(publicClient, deployment.governance);
50563
+ const fee = flatFee * BigInt(addrs.length);
50591
50564
  const chain2 = defineChain({
50592
50565
  id: Number(chainId),
50593
50566
  name: `chain-${chainId}`,
@@ -50603,6 +50576,27 @@ function startServer(sailDir, { port = PORT } = {}) {
50603
50576
  txHash = await walletClient.sendTransaction({ to: deployment.kernel, data, value: fee, account: keyring.viemAccount, chain: chain2 });
50604
50577
  const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
50605
50578
  if (receipt.status !== "success") throw new Error(`registerPermissions reverted (tx ${txHash})`);
50579
+ const feeEth = formatEther(flatFee);
50580
+ for (const addr of addrs) {
50581
+ const meta = permissionMeta.find((p) => p.address && getAddress(p.address) === addr);
50582
+ const ev = {
50583
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
50584
+ actor: "agent",
50585
+ type: "permission_registered",
50586
+ permission: addr,
50587
+ ...meta?.template ? { name: meta.template } : {},
50588
+ sma: safe,
50589
+ txHash,
50590
+ chainId,
50591
+ fee: flatFee.toString(),
50592
+ feeEth
50593
+ };
50594
+ try {
50595
+ import_node_fs2.default.appendFileSync(at("activity.jsonl"), `${JSON.stringify(ev)}
50596
+ `);
50597
+ } catch {
50598
+ }
50599
+ }
50606
50600
  } catch (err) {
50607
50601
  res.status(500).json({ error: `On-chain registration failed: ${err?.message ?? String(err)}` });
50608
50602
  return;