@dev.sail.money/sailor 1.3.0-98 → 1.4.0-244

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 (233) hide show
  1. package/AGENTS.md +7 -7
  2. package/LICENSE +1 -1
  3. package/README.md +64 -472
  4. package/package.json +5 -6
  5. package/packages/cli/README.md +1 -1
  6. package/packages/cli/dist/index.cjs +1481 -463
  7. package/packages/cli/dist/server.cjs +16088 -17900
  8. package/packages/sdk/README.md +1 -1
  9. package/packages/sdk/dist/abis/ConfigurablePermission.d.ts +130 -0
  10. package/packages/sdk/dist/abis/ConfigurablePermission.d.ts.map +1 -0
  11. package/packages/sdk/dist/abis/ConfigurablePermission.js +79 -0
  12. package/packages/sdk/dist/abis/ConfigurablePermission.js.map +1 -0
  13. package/packages/sdk/dist/abis/SailKernel.d.ts +42 -0
  14. package/packages/sdk/dist/abis/SailKernel.d.ts.map +1 -1
  15. package/packages/sdk/dist/abis/SailKernel.js +34 -0
  16. package/packages/sdk/dist/abis/SailKernel.js.map +1 -1
  17. package/packages/sdk/dist/abis/index.d.ts +1 -0
  18. package/packages/sdk/dist/abis/index.d.ts.map +1 -1
  19. package/packages/sdk/dist/abis/index.js +1 -0
  20. package/packages/sdk/dist/abis/index.js.map +1 -1
  21. package/packages/sdk/dist/chains.d.ts +9 -7
  22. package/packages/sdk/dist/chains.d.ts.map +1 -1
  23. package/packages/sdk/dist/chains.js +103 -32
  24. package/packages/sdk/dist/chains.js.map +1 -1
  25. package/packages/sdk/dist/client.d.ts +1 -1
  26. package/packages/sdk/dist/client.d.ts.map +1 -1
  27. package/packages/sdk/dist/client.js +137 -16
  28. package/packages/sdk/dist/client.js.map +1 -1
  29. package/packages/sdk/dist/deployments.d.ts +6 -4
  30. package/packages/sdk/dist/deployments.d.ts.map +1 -1
  31. package/packages/sdk/dist/deployments.js +120 -118
  32. package/packages/sdk/dist/deployments.js.map +1 -1
  33. package/packages/sdk/dist/eip712.d.ts +84 -0
  34. package/packages/sdk/dist/eip712.d.ts.map +1 -1
  35. package/packages/sdk/dist/eip712.js +148 -0
  36. package/packages/sdk/dist/eip712.js.map +1 -1
  37. package/packages/sdk/dist/fees.d.ts +2 -1
  38. package/packages/sdk/dist/fees.d.ts.map +1 -1
  39. package/packages/sdk/dist/fees.js +2 -1
  40. package/packages/sdk/dist/fees.js.map +1 -1
  41. package/packages/sdk/dist/index.d.ts +4 -3
  42. package/packages/sdk/dist/index.d.ts.map +1 -1
  43. package/packages/sdk/dist/index.js +4 -3
  44. package/packages/sdk/dist/index.js.map +1 -1
  45. package/packages/sdk/dist/intelligence.d.ts +1 -1
  46. package/packages/sdk/dist/intelligence.js +1 -1
  47. package/packages/sdk/dist/safe.d.ts +49 -0
  48. package/packages/sdk/dist/safe.d.ts.map +1 -1
  49. package/packages/sdk/dist/safe.js +74 -0
  50. package/packages/sdk/dist/safe.js.map +1 -1
  51. package/packages/sdk/dist/types.d.ts +7 -0
  52. package/packages/sdk/dist/types.d.ts.map +1 -1
  53. package/packages/sdk/package.json +6 -2
  54. package/packages/ui/dist/assets/{add-BX5reKtK.js → add-sHg4oAtj.js} +2 -2
  55. package/packages/ui/dist/assets/{all-wallets-IZ3PlVF-.js → all-wallets-BhPcqrMm.js} +2 -2
  56. package/packages/ui/dist/assets/{app-store-ARgRdMdF.js → app-store-B_PQMwCf.js} +1 -1
  57. package/packages/ui/dist/assets/{apple-VCaqsI9w.js → apple-CqAFK7Fj.js} +2 -2
  58. package/packages/ui/dist/assets/arrow-bottom-CbxIflTv.js +8 -0
  59. package/packages/ui/dist/assets/{arrow-bottom-circle-h5vFiZ2I.js → arrow-bottom-circle-BFgY35YA.js} +2 -2
  60. package/packages/ui/dist/assets/arrow-left-DCG2m2BY.js +8 -0
  61. package/packages/ui/dist/assets/arrow-right-NrPOhG6W.js +8 -0
  62. package/packages/ui/dist/assets/arrow-top-BHTmGsDP.js +8 -0
  63. package/packages/ui/dist/assets/{bank-6c28yOM9.js → bank-BqDvUazs.js} +2 -2
  64. package/packages/ui/dist/assets/{basic-CJnbTfSd.js → basic-CHFjQMod.js} +177 -177
  65. package/packages/ui/dist/assets/{browser-CDKlxM3D.js → browser-C_VdKE7p.js} +2 -2
  66. package/packages/ui/dist/assets/{card-DQdED02q.js → card-w-1brQoV.js} +2 -2
  67. package/packages/ui/dist/assets/ccip-jvDTeGaI.js +1 -0
  68. package/packages/ui/dist/assets/{checkmark-JoRNVoFV.js → checkmark-CLBmxdAF.js} +2 -2
  69. package/packages/ui/dist/assets/{checkmark-bold-B6bGHVIs.js → checkmark-bold-Ca_e1PMs.js} +2 -2
  70. package/packages/ui/dist/assets/chevron-bottom-BhvFRdSL.js +8 -0
  71. package/packages/ui/dist/assets/chevron-left-cjMaNbHC.js +8 -0
  72. package/packages/ui/dist/assets/chevron-right-CRuHR0b-.js +8 -0
  73. package/packages/ui/dist/assets/chevron-top-Bq0a2jj-.js +8 -0
  74. package/packages/ui/dist/assets/{chrome-store-D-fiJ9qJ.js → chrome-store-Cieq6j7x.js} +2 -2
  75. package/packages/ui/dist/assets/{clock-xCPkzwjj.js → clock-Dkj5WbVg.js} +2 -2
  76. package/packages/ui/dist/assets/{close-BkqmPpWG.js → close-C8eE6Dfz.js} +2 -2
  77. package/packages/ui/dist/assets/{coinPlaceholder-PIvN9oq_.js → coinPlaceholder-DndKH-8C.js} +2 -2
  78. package/packages/ui/dist/assets/{compass-CW5kWcxW.js → compass-DzoLByzm.js} +2 -2
  79. package/packages/ui/dist/assets/{copy-DUZlAQJJ.js → copy-Y7UMmVLt.js} +2 -2
  80. package/packages/ui/dist/assets/core-BkWdAWjX.js +907 -0
  81. package/packages/ui/dist/assets/cursor-BAQNxqYB.js +3 -0
  82. package/packages/ui/dist/assets/{cursor-transparent-BUAMB0G1.js → cursor-transparent-BUHY29Re.js} +2 -2
  83. package/packages/ui/dist/assets/{desktop-BxdlwV8P.js → desktop-DkZLCs5P.js} +2 -2
  84. package/packages/ui/dist/assets/{disconnect-GelNh3mn.js → disconnect-CphsO-wg.js} +2 -2
  85. package/packages/ui/dist/assets/{discord-N0jnxkzC.js → discord-CYH46bgt.js} +2 -2
  86. package/packages/ui/dist/assets/{etherscan-B41u7wF4.js → etherscan-2-3-Icbx.js} +2 -2
  87. package/packages/ui/dist/assets/events-DQ172AOg.js +1 -0
  88. package/packages/ui/dist/assets/{exclamation-triangle-BWJC56FC.js → exclamation-triangle-Bx9-1mpn.js} +2 -2
  89. package/packages/ui/dist/assets/{extension-G6I6oogw.js → extension-BuDZDNPV.js} +2 -2
  90. package/packages/ui/dist/assets/external-link-k6oTiNch.js +8 -0
  91. package/packages/ui/dist/assets/{facebook-47twy32b.js → facebook-BVB4qCju.js} +2 -2
  92. package/packages/ui/dist/assets/{farcaster-DYqnqCqs.js → farcaster-CxfWmDK3.js} +2 -2
  93. package/packages/ui/dist/assets/{filters-dF6xeJnE.js → filters-CXyIzkBz.js} +2 -2
  94. package/packages/ui/dist/assets/{github-DbW4Ihz2.js → github-Bga1HQTm.js} +2 -2
  95. package/packages/ui/dist/assets/{google-TKm7G8LF.js → google-CKLjkN5t.js} +2 -2
  96. package/packages/ui/dist/assets/{help-circle-CY3qTmsG.js → help-circle-CW4A601v.js} +1 -1
  97. package/packages/ui/dist/assets/{id-JpHW4q14.js → id-BaRFLHqy.js} +2 -2
  98. package/packages/ui/dist/assets/{image-Dip2fG98.js → image-4pE_c0pP.js} +2 -2
  99. package/packages/ui/dist/assets/index-B07bCKJ8.css +1 -0
  100. package/packages/ui/dist/assets/index-BKPTb_0U.js +1 -0
  101. package/packages/ui/dist/assets/index-BV8s6ez1.js +1 -0
  102. package/packages/ui/dist/assets/index-BaLMTrx4.js +47 -0
  103. package/packages/ui/dist/assets/index-CguqCMNp.js +16 -0
  104. package/packages/ui/dist/assets/index-RMrT9ktE.js +1780 -0
  105. package/packages/ui/dist/assets/{index-q0mGElxZ.js → index-_n5fH6AD.js} +3 -3
  106. package/packages/ui/dist/assets/index.es-rQvUXQTw.js +26 -0
  107. package/packages/ui/dist/assets/{info-B1enFsqh.js → info-UMAWEFh_.js} +2 -2
  108. package/packages/ui/dist/assets/{info-circle-D4j3FISs.js → info-circle-DuTp8NTC.js} +2 -2
  109. package/packages/ui/dist/assets/{lightbulb-DZbKr-vS.js → lightbulb-CkYYJbAa.js} +2 -2
  110. package/packages/ui/dist/assets/{mail-D-TO_uVQ.js → mail-CntZNMWz.js} +2 -2
  111. package/packages/ui/dist/assets/metamask-sdk-DIj7UxWU.js +542 -0
  112. package/packages/ui/dist/assets/{mobile-Bkmwzav2.js → mobile-DdyrzTfW.js} +2 -2
  113. package/packages/ui/dist/assets/{more-D1_I_jiB.js → more-DpZ1Bqpv.js} +2 -2
  114. package/packages/ui/dist/assets/{network-placeholder-DUaFjoXh.js → network-placeholder-B8yy8U56.js} +2 -2
  115. package/packages/ui/dist/assets/{nftPlaceholder-Cc0iUryh.js → nftPlaceholder-BXFbCJ-Z.js} +2 -2
  116. package/packages/ui/dist/assets/{off-BvX71sse.js → off-DavoQSqH.js} +2 -2
  117. package/packages/ui/dist/assets/{play-store-XzLbtbTd.js → play-store-BJeHSxBp.js} +2 -2
  118. package/packages/ui/dist/assets/{plus-B77_RmYR.js → plus-B-L3uTWl.js} +2 -2
  119. package/packages/ui/dist/assets/{qr-code-BNeIhB1d.js → qr-code-CcQBy6o3.js} +1 -1
  120. package/packages/ui/dist/assets/{recycle-horizontal-B9sX-yty.js → recycle-horizontal-BLC3_x1m.js} +2 -2
  121. package/packages/ui/dist/assets/{refresh-BWRygnjm.js → refresh-78cbzBlS.js} +2 -2
  122. package/packages/ui/dist/assets/{reown-logo-DCAEsGFm.js → reown-logo-Bx5RmN6G.js} +2 -2
  123. package/packages/ui/dist/assets/{search-ZXIqrawS.js → search-XWB8l5E2.js} +2 -2
  124. package/packages/ui/dist/assets/{secp256k1-Doz05h4g.js → secp256k1-u9EmDRXd.js} +1 -1
  125. package/packages/ui/dist/assets/{send-BQcO0jNC.js → send-BjGwACTt.js} +2 -2
  126. package/packages/ui/dist/assets/{swapHorizontal-4yZEzz2K.js → swapHorizontal-DJWcWn3A.js} +2 -2
  127. package/packages/ui/dist/assets/{swapHorizontalBold-DxBKu3sl.js → swapHorizontalBold-CG930oAl.js} +2 -2
  128. package/packages/ui/dist/assets/{swapHorizontalMedium-DAQYJ2JK.js → swapHorizontalMedium-CHIdtnCI.js} +2 -2
  129. package/packages/ui/dist/assets/{swapHorizontalRoundedBold-CUlZzFJ6.js → swapHorizontalRoundedBold-B2oGt4O4.js} +2 -2
  130. package/packages/ui/dist/assets/{swapVertical-CjWHAaDM.js → swapVertical-CnjOFXH3.js} +2 -2
  131. package/packages/ui/dist/assets/{telegram-JBeabiJP.js → telegram-BtOilYdH.js} +2 -2
  132. package/packages/ui/dist/assets/{three-dots-D3R8OjSx.js → three-dots-D_VsAJlo.js} +2 -2
  133. package/packages/ui/dist/assets/{twitch-1f9z7xVO.js → twitch-BIX3n7hL.js} +2 -2
  134. package/packages/ui/dist/assets/{twitterIcon-BW7n2xnp.js → twitterIcon-C3DDaPem.js} +2 -2
  135. package/packages/ui/dist/assets/{verify-Cs5owI38.js → verify-CtzMScZy.js} +2 -2
  136. package/packages/ui/dist/assets/{verify-filled-Ci5sx5qS.js → verify-filled-CVjdmBEW.js} +2 -2
  137. package/packages/ui/dist/assets/{w3m-modal-DnWtss8u.js → w3m-modal-BZ3BUhSD.js} +1 -1
  138. package/packages/ui/dist/assets/{wallet-MaGNqaW-.js → wallet-hURaFgJp.js} +2 -2
  139. package/packages/ui/dist/assets/{wallet-placeholder-kTOy12_L.js → wallet-placeholder-BcF2KreB.js} +2 -2
  140. package/packages/ui/dist/assets/{walletconnect-CyuP92jg.js → walletconnect-C8XCABRo.js} +3 -3
  141. package/packages/ui/dist/assets/{warning-circle-BImUCdpG.js → warning-circle-BMz6LCU4.js} +2 -2
  142. package/packages/ui/dist/assets/{x-II45JHpV.js → x-deAHZeLU.js} +2 -2
  143. package/packages/ui/dist/index.html +2 -2
  144. package/templates/default/.agents/skills/sail-mandates/SKILL.md +1 -1
  145. package/templates/default/.agents/skills/sail-mandates/references/approvals.md +3 -3
  146. package/templates/default/.agents/skills/sail-mandates/references/examples-index.md +3 -3
  147. package/templates/default/.agents/skills/sail-onboarding/SKILL.md +34 -33
  148. package/templates/default/.agents/skills/sail-servers/SKILL.md +1 -0
  149. package/templates/default/.agents/skills/sail-swap-quote/SKILL.md +76 -0
  150. package/templates/default/.agents/skills/sail-template-approve-batch/SKILL.md +98 -0
  151. package/templates/default/.agents/skills/sail-template-borrow/SKILL.md +68 -0
  152. package/templates/default/.agents/skills/sail-template-deposit/SKILL.md +61 -0
  153. package/templates/default/.agents/skills/sail-template-swap/SKILL.md +207 -0
  154. package/templates/default/.agents/skills/sail-template-swap-no-oracle/SKILL.md +100 -0
  155. package/templates/default/.agents/skills/sail-template-transfer/SKILL.md +52 -0
  156. package/templates/default/.agents/skills/sail-template-withdraw/SKILL.md +50 -0
  157. package/templates/default/.agents/skills/sail-templates/SKILL.md +142 -0
  158. package/templates/default/.agents/skills/sail-templates/catalog.mjs +200 -0
  159. package/templates/default/.agents/skills/sail-templates/deployed.json +133 -0
  160. package/templates/default/.agents/skills/sail-templates/oracles/IOracle.sol +13 -0
  161. package/templates/default/.agents/skills/sail-templates/oracles/UniV3TwapOracle.sol +114 -0
  162. package/templates/default/.agents/skills/sail-templates/oracles/libs/FullMath.sol +62 -0
  163. package/templates/default/.agents/skills/sail-templates/oracles/libs/TickMath.sol +44 -0
  164. package/templates/default/.agents/skills/sail-templates/references/config-schemas.md +139 -0
  165. package/templates/default/.agents/skills/sail-templates/references/reuse-flow.md +139 -0
  166. package/templates/default/.agents/skills/sail-token-resolve/SKILL.md +174 -0
  167. package/templates/default/.env.example +9 -0
  168. package/templates/default/AGENTS.md +107 -13
  169. package/templates/default/docs/PERMISSION_MODEL.md +2 -2
  170. package/{examples → templates/default/examples}/custom-mandate/.sail/contracts/interfaces/IPermission.sol +4 -0
  171. package/{examples → templates/default/examples}/custom-mandate/README.md +4 -1
  172. package/{examples → templates/default/examples}/permissions/interfaces/IBatchPermission.sol +2 -0
  173. package/{examples → templates/default/examples}/permissions/interfaces/IPermission.sol +4 -0
  174. package/templates/default/scripts/quote-swap.mjs +211 -0
  175. package/templates/default/scripts/resolve-token.mjs +992 -0
  176. package/templates/default/scripts/shared-template-addr.mjs +110 -0
  177. package/templates/default/test/BoundedCallPermission.t.sol +2 -1
  178. package/docs/PERMISSION_MODEL.md +0 -93
  179. package/examples/README.md +0 -24
  180. package/examples/lifi-permissions/LifiBoundedApprovePermissionCloneable.sol +0 -84
  181. package/examples/lifi-permissions/LifiDiamondSwapPermissionCloneable.sol +0 -97
  182. package/examples/lifi-permissions/README.md +0 -53
  183. package/packages/ui/dist/assets/arrow-bottom-Cwptw8fW.js +0 -8
  184. package/packages/ui/dist/assets/arrow-left-bM1TRcV9.js +0 -8
  185. package/packages/ui/dist/assets/arrow-right-BYd_N4Jr.js +0 -8
  186. package/packages/ui/dist/assets/arrow-top-CLUnwVE2.js +0 -8
  187. package/packages/ui/dist/assets/ccip-BL7RIPfY.js +0 -1
  188. package/packages/ui/dist/assets/chevron-bottom-BMCelIEu.js +0 -8
  189. package/packages/ui/dist/assets/chevron-left-ryPLhHCc.js +0 -8
  190. package/packages/ui/dist/assets/chevron-right-CdDNRv7f.js +0 -8
  191. package/packages/ui/dist/assets/chevron-top-BxWIjjGk.js +0 -8
  192. package/packages/ui/dist/assets/core-von2c1sc.js +0 -907
  193. package/packages/ui/dist/assets/cursor-BzRVY7BD.js +0 -3
  194. package/packages/ui/dist/assets/events-C9icG2YB.js +0 -1
  195. package/packages/ui/dist/assets/external-link-BPaGQf4a.js +0 -8
  196. package/packages/ui/dist/assets/fallback-BTfJeGjh.js +0 -1
  197. package/packages/ui/dist/assets/index-BGJ_8fMk.js +0 -1
  198. package/packages/ui/dist/assets/index-Bq3PAopp.js +0 -16
  199. package/packages/ui/dist/assets/index-C4_cA5XW.js +0 -1
  200. package/packages/ui/dist/assets/index-CaDN0gL4.js +0 -1
  201. package/packages/ui/dist/assets/index-DLElIcBr.js +0 -1789
  202. package/packages/ui/dist/assets/index-lhNfD4jA.css +0 -1
  203. package/packages/ui/dist/assets/index.es-BaCKS_tW.js +0 -26
  204. package/packages/ui/dist/assets/metamask-sdk-DljW3QD1.js +0 -542
  205. package/packages/ui/dist/assets/native-CJ5et6AR.js +0 -1
  206. package/packages/ui/dist/assets/parseSignature-BAS-wKoj.js +0 -1
  207. package/scripts/check-docs.mjs +0 -309
  208. package/scripts/check-init.mjs +0 -123
  209. package/scripts/check-update.mjs +0 -177
  210. package/scripts/clean.mjs +0 -17
  211. package/scripts/pack.mjs +0 -21
  212. package/templates/default/.agents/skills/sail-overview/SKILL.md +0 -100
  213. package/templates/default/.agents/skills/sail-sailor/SKILL.md +0 -190
  214. package/templates/default/.agents/skills/sail-setup/SKILL.md +0 -38
  215. package/templates/default/.gitlab-ci.yml +0 -58
  216. /package/{examples → templates/default/examples}/custom-mandate/foundry.toml +0 -0
  217. /package/{examples → templates/default/examples}/custom-mandate/mandates/BoundedCallPermission.sol +0 -0
  218. /package/{examples → templates/default/examples}/custom-mandate/mandates/README.md +0 -0
  219. /package/{examples → templates/default/examples}/custom-mandate/mandates/SailCalldata.sol +0 -0
  220. /package/{examples → templates/default/examples}/permissions/BoundedApproveAndCallBatch.sol +0 -0
  221. /package/{examples → templates/default/examples}/permissions/BoundedBorrow_AaveV3_Arbitrum.sol +0 -0
  222. /package/{examples → templates/default/examples}/permissions/BoundedLimitless_Base.sol +0 -0
  223. /package/{examples → templates/default/examples}/permissions/BoundedPerp_GMXv2_Arbitrum.sol +0 -0
  224. /package/{examples → templates/default/examples}/permissions/BoundedStake_Venice_Base.sol +0 -0
  225. /package/{examples → templates/default/examples}/permissions/BoundedSupply_AaveV3_Arbitrum.sol +0 -0
  226. /package/{examples → templates/default/examples}/permissions/BoundedSwapNative_UniswapV3_Base.sol +0 -0
  227. /package/{examples → templates/default/examples}/permissions/BoundedSwap_UniswapV3_Base.sol +0 -0
  228. /package/{examples → templates/default/examples}/permissions/BoundedSwap_UniswapV4_Unichain.sol +0 -0
  229. /package/{examples → templates/default/examples}/permissions/BoundedTransfer_ERC20_Ethereum.sol +0 -0
  230. /package/{examples → templates/default/examples}/permissions/BoundedVault_ERC4626_Base.sol +0 -0
  231. /package/{examples → templates/default/examples}/permissions/README.md +0 -0
  232. /package/{examples → templates/default/examples}/permissions/SailCalldata.sol +0 -0
  233. /package/{examples → templates/default/examples}/permissions/foundry.toml +0 -0
