@dev.sail.money/sailor 1.3.0-95 → 1.3.0-98

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 (127) hide show
  1. package/AGENTS.md +3 -0
  2. package/package.json +2 -3
  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 +7 -2
  18. package/packages/sdk/dist/intelligence.d.ts.map +1 -1
  19. package/packages/sdk/dist/intelligence.js +2 -2
  20. package/packages/sdk/dist/intelligence.js.map +1 -1
  21. package/packages/sdk/dist/signing.d.ts +14 -0
  22. package/packages/sdk/dist/signing.d.ts.map +1 -1
  23. package/packages/ui/dist/assets/{add-CLs0-B0p.js → add-BX5reKtK.js} +1 -1
  24. package/packages/ui/dist/assets/{all-wallets-CDOk28sj.js → all-wallets-IZ3PlVF-.js} +1 -1
  25. package/packages/ui/dist/assets/{app-store-DVzpi1qN.js → app-store-ARgRdMdF.js} +1 -1
  26. package/packages/ui/dist/assets/{apple-C9U9iH1l.js → apple-VCaqsI9w.js} +1 -1
  27. package/packages/ui/dist/assets/{arrow-bottom-DjQ4jGMe.js → arrow-bottom-Cwptw8fW.js} +1 -1
  28. package/packages/ui/dist/assets/{arrow-bottom-circle-jxial9Si.js → arrow-bottom-circle-h5vFiZ2I.js} +1 -1
  29. package/packages/ui/dist/assets/{arrow-left-BjAyI8D1.js → arrow-left-bM1TRcV9.js} +1 -1
  30. package/packages/ui/dist/assets/{arrow-right-BlFO4Tcx.js → arrow-right-BYd_N4Jr.js} +1 -1
  31. package/packages/ui/dist/assets/{arrow-top-DJ3oh3DH.js → arrow-top-CLUnwVE2.js} +1 -1
  32. package/packages/ui/dist/assets/{bank-B98it1qN.js → bank-6c28yOM9.js} +1 -1
  33. package/packages/ui/dist/assets/{basic-DsgeyPNu.js → basic-CJnbTfSd.js} +1 -1
  34. package/packages/ui/dist/assets/{browser-j4vCeHwE.js → browser-CDKlxM3D.js} +1 -1
  35. package/packages/ui/dist/assets/{card-CzxbMvEd.js → card-DQdED02q.js} +1 -1
  36. package/packages/ui/dist/assets/{ccip-U9yiu_EK.js → ccip-BL7RIPfY.js} +1 -1
  37. package/packages/ui/dist/assets/{checkmark-D5sWBbk3.js → checkmark-JoRNVoFV.js} +1 -1
  38. package/packages/ui/dist/assets/{checkmark-bold-BW6w3KCC.js → checkmark-bold-B6bGHVIs.js} +1 -1
  39. package/packages/ui/dist/assets/{chevron-bottom-BbHhmTQq.js → chevron-bottom-BMCelIEu.js} +1 -1
  40. package/packages/ui/dist/assets/{chevron-left-D8BDH4Wo.js → chevron-left-ryPLhHCc.js} +1 -1
  41. package/packages/ui/dist/assets/{chevron-right-BQuQk5zc.js → chevron-right-CdDNRv7f.js} +1 -1
  42. package/packages/ui/dist/assets/{chevron-top-1oeSXkx4.js → chevron-top-BxWIjjGk.js} +1 -1
  43. package/packages/ui/dist/assets/{chrome-store-B1OMtzC-.js → chrome-store-D-fiJ9qJ.js} +1 -1
  44. package/packages/ui/dist/assets/{clock-e0HueehS.js → clock-xCPkzwjj.js} +1 -1
  45. package/packages/ui/dist/assets/{close-Cy1ytr26.js → close-BkqmPpWG.js} +1 -1
  46. package/packages/ui/dist/assets/{coinPlaceholder-DqJu3ZPV.js → coinPlaceholder-PIvN9oq_.js} +1 -1
  47. package/packages/ui/dist/assets/{compass-XDz_N2Hg.js → compass-CW5kWcxW.js} +1 -1
  48. package/packages/ui/dist/assets/{copy-DmDo3Nob.js → copy-DUZlAQJJ.js} +1 -1
  49. package/packages/ui/dist/assets/{core-DbkMGSYW.js → core-von2c1sc.js} +3 -3
  50. package/packages/ui/dist/assets/cursor-BzRVY7BD.js +3 -0
  51. package/packages/ui/dist/assets/{cursor-transparent-DF2aA10D.js → cursor-transparent-BUAMB0G1.js} +1 -1
  52. package/packages/ui/dist/assets/{desktop-CVydOWLn.js → desktop-BxdlwV8P.js} +1 -1
  53. package/packages/ui/dist/assets/{disconnect-BuN2Whnz.js → disconnect-GelNh3mn.js} +1 -1
  54. package/packages/ui/dist/assets/{discord-DMNPvTB2.js → discord-N0jnxkzC.js} +1 -1
  55. package/packages/ui/dist/assets/{etherscan-CacW-93f.js → etherscan-B41u7wF4.js} +1 -1
  56. package/packages/ui/dist/assets/{events-CZkVDFih.js → events-C9icG2YB.js} +1 -1
  57. package/packages/ui/dist/assets/{exclamation-triangle-B26hIvqO.js → exclamation-triangle-BWJC56FC.js} +1 -1
  58. package/packages/ui/dist/assets/{extension-B7rI0gv7.js → extension-G6I6oogw.js} +1 -1
  59. package/packages/ui/dist/assets/{external-link-CX8skCbH.js → external-link-BPaGQf4a.js} +1 -1
  60. package/packages/ui/dist/assets/{facebook-BDqbE6kl.js → facebook-47twy32b.js} +1 -1
  61. package/packages/ui/dist/assets/{fallback-DWQYKf3l.js → fallback-BTfJeGjh.js} +1 -1
  62. package/packages/ui/dist/assets/{farcaster-zQ3oQzZ8.js → farcaster-DYqnqCqs.js} +1 -1
  63. package/packages/ui/dist/assets/{filters-DCglZYsM.js → filters-dF6xeJnE.js} +1 -1
  64. package/packages/ui/dist/assets/{github-BH-BYjss.js → github-DbW4Ihz2.js} +1 -1
  65. package/packages/ui/dist/assets/{google-DYFWxZbC.js → google-TKm7G8LF.js} +1 -1
  66. package/packages/ui/dist/assets/{help-circle-BXH_DtZ_.js → help-circle-CY3qTmsG.js} +1 -1
  67. package/packages/ui/dist/assets/{id-Dr2yTeX1.js → id-JpHW4q14.js} +1 -1
  68. package/packages/ui/dist/assets/{image-DeJey1Eg.js → image-Dip2fG98.js} +1 -1
  69. package/packages/ui/dist/assets/{index-BP-UuQ8g.js → index-BGJ_8fMk.js} +1 -1
  70. package/packages/ui/dist/assets/{index-C55-Ne92.js → index-Bq3PAopp.js} +1 -1
  71. package/packages/ui/dist/assets/{index-BksbyFjf.js → index-C4_cA5XW.js} +1 -1
  72. package/packages/ui/dist/assets/{index-uSdXE5b_.js → index-CaDN0gL4.js} +1 -1
  73. package/packages/ui/dist/assets/index-DLElIcBr.js +1789 -0
  74. package/packages/ui/dist/assets/index-lhNfD4jA.css +1 -0
  75. package/packages/ui/dist/assets/{index-DVjAiPHI.js → index-q0mGElxZ.js} +3 -3
  76. package/packages/ui/dist/assets/{index.es-dZlNPpfR.js → index.es-BaCKS_tW.js} +4 -4
  77. package/packages/ui/dist/assets/{info-B91pD5Rw.js → info-B1enFsqh.js} +1 -1
  78. package/packages/ui/dist/assets/{info-circle-CySl3_on.js → info-circle-D4j3FISs.js} +1 -1
  79. package/packages/ui/dist/assets/{lightbulb-wwG8LQLo.js → lightbulb-DZbKr-vS.js} +1 -1
  80. package/packages/ui/dist/assets/{mail-D617YZ8y.js → mail-D-TO_uVQ.js} +1 -1
  81. package/packages/ui/dist/assets/{metamask-sdk-RbLbxUgF.js → metamask-sdk-DljW3QD1.js} +1 -1
  82. package/packages/ui/dist/assets/{mobile-CD9KlA0m.js → mobile-Bkmwzav2.js} +1 -1
  83. package/packages/ui/dist/assets/{more-BQ_rpPXo.js → more-D1_I_jiB.js} +1 -1
  84. package/packages/ui/dist/assets/{network-placeholder-BA7nl9e6.js → network-placeholder-DUaFjoXh.js} +1 -1
  85. package/packages/ui/dist/assets/{nftPlaceholder-D1EaaeDn.js → nftPlaceholder-Cc0iUryh.js} +1 -1
  86. package/packages/ui/dist/assets/{off-DnGG35_w.js → off-BvX71sse.js} +1 -1
  87. package/packages/ui/dist/assets/{parseSignature-E29x8poM.js → parseSignature-BAS-wKoj.js} +1 -1
  88. package/packages/ui/dist/assets/{play-store-C5u2FvYB.js → play-store-XzLbtbTd.js} +1 -1
  89. package/packages/ui/dist/assets/{plus-CM4xJkvI.js → plus-B77_RmYR.js} +1 -1
  90. package/packages/ui/dist/assets/{qr-code-kM3FUIdR.js → qr-code-BNeIhB1d.js} +1 -1
  91. package/packages/ui/dist/assets/{recycle-horizontal-DWrRWIBM.js → recycle-horizontal-B9sX-yty.js} +1 -1
  92. package/packages/ui/dist/assets/{refresh-CztbjK2d.js → refresh-BWRygnjm.js} +1 -1
  93. package/packages/ui/dist/assets/{reown-logo-DUHEIodF.js → reown-logo-DCAEsGFm.js} +1 -1
  94. package/packages/ui/dist/assets/{search-BJtb4mn1.js → search-ZXIqrawS.js} +1 -1
  95. package/packages/ui/dist/assets/{secp256k1-BjrTZpQx.js → secp256k1-Doz05h4g.js} +1 -1
  96. package/packages/ui/dist/assets/{send-B3_kavG1.js → send-BQcO0jNC.js} +1 -1
  97. package/packages/ui/dist/assets/{swapHorizontal-BrF9POiv.js → swapHorizontal-4yZEzz2K.js} +1 -1
  98. package/packages/ui/dist/assets/{swapHorizontalBold-Dgf8OjXj.js → swapHorizontalBold-DxBKu3sl.js} +1 -1
  99. package/packages/ui/dist/assets/{swapHorizontalMedium-C6wX0JZj.js → swapHorizontalMedium-DAQYJ2JK.js} +1 -1
  100. package/packages/ui/dist/assets/{swapHorizontalRoundedBold-DTAVw6oF.js → swapHorizontalRoundedBold-CUlZzFJ6.js} +1 -1
  101. package/packages/ui/dist/assets/{swapVertical-KGlTFNoi.js → swapVertical-CjWHAaDM.js} +1 -1
  102. package/packages/ui/dist/assets/{telegram-DnLrK-4y.js → telegram-JBeabiJP.js} +1 -1
  103. package/packages/ui/dist/assets/{three-dots-C6_oMJfw.js → three-dots-D3R8OjSx.js} +1 -1
  104. package/packages/ui/dist/assets/{twitch-DvPqPD4f.js → twitch-1f9z7xVO.js} +1 -1
  105. package/packages/ui/dist/assets/{twitterIcon-BZdk82Am.js → twitterIcon-BW7n2xnp.js} +1 -1
  106. package/packages/ui/dist/assets/{verify-CbESD5G0.js → verify-Cs5owI38.js} +1 -1
  107. package/packages/ui/dist/assets/{verify-filled-BRjTrLxw.js → verify-filled-Ci5sx5qS.js} +1 -1
  108. package/packages/ui/dist/assets/{w3m-modal-D9b2TGHc.js → w3m-modal-DnWtss8u.js} +1 -1
  109. package/packages/ui/dist/assets/{wallet-B87E5UId.js → wallet-MaGNqaW-.js} +1 -1
  110. package/packages/ui/dist/assets/{wallet-placeholder-DmssSbYf.js → wallet-placeholder-kTOy12_L.js} +1 -1
  111. package/packages/ui/dist/assets/{walletconnect-CZkLKWMZ.js → walletconnect-CyuP92jg.js} +1 -1
  112. package/packages/ui/dist/assets/{warning-circle-BPTpHB1P.js → warning-circle-BImUCdpG.js} +1 -1
  113. package/packages/ui/dist/assets/{x-D_dEtEDU.js → x-II45JHpV.js} +1 -1
  114. package/packages/ui/dist/index.html +2 -2
  115. package/scripts/check-docs.mjs +1 -1
  116. package/templates/default/.agents/skills/sail-mandates/SKILL.md +12 -0
  117. package/templates/default/.agents/skills/sail-onboarding/SKILL.md +33 -34
  118. package/templates/default/.agents/skills/sail-overview/SKILL.md +100 -0
  119. package/templates/default/.agents/skills/sail-project-info/SKILL.md +2 -0
  120. package/templates/default/.agents/skills/sail-sailor/SKILL.md +190 -0
  121. package/templates/default/.agents/skills/sail-setup/SKILL.md +38 -0
  122. package/templates/default/.gitlab-ci.yml +58 -0
  123. package/templates/default/AGENTS.md +12 -87
  124. package/packages/cli/dist/mcp.cjs +0 -15534
  125. package/packages/ui/dist/assets/cursor-DRFqL8nZ.js +0 -3
  126. package/packages/ui/dist/assets/index-D6_Zei-R.js +0 -1790
  127. package/packages/ui/dist/assets/index-DZ2C2rHq.css +0 -1
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,10 +1,9 @@
1
1
  {
2
2
  "name": "@dev.sail.money/sailor",
3
- "version": "1.3.0-95",
3
+ "version": "1.3.0-98",
4
4
  "description": "Operator toolkit for Sail Protocol",
5
5
  "bin": {
6
- "sailor": "packages/cli/dist/index.cjs",
7
- "sailor-mcp": "packages/cli/dist/mcp.cjs"
6
+ "sailor": "packages/cli/dist/index.cjs"
8
7
  },
9
8
  "exports": {
10
9
  "./sdk": {
@@ -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
 
@@ -41441,6 +41446,21 @@ var MandateStore = class {
41441
41446
 
41442
41447
  // src/commands/onboard.ts
41443
41448
  init_esm2();
41449
+
41450
+ // src/lib/registration-fee.ts
41451
+ function registrationGate(args) {
41452
+ const { estimate, agentBalanceWei } = args;
41453
+ if (agentBalanceWei !== void 0) {
41454
+ assertFeeAffordable(agentBalanceWei, estimate.totalWei);
41455
+ }
41456
+ return {
41457
+ totalFeeWei: estimate.totalWei,
41458
+ permissionCount: estimate.perPermission.length,
41459
+ disclosure: describeMandateFee(estimate)
41460
+ };
41461
+ }
41462
+
41463
+ // src/commands/onboard.ts
41444
41464
  async function onboard(options) {
41445
41465
  if (!ProjectContext.exists()) {
41446
41466
  emit(options.json, () => console.log('No Sailor project found. Run "sailor init" first.'), {
@@ -41819,6 +41839,17 @@ async function attachMandate(project, channel, publicClient, agentSigner, smaAdd
41819
41839
  hasDeadline: registerPermissionHasDeadline,
41820
41840
  deadline: registrationDeadline
41821
41841
  });
41842
+ const feeEstimate = await estimateMandateRegistrationFee(
41843
+ publicClient,
41844
+ project.contracts.governance,
41845
+ [templateAddress]
41846
+ );
41847
+ const fee = feeEstimate.totalWei;
41848
+ const agentBalanceWei = await publicClient.getBalance({
41849
+ address: agentSigner.viemAccount.address
41850
+ });
41851
+ const gate = registrationGate({ estimate: feeEstimate, agentBalanceWei });
41852
+ say(() => console.log(gate.disclosure));
41822
41853
  say(() => console.log(`
41823
41854
  Pushing signing request for "${template.label}" permission\u2026`));
41824
41855
  say(
@@ -41841,6 +41872,13 @@ Pushing signing request for "${template.label}" permission\u2026`));
41841
41872
  { label: "Mandate signer", value: permissionSigner }
41842
41873
  ],
41843
41874
  explanation: permExplanation,
41875
+ // Disclose the fee in the signing card so the owner sees what the agent
41876
+ // will pay before signing. Only included when non-zero or when there is a
41877
+ // fee to disclose (zero-fee chains pass 0 ETH, still informative).
41878
+ registrationFee: {
41879
+ totalEth: formatEther(fee),
41880
+ permissionCount: 1
41881
+ },
41844
41882
  typedData
41845
41883
  });
41846
41884
  if (response.status === "rejected") {
@@ -41867,12 +41905,6 @@ Connect the owner wallet (mandate signer) in the browser \u2014 the agent wallet
41867
41905
  } catch (err) {
41868
41906
  if (err.message.startsWith("Security:")) throw err;
41869
41907
  }
41870
- say(() => console.log("Estimating permission fee\u2026"));
41871
- const fee = await estimatePermissionFee(
41872
- publicClient,
41873
- project.contracts.governance,
41874
- templateAddress
41875
- );
41876
41908
  say(() => console.log(`Submitting mandate registration (agent pays gas; fee ${fee} wei)\u2026`));
41877
41909
  const walletClient = createWalletClient({
41878
41910
  account: agentSigner.viemAccount,
@@ -41922,7 +41954,10 @@ Connect the owner wallet (mandate signer) in the browser \u2014 the agent wallet
41922
41954
  name: template.label,
41923
41955
  sma: smaAddress,
41924
41956
  txHash,
41925
- chainId: project.chainId
41957
+ chainId: project.chainId,
41958
+ // Registration fee actually paid by the agent for this permission.
41959
+ fee: fee.toString(),
41960
+ feeEth: formatEther(fee)
41926
41961
  });
41927
41962
  return txHash;
41928
41963
  }
@@ -42339,6 +42374,7 @@ ${spec.label} clone (${options.template})`);
42339
42374
  hasDeadline: registerPermissionHasDeadline,
42340
42375
  deadline
42341
42376
  });
42377
+ const fee = await readPermissionRegistrationFee(publicClient, project.contracts.governance);
42342
42378
  const label = options.label ?? `${spec.label} (${options.template})`;
42343
42379
  say(
42344
42380
  () => console.log(
@@ -42358,6 +42394,10 @@ ${spec.label} clone (${options.template})`);
42358
42394
  { label: "Mandate signer", value: permissionSigner },
42359
42395
  ...spec.describe(initParams)
42360
42396
  ],
42397
+ registrationFee: {
42398
+ totalEth: formatEther(fee),
42399
+ permissionCount: 1
42400
+ },
42361
42401
  typedData
42362
42402
  });
42363
42403
  if (response.status === "rejected") {
@@ -42384,7 +42424,6 @@ Connect the owner wallet (mandate signer) in the browser \u2014 the agent wallet
42384
42424
  } catch (err) {
42385
42425
  if (err.message.startsWith("Security:")) throw err;
42386
42426
  }
42387
- const fee = await estimatePermissionFee(publicClient, project.contracts.governance, clone);
42388
42427
  if (!registerPermissionHasDeadline || deadline === void 0) {
42389
42428
  throw new Error(
42390
42429
  "deploy-clone requires a selective kernel (RegisterPermission with deadline). This chain's kernel does not match."
@@ -42437,7 +42476,10 @@ Connect the owner wallet (mandate signer) in the browser \u2014 the agent wallet
42437
42476
  name: storedClone.name,
42438
42477
  sma,
42439
42478
  txHash,
42440
- chainId: project.chainId
42479
+ chainId: project.chainId,
42480
+ // Registration fee actually paid by the agent for this permission.
42481
+ fee: fee.toString(),
42482
+ feeEth: formatEther(fee)
42441
42483
  });
42442
42484
  emit(json, () => {
42443
42485
  }, {
@@ -42756,6 +42798,8 @@ Attaching ${permissions.length} permissions in one signature \u2014 the mandate
42756
42798
  explanation: (rec ? explainPermission(rec.name, rec.sourcePath) : null) ?? void 0
42757
42799
  };
42758
42800
  });
42801
+ const flatFee = await readPermissionRegistrationFee(publicClient, project.contracts.governance);
42802
+ const fee = flatFee * BigInt(permissions.length);
42759
42803
  const response = await channel.requestSignature({
42760
42804
  type: "typed-data",
42761
42805
  kind: "register-permission",
@@ -42768,6 +42812,11 @@ Attaching ${permissions.length} permissions in one signature \u2014 the mandate
42768
42812
  { label: "Mandate signer", value: permissionSigner }
42769
42813
  ],
42770
42814
  permissions: permExplanations,
42815
+ registrationFee: {
42816
+ totalEth: formatEther(fee),
42817
+ permissionCount: permissions.length,
42818
+ ...permissions.length > 1 ? { perPermissionEth: formatEther(flatFee) } : {}
42819
+ },
42771
42820
  typedData
42772
42821
  });
42773
42822
  if (response.status === "rejected") {
@@ -42776,10 +42825,6 @@ Attaching ${permissions.length} permissions in one signature \u2014 the mandate
42776
42825
  if (response.status !== "signature") {
42777
42826
  throw new Error(`Expected an EIP-712 signature response, got: ${response.status}`);
42778
42827
  }
42779
- let fee = 0n;
42780
- for (const permission of permissions) {
42781
- fee += await estimatePermissionFee(publicClient, project.contracts.governance, permission);
42782
- }
42783
42828
  const chain2 = getChainById(project.chainId);
42784
42829
  const walletClient = createWalletClient({
42785
42830
  account: agentSigner.viemAccount,
@@ -42812,7 +42857,10 @@ Attaching ${permissions.length} permissions in one signature \u2014 the mandate
42812
42857
  permission,
42813
42858
  sma,
42814
42859
  txHash,
42815
- chainId: project.chainId
42860
+ chainId: project.chainId,
42861
+ // Each permission in the batch is charged the same flat fee.
42862
+ fee: flatFee.toString(),
42863
+ feeEth: formatEther(flatFee)
42816
42864
  });
42817
42865
  say(() => {
42818
42866
  if (!present) {
@@ -43024,6 +43072,35 @@ function runForgeBuild() {
43024
43072
 
43025
43073
  // src/commands/mandate.ts
43026
43074
  init_esm2();
43075
+ function chargeablePermissions(tracked) {
43076
+ return tracked.filter((p) => !p.revokedOnChain && !p.registeredOnSma);
43077
+ }
43078
+ async function liveMandateFee(chainId, permissionAddresses) {
43079
+ if (permissionAddresses.length === 0) return { totalWei: 0n, perPermission: [] };
43080
+ try {
43081
+ const deployment = getSailDeployment(chainId);
43082
+ const rpcUrl = getRpcUrl(chainId) ?? getChainById(chainId).rpcUrls.default.http[0];
43083
+ const pc = createPublicClient({ chain: getChainById(chainId), transport: http(rpcUrl) });
43084
+ return await estimateMandateRegistrationFee(
43085
+ pc,
43086
+ deployment.governance,
43087
+ permissionAddresses
43088
+ );
43089
+ } catch {
43090
+ return null;
43091
+ }
43092
+ }
43093
+ function draftFeeFromEstimate(estimate) {
43094
+ const perFeeWei = estimate.perPermission[0]?.feeWei ?? 0n;
43095
+ return {
43096
+ perPermissionWei: perFeeWei.toString(),
43097
+ perPermissionEth: formatEther(perFeeWei),
43098
+ totalWei: estimate.totalWei.toString(),
43099
+ totalEth: formatEther(estimate.totalWei),
43100
+ permissionCount: estimate.perPermission.length,
43101
+ disclosure: describeMandateFee(estimate)
43102
+ };
43103
+ }
43027
43104
  function resolveActiveChain(account2) {
43028
43105
  try {
43029
43106
  const project = new ProjectContext();
@@ -43096,19 +43173,28 @@ ${permissions.length} permission(s) tracked for SMA ${account2.safe}:
43096
43173
  console.log(` ${status2}`);
43097
43174
  }
43098
43175
  const store = new MandateStore();
43176
+ const chargeable = chargeablePermissions(permissions);
43177
+ const draftPermissions = chargeable.map((p) => {
43178
+ const mandate2 = store.find(p.address);
43179
+ const explanation = explainPermission(p.label, mandate2?.sourcePath) ?? void 0;
43180
+ return { address: p.address, label: p.label, explanation };
43181
+ });
43182
+ let registrationFee;
43183
+ const unregisteredAddresses = chargeable.map((p) => p.address);
43184
+ if (unregisteredAddresses.length > 0) {
43185
+ const estimate = await liveMandateFee(chainId, unregisteredAddresses);
43186
+ if (estimate !== null) registrationFee = draftFeeFromEstimate(estimate);
43187
+ }
43099
43188
  const draft = {
43100
43189
  account: account2.safe,
43101
43190
  chainId,
43102
- // Only permissions not already registered on this SMA belong in the signing
43103
- // draft re-registering an already-registered permission reverts on-chain.
43104
- permissions: permissions.filter((p) => !p.revokedOnChain && !p.registeredOnSma).map((p) => {
43105
- const mandate2 = store.find(p.address);
43106
- const explanation = explainPermission(p.label, mandate2?.sourcePath) ?? void 0;
43107
- return { address: p.address, label: p.label, explanation };
43108
- }),
43109
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
43191
+ permissions: draftPermissions,
43192
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
43193
+ ...registrationFee ? { registrationFee } : {}
43110
43194
  };
43111
43195
  writeJsonFile(sailPath("mandate-draft.json"), draft);
43196
+ if (registrationFee) console.log(`
43197
+ ${registrationFee.disclosure}`);
43112
43198
  console.log("\nDraft written to .sail/mandate-draft.json for the UI to display.");
43113
43199
  }
43114
43200
  async function mandateSign(opts = {}) {
@@ -43135,6 +43221,15 @@ Permissions tracked for SMA ${account2.safe}:
43135
43221
  "\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`)."
43136
43222
  );
43137
43223
  const activePermissions = permissions.filter((p) => !p.revokedOnChain);
43224
+ const unregistered = chargeablePermissions(permissions);
43225
+ if (unregistered.length > 0) {
43226
+ const estimate = await liveMandateFee(chainId, unregistered.map((p) => p.address));
43227
+ if (estimate !== null) {
43228
+ console.log(`
43229
+ ${describeMandateFee(estimate)}`);
43230
+ console.log(" Paid by the agent wallet on registration, per permission.");
43231
+ }
43232
+ }
43138
43233
  const proceed = opts.yes || await confirm(
43139
43234
  `Confirm these ${activePermissions.length} permission(s) are authorized for your SMA?`
43140
43235
  );
@@ -43142,7 +43237,6 @@ Permissions tracked for SMA ${account2.safe}:
43142
43237
  console.log("No permissions confirmed.");
43143
43238
  return;
43144
43239
  }
43145
- const unregistered = activePermissions.filter((p) => !p.registeredOnSma);
43146
43240
  if (unregistered.length === 0) {
43147
43241
  console.log(`
43148
43242
  \u2713 Confirmed ${activePermissions.length} permission(s) for ${account2.safe}.`);
@@ -43684,10 +43778,8 @@ Re-approving ${permissions.length} mandate(s) \u2014 the mandate signer (${permi
43684
43778
  if (response.status !== "signature") {
43685
43779
  throw new Error(`Expected an EIP-712 signature response, got: ${response.status}`);
43686
43780
  }
43687
- let fee = 0n;
43688
- for (const permission of permissions) {
43689
- fee += await estimatePermissionFee(publicClient, project.contracts.governance, permission);
43690
- }
43781
+ const flatFee = await readPermissionRegistrationFee(publicClient, project.contracts.governance);
43782
+ const fee = flatFee * BigInt(permissions.length);
43691
43783
  const agentSigner = await loadManagerSigner();
43692
43784
  const chain2 = getChainById(project.chainId);
43693
43785
  const walletClient = createWalletClient({