@dev.sail.money/sailor 0.0.2 → 0.1.0-local

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 (211) hide show
  1. package/AGENTS.md +140 -111
  2. package/LICENSE +21 -21
  3. package/README.md +430 -337
  4. package/docs/PERMISSION_MODEL.md +93 -93
  5. package/examples/permissions/BoundedApproveAndCallBatch.sol +179 -0
  6. package/examples/permissions/BoundedBet_Limitless_Base.sol +97 -96
  7. package/examples/permissions/BoundedBorrow_AaveV3_Arbitrum.sol +94 -94
  8. package/examples/permissions/BoundedPerp_GMXv2_Arbitrum.sol +154 -143
  9. package/examples/permissions/BoundedStake_Venice_Base.sol +85 -0
  10. package/examples/permissions/BoundedSupply_AaveV3_Arbitrum.sol +82 -0
  11. package/examples/permissions/BoundedSwap_UniswapV3_Base.sol +116 -113
  12. package/examples/permissions/BoundedSwap_UniswapV4_Unichain.sol +150 -144
  13. package/examples/permissions/BoundedTransfer_ERC20_Ethereum.sol +73 -73
  14. package/examples/permissions/BoundedVault_ERC4626_Base.sol +97 -0
  15. package/examples/permissions/README.md +79 -52
  16. package/examples/permissions/SailCalldata.sol +118 -0
  17. package/examples/permissions/foundry.toml +10 -10
  18. package/examples/permissions/interfaces/IBatchPermission.sol +38 -0
  19. package/examples/permissions/interfaces/IPermission.sol +18 -18
  20. package/package.json +45 -39
  21. package/packages/cli/README.md +34 -34
  22. package/packages/cli/dist/index.cjs +4571 -2944
  23. package/packages/cli/dist/server.cjs +1252 -2010
  24. package/packages/sdk/README.md +65 -65
  25. package/packages/sdk/dist/chains.d.ts +12 -0
  26. package/packages/sdk/dist/chains.d.ts.map +1 -0
  27. package/packages/sdk/dist/chains.js +94 -0
  28. package/packages/sdk/dist/chains.js.map +1 -0
  29. package/packages/sdk/dist/deployments.d.ts +14 -7
  30. package/packages/sdk/dist/deployments.d.ts.map +1 -1
  31. package/packages/sdk/dist/deployments.js +132 -141
  32. package/packages/sdk/dist/deployments.js.map +1 -1
  33. package/packages/sdk/dist/index.d.ts +3 -2
  34. package/packages/sdk/dist/index.d.ts.map +1 -1
  35. package/packages/sdk/dist/index.js +3 -2
  36. package/packages/sdk/dist/index.js.map +1 -1
  37. package/packages/sdk/dist/intelligence.d.ts +1 -1
  38. package/packages/sdk/dist/intelligence.js +1 -1
  39. package/packages/sdk/dist/lifi.d.ts +17 -0
  40. package/packages/sdk/dist/lifi.d.ts.map +1 -1
  41. package/packages/sdk/dist/lifi.js +24 -0
  42. package/packages/sdk/dist/lifi.js.map +1 -1
  43. package/packages/sdk/dist/safe.d.ts +83 -0
  44. package/packages/sdk/dist/safe.d.ts.map +1 -1
  45. package/packages/sdk/dist/safe.js +92 -1
  46. package/packages/sdk/dist/safe.js.map +1 -1
  47. package/packages/sdk/dist/templates/ammLiquidity.d.ts +24 -11
  48. package/packages/sdk/dist/templates/ammLiquidity.d.ts.map +1 -1
  49. package/packages/sdk/dist/templates/ammLiquidity.js +39 -31
  50. package/packages/sdk/dist/templates/ammLiquidity.js.map +1 -1
  51. package/packages/sdk/dist/templates/approveAndCallBatch.d.ts +24 -10
  52. package/packages/sdk/dist/templates/approveAndCallBatch.d.ts.map +1 -1
  53. package/packages/sdk/dist/templates/approveAndCallBatch.js +36 -23
  54. package/packages/sdk/dist/templates/approveAndCallBatch.js.map +1 -1
  55. package/packages/sdk/dist/templates/boundedBorrow.d.ts +19 -9
  56. package/packages/sdk/dist/templates/boundedBorrow.d.ts.map +1 -1
  57. package/packages/sdk/dist/templates/boundedBorrow.js +28 -19
  58. package/packages/sdk/dist/templates/boundedBorrow.js.map +1 -1
  59. package/packages/sdk/dist/templates/boundedSwap.d.ts +19 -9
  60. package/packages/sdk/dist/templates/boundedSwap.d.ts.map +1 -1
  61. package/packages/sdk/dist/templates/boundedSwap.js +30 -20
  62. package/packages/sdk/dist/templates/boundedSwap.js.map +1 -1
  63. package/packages/sdk/dist/templates/defiBundle.d.ts +35 -9
  64. package/packages/sdk/dist/templates/defiBundle.d.ts.map +1 -1
  65. package/packages/sdk/dist/templates/defiBundle.js +84 -22
  66. package/packages/sdk/dist/templates/defiBundle.js.map +1 -1
  67. package/packages/sdk/dist/templates/pendle.d.ts +23 -8
  68. package/packages/sdk/dist/templates/pendle.d.ts.map +1 -1
  69. package/packages/sdk/dist/templates/pendle.js +34 -14
  70. package/packages/sdk/dist/templates/pendle.js.map +1 -1
  71. package/packages/sdk/dist/templates/transferTarget.d.ts +11 -3
  72. package/packages/sdk/dist/templates/transferTarget.d.ts.map +1 -1
  73. package/packages/sdk/dist/templates/transferTarget.js +14 -7
  74. package/packages/sdk/dist/templates/transferTarget.js.map +1 -1
  75. package/packages/sdk/dist/types.d.ts +19 -1
  76. package/packages/sdk/dist/types.d.ts.map +1 -1
  77. package/packages/sdk/package.json +80 -52
  78. package/packages/ui/dist/assets/{add-DaJhwIBV.js → add-BxpXfVWe.js} +1 -1
  79. package/packages/ui/dist/assets/{all-wallets-BUxsqWXi.js → all-wallets-BKTn_sWK.js} +1 -1
  80. package/packages/ui/dist/assets/{app-store-DkltwTqE.js → app-store-CfuKbwxR.js} +1 -1
  81. package/packages/ui/dist/assets/{apple-owVOeaIT.js → apple-BKSBbNYg.js} +1 -1
  82. package/packages/ui/dist/assets/{arrow-bottom-D2mmNJve.js → arrow-bottom-D4bG6gZi.js} +1 -1
  83. package/packages/ui/dist/assets/{arrow-bottom-circle-CbNYijx-.js → arrow-bottom-circle-BNTs1p0T.js} +1 -1
  84. package/packages/ui/dist/assets/{arrow-left-DJB61s4C.js → arrow-left-2uee3vYv.js} +1 -1
  85. package/packages/ui/dist/assets/{arrow-right-BBrsQ9R4.js → arrow-right-BktjMV6h.js} +1 -1
  86. package/packages/ui/dist/assets/{arrow-top-Cil6bOc8.js → arrow-top-Izu28fX4.js} +1 -1
  87. package/packages/ui/dist/assets/{bank-CbwEmRo3.js → bank-USBaAyFM.js} +1 -1
  88. package/packages/ui/dist/assets/{basic-CLNfjw3m.js → basic-C_9KjTEH.js} +1 -1
  89. package/packages/ui/dist/assets/{browser-B5TtF4Pb.js → browser-DAEMAKV7.js} +1 -1
  90. package/packages/ui/dist/assets/{card-CO7BVB-C.js → card-DT8yDkKN.js} +1 -1
  91. package/packages/ui/dist/assets/{ccip-2W7K3_J3.js → ccip-CkqfGSxX.js} +1 -1
  92. package/packages/ui/dist/assets/{checkmark-BEtSHq9m.js → checkmark-CsgdEXFj.js} +1 -1
  93. package/packages/ui/dist/assets/{checkmark-bold-D9xGHzPE.js → checkmark-bold-D2gjOQo2.js} +1 -1
  94. package/packages/ui/dist/assets/{chevron-bottom-BDztht6i.js → chevron-bottom-tprFynYV.js} +1 -1
  95. package/packages/ui/dist/assets/{chevron-left-EV4GFNbc.js → chevron-left-D2Zj1gNB.js} +1 -1
  96. package/packages/ui/dist/assets/{chevron-right-B4_bB9oR.js → chevron-right-D1rRuAVe.js} +1 -1
  97. package/packages/ui/dist/assets/{chevron-top-D54xPNzF.js → chevron-top-24dL1mbL.js} +1 -1
  98. package/packages/ui/dist/assets/{chrome-store-DYUpAJJq.js → chrome-store-Vy-5niYX.js} +1 -1
  99. package/packages/ui/dist/assets/{clock-Ca1T1Soz.js → clock-qBjLnVdJ.js} +1 -1
  100. package/packages/ui/dist/assets/{close-BZqWjurK.js → close-DARDwgcu.js} +1 -1
  101. package/packages/ui/dist/assets/{coinPlaceholder-e6fl2XDo.js → coinPlaceholder-BvpIbPlD.js} +1 -1
  102. package/packages/ui/dist/assets/{compass-DCLC7zIh.js → compass-BMTO0ayt.js} +1 -1
  103. package/packages/ui/dist/assets/{copy-Th2AaD-O.js → copy-PaXeRHza.js} +1 -1
  104. package/packages/ui/dist/assets/{core-Ckx_cyuH.js → core-BFnStQd-.js} +3 -3
  105. package/packages/ui/dist/assets/cursor-BDvw-B17.js +3 -0
  106. package/packages/ui/dist/assets/{cursor-transparent-BKHeABKB.js → cursor-transparent-BEMdi-8q.js} +1 -1
  107. package/packages/ui/dist/assets/{desktop-CBjY8t6F.js → desktop-CfuLLThw.js} +1 -1
  108. package/packages/ui/dist/assets/{disconnect-DbSs2cli.js → disconnect-DhwgJMiR.js} +1 -1
  109. package/packages/ui/dist/assets/{discord-ZlLOAUkM.js → discord-po8qoN1s.js} +1 -1
  110. package/packages/ui/dist/assets/{etherscan-CKUrqWYN.js → etherscan-BEsz0_yx.js} +1 -1
  111. package/packages/ui/dist/assets/{events-CiKP71cK.js → events-Bz33Unzu.js} +1 -1
  112. package/packages/ui/dist/assets/{exclamation-triangle-DA1QzFiO.js → exclamation-triangle-7CjTAGOQ.js} +1 -1
  113. package/packages/ui/dist/assets/{extension-BVJkmvpJ.js → extension-CmxjEWEt.js} +1 -1
  114. package/packages/ui/dist/assets/{external-link-D_bsR7B2.js → external-link-CmQ--bNS.js} +1 -1
  115. package/packages/ui/dist/assets/{facebook-CmFmhojx.js → facebook-CIBn9b65.js} +1 -1
  116. package/packages/ui/dist/assets/{fallback-Ofl6uSnB.js → fallback-DATyrQlb.js} +1 -1
  117. package/packages/ui/dist/assets/{farcaster-Co-M3Ss8.js → farcaster-OJ3Jasxg.js} +1 -1
  118. package/packages/ui/dist/assets/{filters-B1WwNaFU.js → filters-D4x09zeL.js} +1 -1
  119. package/packages/ui/dist/assets/{github-CP4fP6gn.js → github-ZlIuMArp.js} +1 -1
  120. package/packages/ui/dist/assets/{google-CsOIXJ6V.js → google-Gwg85sfv.js} +1 -1
  121. package/packages/ui/dist/assets/{help-circle-DiMkomdF.js → help-circle-D1uOWYcX.js} +1 -1
  122. package/packages/ui/dist/assets/{id-lmscL5LX.js → id-C0-5UdYk.js} +1 -1
  123. package/packages/ui/dist/assets/{image-B-ubJrY5.js → image-D_DUsv8-.js} +1 -1
  124. package/packages/ui/dist/assets/{index-CZR1Qjhs.js → index-BCzex_R6.js} +1 -1
  125. package/packages/ui/dist/assets/index-BUhrHLpY.js +1775 -0
  126. package/packages/ui/dist/assets/index-Cq02kQmy.css +1 -0
  127. package/packages/ui/dist/assets/{index-BaukYv-x.js → index-CrYzBWfD.js} +1 -1
  128. package/packages/ui/dist/assets/{index-CF0KMmke.js → index-DdbJhIdl.js} +3 -3
  129. package/packages/ui/dist/assets/{index-DVgfCzCo.js → index-DiojfeVM.js} +1 -1
  130. package/packages/ui/dist/assets/{index-Dbh5V1Z0.js → index-izd7vu_r.js} +1 -1
  131. package/packages/ui/dist/assets/{index.es-C78cE5SI.js → index.es-DdkHhQAj.js} +4 -4
  132. package/packages/ui/dist/assets/{info-Cqg57EVo.js → info-CiRd_kEG.js} +1 -1
  133. package/packages/ui/dist/assets/{info-circle-DkeSWNKV.js → info-circle-ypxjqarK.js} +1 -1
  134. package/packages/ui/dist/assets/{lightbulb-DNlO4qKh.js → lightbulb-B-pxLxd8.js} +1 -1
  135. package/packages/ui/dist/assets/{mail-kVQ8Jb9Y.js → mail-BYmicuVZ.js} +1 -1
  136. package/packages/ui/dist/assets/{metamask-sdk-CBalSvz7.js → metamask-sdk-Ccl6DG7Q.js} +1 -1
  137. package/packages/ui/dist/assets/{mobile-BEteuhF7.js → mobile-CtP5PqVT.js} +1 -1
  138. package/packages/ui/dist/assets/{more-DBWmXQli.js → more-6C2733we.js} +1 -1
  139. package/packages/ui/dist/assets/{network-placeholder-Dg1uUHiL.js → network-placeholder-CdhxMzqd.js} +1 -1
  140. package/packages/ui/dist/assets/{nftPlaceholder-i3AHSiD9.js → nftPlaceholder-DVmTWEAY.js} +1 -1
  141. package/packages/ui/dist/assets/{off-BtMm0fi2.js → off-DNYLughs.js} +1 -1
  142. package/packages/ui/dist/assets/{parseSignature-Cb5FlWWg.js → parseSignature-Dq2B5Bu3.js} +1 -1
  143. package/packages/ui/dist/assets/{play-store-iKKkXa6a.js → play-store-D7Qut5ta.js} +1 -1
  144. package/packages/ui/dist/assets/{plus-CA5NaRtb.js → plus-kqMyjt3q.js} +1 -1
  145. package/packages/ui/dist/assets/{qr-code-D2kiqR7h.js → qr-code-DiUCWRbz.js} +1 -1
  146. package/packages/ui/dist/assets/{recycle-horizontal-Dcme7R03.js → recycle-horizontal-Boe3XiS-.js} +1 -1
  147. package/packages/ui/dist/assets/{refresh-Dega3sDp.js → refresh-CrBgBQYO.js} +1 -1
  148. package/packages/ui/dist/assets/{reown-logo-xNkksyWJ.js → reown-logo-CFZCCHSx.js} +1 -1
  149. package/packages/ui/dist/assets/{search-HYl7NO8x.js → search-ChTDrghU.js} +1 -1
  150. package/packages/ui/dist/assets/{secp256k1-Cxd6_SiH.js → secp256k1-DAV5Q_FR.js} +1 -1
  151. package/packages/ui/dist/assets/{send-CJU8CUAo.js → send-DLFbBFe1.js} +1 -1
  152. package/packages/ui/dist/assets/{swapHorizontal-IMUKiUre.js → swapHorizontal-BEs3emfG.js} +1 -1
  153. package/packages/ui/dist/assets/{swapHorizontalBold-CNYnNJ9-.js → swapHorizontalBold-CC-Hfa7W.js} +1 -1
  154. package/packages/ui/dist/assets/{swapHorizontalMedium-B9VxEYsT.js → swapHorizontalMedium-BmR0H8DC.js} +1 -1
  155. package/packages/ui/dist/assets/{swapHorizontalRoundedBold-Dz33l_Jh.js → swapHorizontalRoundedBold-BdP5NGIH.js} +1 -1
  156. package/packages/ui/dist/assets/{swapVertical-CHUmjVJ0.js → swapVertical-CPrGEJPY.js} +1 -1
  157. package/packages/ui/dist/assets/{telegram-kl9S2mbU.js → telegram-CxNoZ80Q.js} +1 -1
  158. package/packages/ui/dist/assets/{three-dots-U5lhA1Am.js → three-dots-BRa6SBpL.js} +1 -1
  159. package/packages/ui/dist/assets/{twitch-KTEUWXEp.js → twitch-BC338bG5.js} +1 -1
  160. package/packages/ui/dist/assets/{twitterIcon-BHiq8mRg.js → twitterIcon-BGZmt2i9.js} +1 -1
  161. package/packages/ui/dist/assets/{verify-CfN-BXNd.js → verify-CEstW0zw.js} +1 -1
  162. package/packages/ui/dist/assets/{verify-filled-DwZccetj.js → verify-filled-OkZb0weU.js} +1 -1
  163. package/packages/ui/dist/assets/{w3m-modal-CS-PFqPE.js → w3m-modal-pS09ECwE.js} +1 -1
  164. package/packages/ui/dist/assets/{wallet-DVlGkhOY.js → wallet-BXVKCgC9.js} +1 -1
  165. package/packages/ui/dist/assets/{wallet-placeholder-CvR_iEWX.js → wallet-placeholder-C_kNhB1c.js} +1 -1
  166. package/packages/ui/dist/assets/{walletconnect-8pZBDvVI.js → walletconnect-CRKIuUHH.js} +1 -1
  167. package/packages/ui/dist/assets/{warning-circle-ylLEE0Yp.js → warning-circle-DB2NnwlJ.js} +1 -1
  168. package/packages/ui/dist/assets/{x-C_TBsTMj.js → x-DT4RmwL5.js} +1 -1
  169. package/packages/ui/dist/index.html +14 -14
  170. package/scripts/check-docs.mjs +262 -262
  171. package/scripts/check-init.mjs +108 -109
  172. package/scripts/postinstall.js +81 -366
  173. package/templates/custom-mandate/.sail/contracts/interfaces/IPermission.sol +18 -18
  174. package/templates/custom-mandate/README.md +116 -85
  175. package/templates/custom-mandate/foundry.toml +8 -8
  176. package/templates/custom-mandate/mandates/BoundedCallPermission.sol +41 -35
  177. package/templates/custom-mandate/mandates/README.md +16 -16
  178. package/templates/custom-mandate/mandates/SailCalldata.sol +118 -0
  179. package/templates/{dca-rebalancer → default}/.cursor/rules +25 -25
  180. package/templates/default/.env.example +20 -0
  181. package/templates/{dca-rebalancer → default}/.github/workflows/agent-tick.yml +33 -32
  182. package/templates/{dca-rebalancer → default}/.sail/README.md +13 -13
  183. package/templates/{dca-rebalancer → default}/.sail/config.json +10 -10
  184. package/templates/default/AGENTS.md +171 -0
  185. package/templates/{dca-rebalancer → default}/CLAUDE.md +2 -2
  186. package/templates/default/README.md +16 -0
  187. package/templates/{dca-rebalancer → default}/_gitignore +13 -13
  188. package/templates/{dca-rebalancer → default}/docs/PERMISSION_MODEL.md +93 -93
  189. package/templates/default/examples/dca/README.md +16 -0
  190. package/templates/default/examples/dca/agent.ts +174 -0
  191. package/templates/{dca-rebalancer/src → default/examples/dca}/mandate.ts +45 -67
  192. package/templates/{dca-rebalancer → default}/package.json +17 -17
  193. package/templates/default/src/agent.ts +37 -0
  194. package/templates/default/src/config.ts +24 -0
  195. package/templates/default/src/mandate.ts +22 -0
  196. package/templates/default/tsconfig.json +17 -0
  197. package/templates/{dca-rebalancer → default}/ui/README.md +3 -3
  198. package/templates/lifi-permissions/LifiBoundedApprovePermissionCloneable.sol +84 -84
  199. package/templates/lifi-permissions/LifiDiamondSwapPermissionCloneable.sol +97 -97
  200. package/templates/lifi-permissions/README.md +53 -53
  201. package/packages/ui/dist/assets/cursor-DV7rOqbJ.js +0 -3
  202. package/packages/ui/dist/assets/index-CKxgNxS9.css +0 -1
  203. package/packages/ui/dist/assets/index-Q2Yai4Fe.js +0 -2103
  204. package/templates/dca-rebalancer/.env.example +0 -6
  205. package/templates/dca-rebalancer/AGENTS.md +0 -246
  206. package/templates/dca-rebalancer/AGENT_PLAYBOOK.md +0 -110
  207. package/templates/dca-rebalancer/README.md +0 -16
  208. package/templates/dca-rebalancer/src/agent.ts +0 -253
  209. package/templates/dca-rebalancer/src/config.ts +0 -27
  210. package/templates/dca-rebalancer/tsconfig.json +0 -8
  211. /package/templates/{dca-rebalancer → default}/.sail/.gitkeep +0 -0