@@ -36,11 +36,14 @@ struct Context {
36
36
  uint256 value; // msg.value (native asset) sent with the call
37
37
  uint256 blockTimestamp; // block.timestamp at evaluation
38
38
  uint256 blockNumber; // block.number at evaluation
39
+ uint256 configEpoch; // kernel registrationEpoch(account, permission) at dispatch;
40
+ // ignore it unless your permission takes post-deploy configuration
39
41
  }
40
42
  ```
41
43
 
42
44
  - `evaluate` — your policy. Return `true` to permit the call, `false` to block it. Runs under a
43
- 100k-gas `staticcall`; a revert or gas overage is treated as `false`.
45
+ 150,000-gas `staticcall` (`SailKernel.PERMISSION_GAS_CAP`); a revert or gas overage is treated
46
+ as `false`.
44
47
  - `discriminator` — a stable `bytes32` name for your permission (e.g. `keccak256("MyMandate")`).
45
48
 
46
49
  Keep all policy parameters constructor-configured so each deployment is a complete, reviewable
@@ -26,6 +26,8 @@ struct BatchContext {
26
26
  bytes32 batchHash; // keccak256(abi.encode(calls)) — stable id for the exact sequence
27
27
  uint256 blockTimestamp; // block.timestamp at dispatch
28
28
  uint256 blockNumber; // block.number at dispatch
29
+ uint256 configEpoch; // kernel registrationEpoch(account, permission) at dispatch —
30
+ // same fail-closed freshness check as Context.configEpoch
29
31
  }
30
32
 
31
33
  interface IBatchPermission {
@@ -10,6 +10,10 @@ struct Context {
10
10
  uint256 value;
11
11
  uint256 blockTimestamp;
12
12
  uint256 blockNumber;
13
+ /// @dev Kernel registrationEpoch(account, permission) at dispatch. Configurable
14
+ /// permissions fail closed on a mismatch with the epoch stamped at configure()
15
+ /// time; permissions without post-deploy configuration can ignore it.
16
+ uint256 configEpoch;
13
17
  }
14
18
 
15
19
  interface IPermission {
@@ -0,0 +1,211 @@
1
+ #!/usr/bin/env node
2
+ // quote-swap.mjs — live Uniswap V3 quote via QuoterV2 + an amountOutMinimum floor.
3
+ //
4
+ // Takes resolved tokens + an exact amount (from resolve-token.mjs), returns the
5
+ // expected output and a slippage-adjusted amountOutMinimum ready to embed in a
6
+ // swap dispatch. Pure JS, no dependencies.
7
+ //
8
+ // node scripts/quote-swap.mjs \
9
+ // --token-in 0x078D...AD6 --decimals-in 6 \
10
+ // --token-out 0x4200...0006 --decimals-out 18 \
11
+ // --amount 25000000 --fee 3000 --slippage-bps 100
12
+ //
13
+ // Output: one JSON object on stdout; human notes on stderr.
14
+
15
+ import { existsSync, readFileSync } from "node:fs";
16
+ import { resolve as resolvePath } from "node:path";
17
+
18
+ const CHAINS = {
19
+ ethereum: { chainId: 1, quoterV2: "0x61fFE014bA17989E743c5F6cB21bF9697530B21e" },
20
+ unichain: { chainId: 130, quoterV2: "0x385a5cf5f83e99f7bb2852b6a19c3538b9fa7658" },
21
+ base: { chainId: 8453, quoterV2: "0x3d4e44Eb1374240CE5F1B871ab261CD16335B76a" },
22
+ arbitrum: { chainId: 42161, quoterV2: "0x61fFE014bA17989E743c5F6cB21bF9697530B21e" },
23
+ };
24
+
25
+ const SEL_QUOTE = "0xc6a5026a"; // quoteExactInputSingle((address,address,uint256,uint24,uint160))
26
+ const ADDR_ZERO = "0x" + "0".repeat(40);
27
+
28
+ // ── minimal ABI encode/decode (mirrors resolve-token.mjs) ───────────────────────
29
+ function pad32(h) {
30
+ h = h.toLowerCase().replace(/^0x/, "");
31
+ if (h.length < 64) h = "0".repeat(64 - h.length) + h;
32
+ return h;
33
+ }
34
+ function uintToHex(n) {
35
+ let h = n.toString(16);
36
+ if (h.length % 2) h = "0" + h;
37
+ return pad32(h);
38
+ }
39
+ function encodeQuoteCall(tokenIn, tokenOut, amountIn, fee) {
40
+ return SEL_QUOTE + pad32(tokenIn) + pad32(tokenOut) + uintToHex(amountIn) + uintToHex(BigInt(fee)) + uintToHex(0n);
41
+ }
42
+ function decodeUint(hex, word = 0) {
43
+ const h = hex.toLowerCase().replace(/^0x/, "");
44
+ const w = h.slice(word * 64, word * 64 + 64);
45
+ return w ? BigInt("0x" + w) : 0n;
46
+ }
47
+
48
+ async function ethCall(rpc, to, data, from = ADDR_ZERO) {
49
+ const res = await fetch(rpc, {
50
+ method: "POST",
51
+ headers: { "Content-Type": "application/json" },
52
+ body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_call", params: [{ to, data, from }, "latest"] }),
53
+ signal: AbortSignal.timeout(15_000),
54
+ });
55
+ if (!res.ok) throw new Error(`eth_call HTTP ${res.status}`);
56
+ const json = await res.json();
57
+ if (json.error) throw new Error(`QuoterV2 reverted: ${JSON.stringify(json.error)}`);
58
+ if (!json.result) throw new Error("eth_call returned no result");
59
+ return json.result;
60
+ }
61
+
62
+ function readSailEnv() {
63
+ const p = resolvePath(process.cwd(), ".sail", ".env.local");
64
+ const out = {};
65
+ if (existsSync(p)) {
66
+ for (const line of readFileSync(p, "utf8").split("\n")) {
67
+ const m = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)\s*$/);
68
+ if (m) out[m[1]] = m[2].replace(/^["']|["']$/g, "");
69
+ }
70
+ }
71
+ return out;
72
+ }
73
+ function readSailConfig() {
74
+ const p = resolvePath(process.cwd(), ".sail", "config.json");
75
+ if (!existsSync(p)) return {};
76
+ try {
77
+ return JSON.parse(readFileSync(p, "utf8"));
78
+ } catch {
79
+ return {};
80
+ }
81
+ }
82
+ function resolveChain(chainFlag) {
83
+ if (chainFlag) {
84
+ const c = CHAINS[chainFlag.toLowerCase()];
85
+ if (c) return { name: chainFlag.toLowerCase(), ...c };
86
+ for (const [name, cfg] of Object.entries(CHAINS)) if (String(cfg.chainId) === String(chainFlag)) return { name, ...cfg };
87
+ throw new Error(`Unknown chain "${chainFlag}". Known: ${Object.keys(CHAINS).join(", ")}`);
88
+ }
89
+ const env = readSailEnv();
90
+ const cfg = readSailConfig();
91
+ const id = env.CHAIN_ID ?? cfg.chainId;
92
+ if (id) for (const [name, c] of Object.entries(CHAINS)) if (String(c.chainId) === String(id)) return { name, ...c };
93
+ throw new Error(`Could not resolve chain. Pass --chain <${Object.keys(CHAINS).join("|")}>.`);
94
+ }
95
+ function resolveRpc(chain, rpcFlag) {
96
+ if (rpcFlag) return rpcFlag;
97
+ const env = readSailEnv();
98
+ // Mirror resolve-token.mjs / packages/cli getRpcUrl(): named var, then chainId-keyed
99
+ // var (RPC_URL_8453 — written by the UI / used by multi-chain projects), then generic.
100
+ const nameVar = `${chain.name.toUpperCase().replace("-", "_")}_RPC_URL`;
101
+ const idVar = `RPC_URL_${chain.chainId}`;
102
+ return env[nameVar] ?? env[idVar] ?? env.RPC_URL ?? null;
103
+ }
104
+
105
+ function parseArgs(argv) {
106
+ const out = {};
107
+ for (let i = 0; i < argv.length; i++) {
108
+ const a = argv[i];
109
+ if (a.startsWith("--")) {
110
+ const key = a.replace(/^--/, "").replace(/-([a-z])/g, (_, c) => c.toUpperCase());
111
+ out[key] = argv[++i];
112
+ }
113
+ }
114
+ return out;
115
+ }
116
+
117
+ function fmtUnits(valueBI, decimals) {
118
+ // bigint base units → human string, preserving precision
119
+ const neg = valueBI < 0n;
120
+ let s = (neg ? -valueBI : valueBI).toString().padStart(decimals + 1, "0");
121
+ const intPart = s.slice(0, s.length - decimals);
122
+ const fracPart = decimals > 0 ? s.slice(s.length - decimals) : "";
123
+ const trimmedFrac = fracPart.replace(/0+$/, "");
124
+ let out = trimmedFrac.length ? `${intPart}.${trimmedFrac}` : intPart;
125
+ if (decimals > 0 && !trimmedFrac) out = `${intPart}.0`;
126
+ return (neg ? "-" : "") + out;
127
+ }
128
+
129
+ function errMsg(e) {
130
+ return e && typeof e.message === "string" ? e.message : String(e);
131
+ }
132
+
133
+ async function main() {
134
+ const a = parseArgs(process.argv.slice(2));
135
+ if (process.argv.length <= 2 || a.help) {
136
+ process.stderr.write(
137
+ "Usage: quote-swap.mjs --token-in <addr> --token-out <addr> --amount <baseUnits>\n" +
138
+ " --decimals-in <n> --decimals-out <n> --fee <tier> [--slippage-bps <n>] [--chain <>] [--rpc URL]\n",
139
+ );
140
+ process.exit(process.argv.length <= 2 ? 1 : 0);
141
+ }
142
+
143
+ const need = ["tokenIn", "tokenOut", "amount", "decimalsIn", "decimalsOut", "fee"];
144
+ for (const k of need) if (a[k] === undefined) throw new Error(`Missing --${k.replace(/([A-Z])/g, "-$1").toLowerCase()}`);
145
+
146
+ const chain = resolveChain(a.chain);
147
+ const rpc = resolveRpc(chain, a.rpc);
148
+ if (!rpc) throw new Error(`No RPC for ${chain.name}. Pass --rpc or set RPC_URL in .sail/.env.local.`);
149
+
150
+ const tokenIn = a.tokenIn;
151
+ const tokenOut = a.tokenOut;
152
+ const amountIn = BigInt(a.amount);
153
+ const fee = Number(a.fee);
154
+ const decimalsIn = Number(a.decimalsIn);
155
+ const decimalsOut = Number(a.decimalsOut);
156
+ const slippageBps = a.slippageBps !== undefined ? Number(a.slippageBps) : 100; // default 1%
157
+ if (!Number.isInteger(slippageBps) || slippageBps < 0 || slippageBps > 10_000) {
158
+ throw new Error(`--slippage-bps must be an integer in [0, 10000], got "${a.slippageBps}".`);
159
+ }
160
+
161
+ const data = encodeQuoteCall(tokenIn, tokenOut, amountIn, fee);
162
+ const ret = await ethCall(rpc, chain.quoterV2, data);
163
+ const amountOut = decodeUint(ret, 0); // word 0
164
+ const gasEstimate = decodeUint(ret, 3); // word 3
165
+
166
+ if (amountOut === 0n) {
167
+ throw new Error(
168
+ `QuoterV2 returned 0 for ${amountIn} (tokenIn) → tokenOut at fee ${fee}. The pool is empty or does not exist at this tier.`,
169
+ );
170
+ }
171
+
172
+ const amountOutMinimum = (amountOut * BigInt(10_000 - slippageBps)) / 10_000n;
173
+ // price = (amountOut / 10^decOut) / (amountIn / 10^decIn) → units of tokenOut per tokenIn
174
+ const humanIn = fmtUnits(amountIn, decimalsIn);
175
+ const humanOut = fmtUnits(amountOut, decimalsOut);
176
+ const humanMin = fmtUnits(amountOutMinimum, decimalsOut);
177
+
178
+ const out = {
179
+ tokenIn,
180
+ tokenOut,
181
+ decimalsIn,
182
+ decimalsOut,
183
+ amountIn: amountIn.toString(),
184
+ amountOut: amountOut.toString(),
185
+ amountOutMinimum: amountOutMinimum.toString(),
186
+ slippageBps,
187
+ fee,
188
+ chain: chain.name,
189
+ chainId: chain.chainId,
190
+ gasEstimate: gasEstimate.toString(),
191
+ human: {
192
+ amountIn: humanIn,
193
+ amountOut: humanOut,
194
+ amountOutMinimum: humanMin,
195
+ price: `${humanOut} out per ${humanIn} in`,
196
+ },
197
+ note: "amountOutMinimum is the slippage floor — embed it in the swap calldata the agent dispatches.",
198
+ };
199
+
200
+ process.stdout.write(JSON.stringify(out, null, 2) + "\n");
201
+ process.stderr.write(
202
+ `\nQuote: ${humanIn} in → ${humanOut} out (fee ${fee})\n` +
203
+ ` amountOutMinimum (${slippageBps / 100}% slip): ${humanMin}\n` +
204
+ ` gas estimate: ${gasEstimate}\n`,
205
+ );
206
+ }
207
+
208
+ main().catch((err) => {
209
+ process.stderr.write(`\nquote-swap failed: ${errMsg(err)}\n`);
210
+ process.exit(1);
211
+ });