@@ -1,85 +1,116 @@
1
- # Write Your Own Permission — Sail Protocol
2
-
3
- Sail Protocol accepts ANY contract implementing `IPermission`. There is no fixed set of
4
- permission types. `BoundedCallPermission` here is a general primitive; `examples/permissions/`
5
- shows protocol-specific patterns. Every financial bound your mandate enforces should live in
6
- Solidity — the kernel checks `evaluate()` on every dispatch. The agent's TypeScript can be changed
7
- without your signature; the permission contract cannot. You own what you deploy.
8
-
9
- ---
10
-
11
- Sailor does not ship a blessed library of financial permission contracts. You author, review, and
12
- deploy your own `IPermission` contract, and Sailor makes deploying and registering it easy.
13
-
14
- ## What a permission contract is
15
-
16
- A permission contract is an on-chain policy that the SailKernel consults before it lets your agent
17
- (the manager) execute any transaction from your SMA. On every dispatch the kernel calls
18
- `evaluate(txData, ctx)` on each registered permission via `staticcall` — return `true` to allow the
19
- call, `false` to block it. The contract holds your rules (allowed targets, size caps, token
20
- allowlists, time windows, …) so the agent can only ever act inside the bounds you deployed.
21
-
22
- ## The IPermission interface
23
-
24
- ```solidity
25
- interface IPermission {
26
- function evaluate(bytes calldata txData, Context calldata ctx) external view returns (bool);
27
- function discriminator() external view returns (bytes32);
28
- }
29
-
30
- struct Context {
31
- address account; // the SMA (Safe) the dispatch executes from
32
- address manager; // the delegated signer authorized to dispatch
33
- address submitter; // the address that submitted the dispatch transaction
34
- address target; // the contract the call is directed at
35
- bytes4 selector; // the 4-byte function selector being called
36
- uint256 value; // msg.value (native asset) sent with the call
37
- uint256 blockTimestamp; // block.timestamp at evaluation
38
- uint256 blockNumber; // block.number at evaluation
39
- }
40
- ```
41
-
42
- - `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`.
44
- - `discriminator` — a stable `bytes32` name for your permission (e.g. `keccak256("MyMandate")`).
45
-
46
- Keep all policy parameters constructor-configured so each deployment is a complete, reviewable
47
- policy before it is attached to the SMA.
48
-
49
- ## Workflow
50
-
51
- ```bash
52
- # 1. Write your contract in mandates/ (start from BoundedCallPermission.sol)
53
- # 2. Compile
54
- forge build
55
-
56
- # 3. Deploy and attach in one step
57
- sailor mandate deploy --contract <Name> --attach --sma <SMA>
58
- ```
59
-
60
- Or deploy first and attach later (two-step):
61
-
62
- ```bash
63
- sailor mandate deploy --contract <Name> # prints the deployed address
64
- sailor mandate attach --address <deployedAddress> --sma <SMA>
65
- ```
66
-
67
- Both attach paths open the browser signing station so the owner authorizes the registration
68
- (EIP-712 `RegisterPermission`); the agent submits the on-chain transaction.
69
-
70
- ## Prerequisites
71
-
72
- - [Foundry](https://book.getfoundry.sh/getting-started/installation)
73
- - An existing Sailor agent (created with `sailor init`)
74
-
75
- ## Responsibility
76
-
77
- > **You are responsible for the correctness of your permission logic. Sailor registers whatever
78
- > contract address you provide. A bug can block all agent activity or authorize transactions you did
79
- > not intend. Review carefully before attaching.**
80
-
81
- ## Structure
82
-
83
- - `foundry.toml` Foundry config with `@sail/` remapping to `.sail/contracts/`
84
- - `.sail/contracts/interfaces/IPermission.sol` interface copy (matches SailProtocol)
85
- - `mandates/BoundedCallPermission.sol` — general primitive: allowlisted targets, optional selector filter, max ETH value
1
+ # Write Your Own Permission — Sail Protocol
2
+
3
+ Sail Protocol accepts ANY contract implementing `IPermission`. There is no fixed set of
4
+ permission types. `BoundedCallPermission` here is a general primitive; `examples/permissions/`
5
+ shows protocol-specific patterns. Every financial bound your mandate enforces should live in
6
+ Solidity — the kernel checks `evaluate()` on every dispatch. The agent's TypeScript can be changed
7
+ without your signature; the permission contract cannot. You own what you deploy.
8
+
9
+ ---
10
+
11
+ Sailor does not ship a blessed library of financial permission contracts. You author, review, and
12
+ deploy your own `IPermission` contract, and Sailor makes deploying and registering it easy.
13
+
14
+ ## What a permission contract is
15
+
16
+ A permission contract is an on-chain policy that the SailKernel consults before it lets your agent
17
+ (the manager) execute any transaction from your SMA. On every dispatch the kernel calls
18
+ `evaluate(txData, ctx)` on each registered permission via `staticcall` — return `true` to allow the
19
+ call, `false` to block it. The contract holds your rules (allowed targets, size caps, token
20
+ allowlists, time windows, …) so the agent can only ever act inside the bounds you deployed.
21
+
22
+ ## The IPermission interface
23
+
24
+ ```solidity
25
+ interface IPermission {
26
+ function evaluate(bytes calldata txData, Context calldata ctx) external view returns (bool);
27
+ function discriminator() external view returns (bytes32);
28
+ }
29
+
30
+ struct Context {
31
+ address account; // the SMA (Safe) the dispatch executes from
32
+ address manager; // the delegated signer authorized to dispatch
33
+ address submitter; // the address that submitted the dispatch transaction
34
+ address target; // the contract the call is directed at
35
+ bytes4 selector; // the 4-byte function selector being called
36
+ uint256 value; // msg.value (native asset) sent with the call
37
+ uint256 blockTimestamp; // block.timestamp at evaluation
38
+ uint256 blockNumber; // block.number at evaluation
39
+ }
40
+ ```
41
+
42
+ - `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`.
44
+ - `discriminator` — a stable `bytes32` name for your permission (e.g. `keccak256("MyMandate")`).
45
+
46
+ Keep all policy parameters constructor-configured so each deployment is a complete, reviewable
47
+ policy before it is attached to the SMA.
48
+
49
+ ## Workflow
50
+
51
+ ```bash
52
+ # 1. Write your contract in mandates/ (start from BoundedCallPermission.sol)
53
+ # 2. Compile
54
+ forge build
55
+
56
+ # 3. Deploy and attach in one step
57
+ sailor mandate deploy --contract <Name> --attach --sma <SMA>
58
+ ```
59
+
60
+ Or deploy first and attach later (two-step):
61
+
62
+ ```bash
63
+ sailor mandate deploy --contract <Name> # prints the deployed address
64
+ sailor mandate attach --address <deployedAddress> --sma <SMA>
65
+ ```
66
+
67
+ Both attach paths open the browser signing station so the owner authorizes the registration
68
+ (EIP-712 `RegisterPermission`); the agent submits the on-chain transaction.
69
+
70
+ ## Prerequisites
71
+
72
+ - [Foundry](https://book.getfoundry.sh/getting-started/installation)
73
+ - An existing Sailor agent (created with `sailor init`)
74
+
75
+ ## Responsibility
76
+
77
+ > **You are responsible for the correctness of your permission logic. Sailor registers whatever
78
+ > contract address you provide. A bug can block all agent activity or authorize transactions you did
79
+ > not intend. Review carefully before attaching.**
80
+
81
+ ## Extracting calldata parameters safely
82
+
83
+ When you need to bound a specific call argument (amount cap, recipient check, slippage floor),
84
+ use `SailCalldata` instead of manual `abi.decode`. The two common bugs it prevents:
85
+
86
+ 1. **Forgetting the length check** — decoding before checking `txData.length` can revert or
87
+ silently return wrong values. `SailCalldata.hasParams(txData, N)` is the one-line guard.
88
+ 2. **Wrong slot index** — off-by-one decodes the wrong parameter. Named helpers make the
89
+ intent explicit: `asAddress(txData, 0)`, `asUint256(txData, 1)`, `asAddress(txData, 2)`.
90
+
91
+ ```solidity
92
+ import {SailCalldata} from "./SailCalldata.sol";
93
+
94
+ function evaluate(bytes calldata txData, Context calldata ctx) external view returns (bool) {
95
+ if (ctx.target != POOL) return false;
96
+ if (ctx.selector != SEL_SUPPLY) return false;
97
+ // supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode)
98
+ if (!SailCalldata.hasParams(txData, 4)) return false;
99
+ address asset = SailCalldata.asAddress(txData, 0);
100
+ uint256 amount = SailCalldata.asUint256(txData, 1);
101
+ address onBehalfOf = SailCalldata.asAddress(txData, 2);
102
+ // ...
103
+ }
104
+ ```
105
+
106
+ Available helpers: `asAddress`, `asUint256`, `asInt256`, `asBytes32`, `asBool`,
107
+ `asUint128`, `asUint64`, `asUint32`, `asUint24`, `asUint16`, `asBytes4`.
108
+ Only covers static (fixed-size) types. For `bytes`, `string`, or dynamic arrays,
109
+ use `abi.decode(txData[4:], ...)` after the `hasParams` guard.
110
+
111
+ ## Structure
112
+
113
+ - `foundry.toml` — Foundry config with `@sail/` remapping to `.sail/contracts/`
114
+ - `.sail/contracts/interfaces/IPermission.sol` — interface copy (matches SailProtocol)
115
+ - `mandates/BoundedCallPermission.sol` — general primitive: allowlisted targets, optional selector filter, max ETH value
116
+ - `mandates/SailCalldata.sol` — safe calldata parameter extraction helpers
@@ -1,8 +1,8 @@
1
- [profile.default]
2
- src = "mandates"
3
- out = "out"
4
- libs = ["lib"]
5
- remappings = ["@sail/=.sail/contracts/"]
6
- solc = "0.8.26"
7
- optimizer = true
8
- optimizer_runs = 200
1
+ [profile.default]
2
+ src = "mandates"
3
+ out = "out"
4
+ libs = ["lib"]
5
+ remappings = ["@sail/=.sail/contracts/"]
6
+ solc = "0.8.26"
7
+ optimizer = true
8
+ optimizer_runs = 200
@@ -1,35 +1,41 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity 0.8.26;
3
-
4
- import {IPermission, Context} from "@sail/interfaces/IPermission.sol";
5
-
6
- /// @title BoundedCallPermission
7
- /// @notice General-purpose IPermission primitive. Bounds the universal properties of any call:
8
- /// allowed targets, allowed selectors, and max ETH value. Protocol-agnostic.
9
- /// For calldata-parameter bounds (amount caps, recipient checks, slippage), write a
10
- /// protocol-specific permission — see examples/permissions/ for the pattern per protocol.
11
- /// @dev Deploy one instance per SMA with constructor-configured parameters.
12
- contract BoundedCallPermission is IPermission {
13
- bytes32 private constant DISCRIMINATOR = keccak256("BoundedCallPermission");
14
-
15
- mapping(address => bool) public isAllowedTarget;
16
- mapping(bytes4 => bool) public isAllowedSelector;
17
- bool public immutable SELECTOR_FILTERING;
18
- uint256 public immutable MAX_VALUE;
19
-
20
- constructor(address[] memory allowedTargets, bytes4[] memory allowedSelectors, uint256 maxValue) {
21
- for (uint256 i = 0; i < allowedTargets.length; i++) isAllowedTarget[allowedTargets[i]] = true;
22
- SELECTOR_FILTERING = allowedSelectors.length > 0;
23
- for (uint256 i = 0; i < allowedSelectors.length; i++) isAllowedSelector[allowedSelectors[i]] = true;
24
- MAX_VALUE = maxValue;
25
- }
26
-
27
- function evaluate(bytes calldata, Context calldata ctx) external view returns (bool) {
28
- if (!isAllowedTarget[ctx.target]) return false;
29
- if (SELECTOR_FILTERING && !isAllowedSelector[ctx.selector]) return false;
30
- if (ctx.value > MAX_VALUE) return false;
31
- return true;
32
- }
33
-
34
- function discriminator() external pure returns (bytes32) { return DISCRIMINATOR; }
35
- }
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.26;
3
+
4
+ import {IPermission, Context} from "@sail/interfaces/IPermission.sol";
5
+ // SailCalldata: safe helpers for extracting calldata parameters inside evaluate().
6
+ // Use SailCalldata.hasParams(txData, N) + SailCalldata.asAddress/asUint256/... instead of
7
+ // manual abi.decode when you need to bound specific call arguments (amounts, recipients, etc.).
8
+ // See SailCalldata.sol for the full API and examples/permissions/ for protocol examples.
9
+ import {SailCalldata} from "./SailCalldata.sol";
10
+
11
+ /// @title BoundedCallPermission
12
+ /// @notice General-purpose IPermission primitive. Bounds the universal properties of any call:
13
+ /// allowed targets, allowed selectors, and max ETH value. Protocol-agnostic.
14
+ /// For calldata-parameter bounds (amount caps, recipient checks, slippage), use
15
+ /// SailCalldata (imported above) and write a protocol-specific permission —
16
+ /// see examples/permissions/ for the pattern per protocol.
17
+ /// @dev Deploy one instance per SMA with constructor-configured parameters.
18
+ contract BoundedCallPermission is IPermission {
19
+ bytes32 private constant DISCRIMINATOR = keccak256("BoundedCallPermission");
20
+
21
+ mapping(address => bool) public isAllowedTarget;
22
+ mapping(bytes4 => bool) public isAllowedSelector;
23
+ bool public immutable SELECTOR_FILTERING;
24
+ uint256 public immutable MAX_VALUE;
25
+
26
+ constructor(address[] memory allowedTargets, bytes4[] memory allowedSelectors, uint256 maxValue) {
27
+ for (uint256 i = 0; i < allowedTargets.length; i++) isAllowedTarget[allowedTargets[i]] = true;
28
+ SELECTOR_FILTERING = allowedSelectors.length > 0;
29
+ for (uint256 i = 0; i < allowedSelectors.length; i++) isAllowedSelector[allowedSelectors[i]] = true;
30
+ MAX_VALUE = maxValue;
31
+ }
32
+
33
+ function evaluate(bytes calldata, Context calldata ctx) external view returns (bool) {
34
+ if (!isAllowedTarget[ctx.target]) return false;
35
+ if (SELECTOR_FILTERING && !isAllowedSelector[ctx.selector]) return false;
36
+ if (ctx.value > MAX_VALUE) return false;
37
+ return true;
38
+ }
39
+
40
+ function discriminator() external pure returns (bytes32) { return DISCRIMINATOR; }
41
+ }
@@ -1,16 +1,16 @@
1
- # Mandates
2
-
3
- Solidity permission contracts live here. Each contract implements `@sail/interfaces/IPermission.sol`.
4
- The SailKernel calls `evaluate(txData, ctx)` before any manager dispatch. Return `true` to permit,
5
- `false` to block.
6
-
7
- ## Workflow
8
-
9
- ```bash
10
- forge build
11
- sailor mandate prepare
12
- sailor ui
13
- ```
14
-
15
- Keep all policy parameters constructor-configured so each deployment has a complete, reviewable
16
- policy before it is attached to the SMA.
1
+ # Mandates
2
+
3
+ Solidity permission contracts live here. Each contract implements `@sail/interfaces/IPermission.sol`.
4
+ The SailKernel calls `evaluate(txData, ctx)` before any manager dispatch. Return `true` to permit,
5
+ `false` to block.
6
+
7
+ ## Workflow
8
+
9
+ ```bash
10
+ forge build
11
+ sailor mandate prepare
12
+ sailor ui
13
+ ```
14
+
15
+ Keep all policy parameters constructor-configured so each deployment has a complete, reviewable
16
+ policy before it is attached to the SMA.
@@ -0,0 +1,118 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.26;
3
+
4
+ // ─────────────────────────────────────────────────────────────────────────────
5
+ // SailCalldata — safe static-parameter extraction for IPermission.evaluate()
6
+ //
7
+ // PROBLEM
8
+ // evaluate(bytes calldata txData, Context calldata ctx) receives raw ABI-
9
+ // encoded calldata. Extracting parameters by hand is the most dangerous part
10
+ // of permission writing:
11
+ // • Forgetting the length check before decoding → silent wrong value or
12
+ // revert instead of clean `return false`.
13
+ // • Wrong slot index → decoding the wrong parameter (type-checks, still wrong).
14
+ // • Truncation bugs when casting uint256 slots to address (padding bits).
15
+ //
16
+ // SOLUTION
17
+ // This library centralises the three operations into named helpers:
18
+ // 1. hasParams() — explicit length guard (call once, at the top of evaluate)
19
+ // 2. asAddress() — extract + mask to 20 bytes
20
+ // 3. asUint256(), asInt256(), asBytes32(), asBool(), asUint128() — typed slots
21
+ //
22
+ // All helpers are view/pure and add zero gas overhead beyond the slice itself.
23
+ //
24
+ // USAGE (replace abi.decode pattern)
25
+ //
26
+ // // Before:
27
+ // if (txData.length < 4 + 3 * 32) return false;
28
+ // (address asset, uint256 amount, address onBehalfOf) =
29
+ // abi.decode(txData[4:], (address, uint256, address));
30
+ //
31
+ // // After:
32
+ // if (!SailCalldata.hasParams(txData, 3)) return false;
33
+ // address asset = SailCalldata.asAddress(txData, 0);
34
+ // uint256 amount = SailCalldata.asUint256(txData, 1);
35
+ // address onBehalfOf = SailCalldata.asAddress(txData, 2);
36
+ //
37
+ // LIMITATIONS
38
+ // Only covers static (fixed-size) ABI types. Dynamic types (bytes, string,
39
+ // arrays) use pointer indirection — decode them with abi.decode(txData[4:], ...)
40
+ // after the hasParams() guard, or write a dedicated extractor.
41
+ //
42
+ // SLOT INDEXING
43
+ // Slots are 0-indexed from the first constructor parameter (after the 4-byte
44
+ // selector). Slot 0 = bytes [4..35], slot 1 = bytes [36..67], etc.
45
+ // ─────────────────────────────────────────────────────────────────────────────
46
+
47
+ library SailCalldata {
48
+ // ── Guards ────────────────────────────────────────────────────────────────
49
+
50
+ /// @notice Returns true when txData is long enough to hold `params` static
51
+ /// 32-byte ABI slots after the 4-byte selector.
52
+ /// @dev Call this once at the top of evaluate() before any slot access.
53
+ /// Returns false (not revert) so evaluate() can return false cleanly.
54
+ function hasParams(bytes calldata txData, uint256 params) internal pure returns (bool) {
55
+ return txData.length >= 4 + params * 32;
56
+ }
57
+
58
+ // ── Static-type extractors ────────────────────────────────────────────────
59
+ // All assume hasParams() has already been checked for the relevant slot.
60
+ // Accessing an out-of-bounds slot will cause the calldata slice to revert —
61
+ // always guard with hasParams() first.
62
+
63
+ /// @notice Extract an address from ABI slot `i` (0-indexed after selector).
64
+ /// Masks to 20 bytes, discarding the ABI zero-padding in the upper 12.
65
+ function asAddress(bytes calldata txData, uint256 i) internal pure returns (address) {
66
+ return address(uint160(uint256(bytes32(txData[4 + i * 32 : 4 + (i + 1) * 32]))));
67
+ }
68
+
69
+ /// @notice Extract a uint256 from ABI slot `i`.
70
+ function asUint256(bytes calldata txData, uint256 i) internal pure returns (uint256) {
71
+ return uint256(bytes32(txData[4 + i * 32 : 4 + (i + 1) * 32]));
72
+ }
73
+
74
+ /// @notice Extract an int256 from ABI slot `i`.
75
+ function asInt256(bytes calldata txData, uint256 i) internal pure returns (int256) {
76
+ return int256(uint256(bytes32(txData[4 + i * 32 : 4 + (i + 1) * 32])));
77
+ }
78
+
79
+ /// @notice Extract a bytes32 from ABI slot `i`.
80
+ function asBytes32(bytes calldata txData, uint256 i) internal pure returns (bytes32) {
81
+ return bytes32(txData[4 + i * 32 : 4 + (i + 1) * 32]);
82
+ }
83
+
84
+ /// @notice Extract a bool from ABI slot `i` (true when slot value is non-zero).
85
+ function asBool(bytes calldata txData, uint256 i) internal pure returns (bool) {
86
+ return asUint256(txData, i) != 0;
87
+ }
88
+
89
+ /// @notice Extract a uint128 from ABI slot `i` (lower 128 bits of the slot).
90
+ function asUint128(bytes calldata txData, uint256 i) internal pure returns (uint128) {
91
+ return uint128(asUint256(txData, i));
92
+ }
93
+
94
+ /// @notice Extract a uint64 from ABI slot `i`.
95
+ function asUint64(bytes calldata txData, uint256 i) internal pure returns (uint64) {
96
+ return uint64(asUint256(txData, i));
97
+ }
98
+
99
+ /// @notice Extract a uint32 from ABI slot `i`.
100
+ function asUint32(bytes calldata txData, uint256 i) internal pure returns (uint32) {
101
+ return uint32(asUint256(txData, i));
102
+ }
103
+
104
+ /// @notice Extract a uint24 from ABI slot `i` (e.g. Uniswap fee tier).
105
+ function asUint24(bytes calldata txData, uint256 i) internal pure returns (uint24) {
106
+ return uint24(asUint256(txData, i));
107
+ }
108
+
109
+ /// @notice Extract a uint16 from ABI slot `i` (e.g. Aave referral code).
110
+ function asUint16(bytes calldata txData, uint256 i) internal pure returns (uint16) {
111
+ return uint16(asUint256(txData, i));
112
+ }
113
+
114
+ /// @notice Extract bytes4 (a function selector) from ABI slot `i`.
115
+ function asBytes4(bytes calldata txData, uint256 i) internal pure returns (bytes4) {
116
+ return bytes4(asBytes32(txData, i));
117
+ }
118
+ }
@@ -1,25 +1,25 @@
1
- # Sail Agent Project
2
-
3
- You are an AI assistant helping the user set up and operate a Sail Protocol SMA (Separately Managed Account) using the Sailor toolkit.
4
-
5
- **Read `AGENTS.md` first.** It is the canonical guide — voice, stage-by-stage workflow, permission authoring, deployment, and automation.
6
-
7
- ## Quick reference
8
-
9
- | File | Purpose |
10
- |------|---------|
11
- | `AGENTS.md` | Full operator guide — start here |
12
- | `.sail/config.json` | Project config (chain, contracts) |
13
- | `.sail/account.json` | Active SMA address |
14
- | `.sail/.env.local` | RPC URL and passphrase — never commit |
15
- | `.sail/keys/manager.json` | Encrypted agent key |
16
- | `.sail/activity.jsonl` | Agent decision journal |
17
- | `mandates/` | Solidity permission contracts |
18
- | `examples/permissions/` | Protocol-specific permission patterns |
19
- | `docs/PERMISSION_MODEL.md` | Conjunctive vs selective kernel deep-dive |
20
-
21
- ## Rules
22
- - Work through AGENTS.md stages in order — never skip
23
- - Always ask before any action that costs gas or moves funds
24
- - The browser UI at localhost:3333 is required for mandate signing and monitoring
25
- - `SAIL_PASSPHRASE` unlocks the manager key headlessly for `sailor run`
1
+ # Sail Agent Project
2
+
3
+ You are an AI assistant helping the user set up and operate a Sail Protocol SMA (Separately Managed Account) using the Sailor toolkit.
4
+
5
+ **Read `AGENTS.md` first.** It is the canonical guide — voice, stage-by-stage workflow, permission authoring, deployment, and automation.
6
+
7
+ ## Quick reference
8
+
9
+ | File | Purpose |
10
+ |------|---------|
11
+ | `AGENTS.md` | Full operator guide — start here |
12
+ | `.sail/config.json` | Project config (chain, contracts) |
13
+ | `.sail/account.json` | Active SMA address |
14
+ | `.sail/.env.local` | RPC URL and passphrase — never commit |
15
+ | `.sail/keys/manager.json` | Encrypted agent key |
16
+ | `.sail/activity.jsonl` | Agent decision journal |
17
+ | `mandates/` | Solidity permission contracts |
18
+ | `examples/permissions/` | Protocol-specific permission patterns |
19
+ | `docs/PERMISSION_MODEL.md` | Conjunctive vs selective kernel deep-dive |
20
+
21
+ ## Rules
22
+ - Work through AGENTS.md stages in order — never skip
23
+ - Always ask before any action that costs gas or moves funds
24
+ - The browser UI at localhost:3333 is required for mandate signing and monitoring
25
+ - `SAIL_PASSPHRASE` unlocks the manager key headlessly for `sailor run`
@@ -0,0 +1,20 @@
1
+ # Sailor agent environment
2
+ #
3
+ # RPC configuration — two patterns, pick one:
4
+ #
5
+ # Option A: single active chain (simplest)
6
+ RPC_URL=https://your-rpc-endpoint
7
+ CHAIN_ID=8453
8
+ #
9
+ # Option B: per-chain endpoints (multi-chain projects, or if you prefer explicit names)
10
+ # Set CHAIN_ID to the chain sailor run uses; omit RPC_URL if all chains have a specific var.
11
+ # CHAIN_ID=8453
12
+ # BASE_RPC_URL=https://your-base-endpoint
13
+ # ARBITRUM_RPC_URL=https://your-arbitrum-endpoint
14
+ # UNICHAIN_RPC_URL=https://your-unichain-endpoint
15
+ # ETH_MAINNET_RPC_URL=https://your-mainnet-endpoint
16
+ # BASE_SEPOLIA_RPC_URL=https://your-base-sepolia-endpoint
17
+ # SEPOLIA_RPC_URL=https://your-sepolia-endpoint
18
+
19
+ # Optional: non-interactive passphrase (CI, GitHub Actions, launchd, systemd)
20
+ # SAIL_PASSPHRASE=change-me-to-a-strong-passphrase
@@ -1,32 +1,33 @@
1
- name: Agent Tick
2
-
3
- on:
4
- schedule:
5
- # Every Monday at 09:00 UTC
6
- - cron: "0 9 * * 1"
7
- workflow_dispatch:
8
-
9
- jobs:
10
- tick:
11
- runs-on: ubuntu-latest
12
- steps:
13
- - uses: actions/checkout@v4
14
-
15
- - uses: pnpm/action-setup@v4
16
- with:
17
- version: 9
18
-
19
- - uses: actions/setup-node@v4
20
- with:
21
- node-version: 20
22
- cache: "pnpm"
23
-
24
- - name: Install dependencies
25
- run: pnpm install --frozen-lockfile
26
-
27
- - name: Run agent tick
28
- env:
29
- RPC_URL: ${{ secrets.RPC_URL }}
30
- MANAGER_KEY: ${{ secrets.MANAGER_KEY }}
31
- CHAIN_ID: ${{ vars.CHAIN_ID || '8453' }}
32
- run: npx sailor run --once
1
+ name: Agent Tick
2
+
3
+ on:
4
+ schedule:
5
+ # Every Monday at 09:00 UTC
6
+ - cron: "0 9 * * 1"
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ tick:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - uses: actions/setup-node@v4
16
+ with:
17
+ node-version: 20
18
+ cache: "npm"
19
+
20
+ - name: Install dependencies
21
+ run: npm ci
22
+
23
+ - name: Copy keystore to expected path
24
+ run: |
25
+ mkdir -p .sail/keys
26
+ cp ci-keystore.json .sail/keys/manager.json
27
+
28
+ - name: Run agent tick
29
+ env:
30
+ RPC_URL: ${{ secrets.RPC_URL }}
31
+ SAIL_PASSPHRASE: ${{ secrets.SAIL_PASSPHRASE }}
32
+ CHAIN_ID: ${{ vars.CHAIN_ID || '8453' }}
33
+ run: npx sailor run --once
@@ -1,13 +1,13 @@
1
- # Sailor Project Workspace
2
-
3
- This folder is the local workspace for one Sailor agent deployment.
4
-
5
- ## Layout
6
-
7
- - `config.json` is the project manifest: name, chain, and state location.
8
- - `keys/` stores encrypted local signing keys. Never commit these files.
9
- - `runtime/` is for local UI and signing handoff state.
10
- - `state/` is for persistent agent state, audit logs, and tx history.
11
-
12
- AI coding agents should read the project's `AGENTS.md` and this folder's `config.json`
13
- before changing strategy code or running commands that touch funds.
1
+ # Sailor Project Workspace
2
+
3
+ This folder is the local workspace for one Sailor agent deployment.
4
+
5
+ ## Layout
6
+
7
+ - `config.json` is the project manifest: name, chain, and state location.
8
+ - `keys/` stores encrypted local signing keys. Never commit these files.
9
+ - `runtime/` is for local UI and signing handoff state.
10
+ - `state/` is for persistent agent state, audit logs, and tx history.
11
+
12
+ AI coding agents should read the project's `AGENTS.md` and this folder's `config.json`
13
+ before changing strategy code or running commands that touch funds.
@@ -1,10 +1,10 @@
1
- {
2
- "version": 1,
3
- "name": "dca-rebalancer",
4
- "chainId": 8453,
5
- "stateDir": ".sail/state",
6
- "contracts": {
7
- "kernel": "",
8
- "mandateFactory": ""
9
- }
10
- }
1
+ {
2
+ "version": 1,
3
+ "name": "sail-agent",
4
+ "chainId": null,
5
+ "stateDir": ".sail/state",
6
+ "contracts": {
7
+ "kernel": "",
8
+ "mandateFactory": ""
9
+ }
10
+ }