@dev.sail.money/sailor 0.0.2 → 1.0.0-38

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 (199) hide show
  1. package/AGENTS.md +43 -15
  2. package/README.md +217 -126
  3. package/examples/permissions/BoundedApproveAndCallBatch.sol +179 -0
  4. package/examples/permissions/BoundedBet_Limitless_Base.sol +8 -7
  5. package/examples/permissions/BoundedBorrow_AaveV3_Arbitrum.sol +13 -13
  6. package/examples/permissions/BoundedPerp_GMXv2_Arbitrum.sol +50 -39
  7. package/examples/permissions/BoundedStake_Venice_Base.sol +85 -0
  8. package/examples/permissions/BoundedSupply_AaveV3_Arbitrum.sol +82 -0
  9. package/examples/permissions/BoundedSwap_UniswapV3_Base.sol +15 -12
  10. package/examples/permissions/BoundedSwap_UniswapV4_Unichain.sol +14 -8
  11. package/examples/permissions/BoundedTransfer_ERC20_Ethereum.sol +6 -6
  12. package/examples/permissions/BoundedVault_ERC4626_Base.sol +97 -0
  13. package/examples/permissions/README.md +29 -2
  14. package/examples/permissions/SailCalldata.sol +118 -0
  15. package/examples/permissions/interfaces/IBatchPermission.sol +38 -0
  16. package/package.json +17 -12
  17. package/packages/cli/dist/index.cjs +4164 -2508
  18. package/packages/cli/dist/server.cjs +897 -1566
  19. package/packages/sdk/dist/chains.d.ts +12 -0
  20. package/packages/sdk/dist/chains.d.ts.map +1 -0
  21. package/packages/sdk/dist/chains.js +94 -0
  22. package/packages/sdk/dist/chains.js.map +1 -0
  23. package/packages/sdk/dist/deployments.d.ts +14 -7
  24. package/packages/sdk/dist/deployments.d.ts.map +1 -1
  25. package/packages/sdk/dist/deployments.js +132 -141
  26. package/packages/sdk/dist/deployments.js.map +1 -1
  27. package/packages/sdk/dist/index.d.ts +3 -2
  28. package/packages/sdk/dist/index.d.ts.map +1 -1
  29. package/packages/sdk/dist/index.js +3 -2
  30. package/packages/sdk/dist/index.js.map +1 -1
  31. package/packages/sdk/dist/intelligence.d.ts +1 -1
  32. package/packages/sdk/dist/intelligence.js +1 -1
  33. package/packages/sdk/dist/lifi.d.ts +17 -0
  34. package/packages/sdk/dist/lifi.d.ts.map +1 -1
  35. package/packages/sdk/dist/lifi.js +24 -0
  36. package/packages/sdk/dist/lifi.js.map +1 -1
  37. package/packages/sdk/dist/safe.d.ts +83 -0
  38. package/packages/sdk/dist/safe.d.ts.map +1 -1
  39. package/packages/sdk/dist/safe.js +92 -1
  40. package/packages/sdk/dist/safe.js.map +1 -1
  41. package/packages/sdk/dist/templates/ammLiquidity.d.ts +24 -11
  42. package/packages/sdk/dist/templates/ammLiquidity.d.ts.map +1 -1
  43. package/packages/sdk/dist/templates/ammLiquidity.js +39 -31
  44. package/packages/sdk/dist/templates/ammLiquidity.js.map +1 -1
  45. package/packages/sdk/dist/templates/approveAndCallBatch.d.ts +24 -10
  46. package/packages/sdk/dist/templates/approveAndCallBatch.d.ts.map +1 -1
  47. package/packages/sdk/dist/templates/approveAndCallBatch.js +36 -23
  48. package/packages/sdk/dist/templates/approveAndCallBatch.js.map +1 -1
  49. package/packages/sdk/dist/templates/boundedBorrow.d.ts +19 -9
  50. package/packages/sdk/dist/templates/boundedBorrow.d.ts.map +1 -1
  51. package/packages/sdk/dist/templates/boundedBorrow.js +28 -19
  52. package/packages/sdk/dist/templates/boundedBorrow.js.map +1 -1
  53. package/packages/sdk/dist/templates/boundedSwap.d.ts +19 -9
  54. package/packages/sdk/dist/templates/boundedSwap.d.ts.map +1 -1
  55. package/packages/sdk/dist/templates/boundedSwap.js +30 -20
  56. package/packages/sdk/dist/templates/boundedSwap.js.map +1 -1
  57. package/packages/sdk/dist/templates/defiBundle.d.ts +35 -9
  58. package/packages/sdk/dist/templates/defiBundle.d.ts.map +1 -1
  59. package/packages/sdk/dist/templates/defiBundle.js +84 -22
  60. package/packages/sdk/dist/templates/defiBundle.js.map +1 -1
  61. package/packages/sdk/dist/templates/pendle.d.ts +23 -8
  62. package/packages/sdk/dist/templates/pendle.d.ts.map +1 -1
  63. package/packages/sdk/dist/templates/pendle.js +34 -14
  64. package/packages/sdk/dist/templates/pendle.js.map +1 -1
  65. package/packages/sdk/dist/templates/transferTarget.d.ts +11 -3
  66. package/packages/sdk/dist/templates/transferTarget.d.ts.map +1 -1
  67. package/packages/sdk/dist/templates/transferTarget.js +14 -7
  68. package/packages/sdk/dist/templates/transferTarget.js.map +1 -1
  69. package/packages/sdk/dist/types.d.ts +19 -1
  70. package/packages/sdk/dist/types.d.ts.map +1 -1
  71. package/packages/sdk/package.json +28 -0
  72. package/packages/ui/dist/assets/{add-DaJhwIBV.js → add-Dl1etsL9.js} +1 -1
  73. package/packages/ui/dist/assets/{all-wallets-BUxsqWXi.js → all-wallets-C0eHLOGG.js} +1 -1
  74. package/packages/ui/dist/assets/{app-store-DkltwTqE.js → app-store-B-VMDEZ3.js} +1 -1
  75. package/packages/ui/dist/assets/{apple-owVOeaIT.js → apple-DkDXzKns.js} +1 -1
  76. package/packages/ui/dist/assets/{arrow-bottom-D2mmNJve.js → arrow-bottom-DtPzuS76.js} +1 -1
  77. package/packages/ui/dist/assets/{arrow-bottom-circle-CbNYijx-.js → arrow-bottom-circle-D7odSAO8.js} +1 -1
  78. package/packages/ui/dist/assets/{arrow-left-DJB61s4C.js → arrow-left-zJV9tpx0.js} +1 -1
  79. package/packages/ui/dist/assets/{arrow-right-BBrsQ9R4.js → arrow-right-BOREfe7o.js} +1 -1
  80. package/packages/ui/dist/assets/{arrow-top-Cil6bOc8.js → arrow-top-CipQc3Af.js} +1 -1
  81. package/packages/ui/dist/assets/{bank-CbwEmRo3.js → bank-C5s7eoV5.js} +1 -1
  82. package/packages/ui/dist/assets/{basic-CLNfjw3m.js → basic-D2es4Vq8.js} +1 -1
  83. package/packages/ui/dist/assets/{browser-B5TtF4Pb.js → browser-DITQWDC9.js} +1 -1
  84. package/packages/ui/dist/assets/{card-CO7BVB-C.js → card-C3DDkaYK.js} +1 -1
  85. package/packages/ui/dist/assets/{ccip-2W7K3_J3.js → ccip-UBXL3JiN.js} +1 -1
  86. package/packages/ui/dist/assets/{checkmark-bold-D9xGHzPE.js → checkmark-bold-D8yW0_K_.js} +1 -1
  87. package/packages/ui/dist/assets/{checkmark-BEtSHq9m.js → checkmark-ngef3MAl.js} +1 -1
  88. package/packages/ui/dist/assets/{chevron-bottom-BDztht6i.js → chevron-bottom-C56BipDR.js} +1 -1
  89. package/packages/ui/dist/assets/{chevron-left-EV4GFNbc.js → chevron-left-BmIPtPl_.js} +1 -1
  90. package/packages/ui/dist/assets/{chevron-right-B4_bB9oR.js → chevron-right-BnySHQ8h.js} +1 -1
  91. package/packages/ui/dist/assets/{chevron-top-D54xPNzF.js → chevron-top-BDGZnNW3.js} +1 -1
  92. package/packages/ui/dist/assets/{chrome-store-DYUpAJJq.js → chrome-store-BYIqJZVF.js} +1 -1
  93. package/packages/ui/dist/assets/{clock-Ca1T1Soz.js → clock-Bl4mUHAM.js} +1 -1
  94. package/packages/ui/dist/assets/{close-BZqWjurK.js → close-B9rhEX6U.js} +1 -1
  95. package/packages/ui/dist/assets/{coinPlaceholder-e6fl2XDo.js → coinPlaceholder-1cO0FQsl.js} +1 -1
  96. package/packages/ui/dist/assets/{compass-DCLC7zIh.js → compass-7i-VuXu2.js} +1 -1
  97. package/packages/ui/dist/assets/{copy-Th2AaD-O.js → copy-OqqXix2J.js} +1 -1
  98. package/packages/ui/dist/assets/{core-Ckx_cyuH.js → core-tX9kIIDJ.js} +3 -3
  99. package/packages/ui/dist/assets/cursor-BoyeQ9fN.js +3 -0
  100. package/packages/ui/dist/assets/{cursor-transparent-BKHeABKB.js → cursor-transparent-5aoRH67u.js} +1 -1
  101. package/packages/ui/dist/assets/{desktop-CBjY8t6F.js → desktop-BaPXK9R6.js} +1 -1
  102. package/packages/ui/dist/assets/{disconnect-DbSs2cli.js → disconnect-LlK5K1CF.js} +1 -1
  103. package/packages/ui/dist/assets/{discord-ZlLOAUkM.js → discord-BdcQNWY_.js} +1 -1
  104. package/packages/ui/dist/assets/{etherscan-CKUrqWYN.js → etherscan-Bb-WxpO1.js} +1 -1
  105. package/packages/ui/dist/assets/{events-CiKP71cK.js → events-DjdZr6no.js} +1 -1
  106. package/packages/ui/dist/assets/{exclamation-triangle-DA1QzFiO.js → exclamation-triangle-COx4VtPV.js} +1 -1
  107. package/packages/ui/dist/assets/{extension-BVJkmvpJ.js → extension-DF63DTWO.js} +1 -1
  108. package/packages/ui/dist/assets/{external-link-D_bsR7B2.js → external-link-DyghCkQu.js} +1 -1
  109. package/packages/ui/dist/assets/{facebook-CmFmhojx.js → facebook-Dcg4bZMR.js} +1 -1
  110. package/packages/ui/dist/assets/{fallback-Ofl6uSnB.js → fallback-DJIr_fH3.js} +1 -1
  111. package/packages/ui/dist/assets/{farcaster-Co-M3Ss8.js → farcaster-BkmV5HjO.js} +1 -1
  112. package/packages/ui/dist/assets/{filters-B1WwNaFU.js → filters-DLHj1T_P.js} +1 -1
  113. package/packages/ui/dist/assets/{github-CP4fP6gn.js → github-BFDCgKrF.js} +1 -1
  114. package/packages/ui/dist/assets/{google-CsOIXJ6V.js → google-C08SpmIy.js} +1 -1
  115. package/packages/ui/dist/assets/{help-circle-DiMkomdF.js → help-circle-DU1IFmWp.js} +1 -1
  116. package/packages/ui/dist/assets/{id-lmscL5LX.js → id-DtDRGf3L.js} +1 -1
  117. package/packages/ui/dist/assets/{image-B-ubJrY5.js → image-CgCXJEjT.js} +1 -1
  118. package/packages/ui/dist/assets/{index-Dbh5V1Z0.js → index-8chM4S5Y.js} +1 -1
  119. package/packages/ui/dist/assets/{index-BaukYv-x.js → index-B5sCtNuq.js} +1 -1
  120. package/packages/ui/dist/assets/{index-CF0KMmke.js → index-BBfBEazf.js} +3 -3
  121. package/packages/ui/dist/assets/{index-CZR1Qjhs.js → index-BhXPwltt.js} +1 -1
  122. package/packages/ui/dist/assets/index-Cm05Py20.css +1 -0
  123. package/packages/ui/dist/assets/{index-DVgfCzCo.js → index-D37bD6Yt.js} +1 -1
  124. package/packages/ui/dist/assets/index-DZfBh-cg.js +1775 -0
  125. package/packages/ui/dist/assets/{index.es-C78cE5SI.js → index.es-BEcNQEn-.js} +4 -4
  126. package/packages/ui/dist/assets/{info-Cqg57EVo.js → info-ClsdYA4P.js} +1 -1
  127. package/packages/ui/dist/assets/{info-circle-DkeSWNKV.js → info-circle-DJmn4Bsv.js} +1 -1
  128. package/packages/ui/dist/assets/{lightbulb-DNlO4qKh.js → lightbulb-CXSftjXS.js} +1 -1
  129. package/packages/ui/dist/assets/{mail-kVQ8Jb9Y.js → mail-cdYKOl9P.js} +1 -1
  130. package/packages/ui/dist/assets/{metamask-sdk-CBalSvz7.js → metamask-sdk-Co3aIEln.js} +1 -1
  131. package/packages/ui/dist/assets/{mobile-BEteuhF7.js → mobile-DEHYlk8L.js} +1 -1
  132. package/packages/ui/dist/assets/{more-DBWmXQli.js → more-Cq_fo8pI.js} +1 -1
  133. package/packages/ui/dist/assets/{network-placeholder-Dg1uUHiL.js → network-placeholder-_dLCK4xB.js} +1 -1
  134. package/packages/ui/dist/assets/{nftPlaceholder-i3AHSiD9.js → nftPlaceholder-Dz4HKEr4.js} +1 -1
  135. package/packages/ui/dist/assets/{off-BtMm0fi2.js → off-B1k1lhkr.js} +1 -1
  136. package/packages/ui/dist/assets/{parseSignature-Cb5FlWWg.js → parseSignature-Cr0ptV2X.js} +1 -1
  137. package/packages/ui/dist/assets/{play-store-iKKkXa6a.js → play-store-lYqe4eeL.js} +1 -1
  138. package/packages/ui/dist/assets/{plus-CA5NaRtb.js → plus-QMgh1krr.js} +1 -1
  139. package/packages/ui/dist/assets/{qr-code-D2kiqR7h.js → qr-code-ClVHbZWN.js} +1 -1
  140. package/packages/ui/dist/assets/{recycle-horizontal-Dcme7R03.js → recycle-horizontal-CxGYnWid.js} +1 -1
  141. package/packages/ui/dist/assets/{refresh-Dega3sDp.js → refresh-CPysMza_.js} +1 -1
  142. package/packages/ui/dist/assets/{reown-logo-xNkksyWJ.js → reown-logo-OoL_zJd0.js} +1 -1
  143. package/packages/ui/dist/assets/{search-HYl7NO8x.js → search-2GaRbf1I.js} +1 -1
  144. package/packages/ui/dist/assets/{secp256k1-Cxd6_SiH.js → secp256k1-BrB8qSSy.js} +1 -1
  145. package/packages/ui/dist/assets/{send-CJU8CUAo.js → send-CC2UuIfD.js} +1 -1
  146. package/packages/ui/dist/assets/{swapHorizontal-IMUKiUre.js → swapHorizontal-BRqYwsqT.js} +1 -1
  147. package/packages/ui/dist/assets/{swapHorizontalBold-CNYnNJ9-.js → swapHorizontalBold-Bj0GSRq9.js} +1 -1
  148. package/packages/ui/dist/assets/{swapHorizontalMedium-B9VxEYsT.js → swapHorizontalMedium-oLOjpU2A.js} +1 -1
  149. package/packages/ui/dist/assets/{swapHorizontalRoundedBold-Dz33l_Jh.js → swapHorizontalRoundedBold-TJ652QXb.js} +1 -1
  150. package/packages/ui/dist/assets/{swapVertical-CHUmjVJ0.js → swapVertical-e0NLyV3x.js} +1 -1
  151. package/packages/ui/dist/assets/{telegram-kl9S2mbU.js → telegram-WhJHVeoU.js} +1 -1
  152. package/packages/ui/dist/assets/{three-dots-U5lhA1Am.js → three-dots-BlBAOyW-.js} +1 -1
  153. package/packages/ui/dist/assets/{twitch-KTEUWXEp.js → twitch-BH7vWmPc.js} +1 -1
  154. package/packages/ui/dist/assets/{twitterIcon-BHiq8mRg.js → twitterIcon-As0Nkanp.js} +1 -1
  155. package/packages/ui/dist/assets/{verify-CfN-BXNd.js → verify-BEJ0QuLl.js} +1 -1
  156. package/packages/ui/dist/assets/{verify-filled-DwZccetj.js → verify-filled-B8Ww2N7z.js} +1 -1
  157. package/packages/ui/dist/assets/{w3m-modal-CS-PFqPE.js → w3m-modal-5rOSZgOR.js} +1 -1
  158. package/packages/ui/dist/assets/{wallet-DVlGkhOY.js → wallet-CAfC3aml.js} +1 -1
  159. package/packages/ui/dist/assets/{wallet-placeholder-CvR_iEWX.js → wallet-placeholder-4RZI464Z.js} +1 -1
  160. package/packages/ui/dist/assets/{walletconnect-8pZBDvVI.js → walletconnect-BiltKqAe.js} +1 -1
  161. package/packages/ui/dist/assets/{warning-circle-ylLEE0Yp.js → warning-circle-CI4jqpHo.js} +1 -1
  162. package/packages/ui/dist/assets/{x-C_TBsTMj.js → x-FBttjBWO.js} +1 -1
  163. package/packages/ui/dist/index.html +2 -2
  164. package/scripts/check-init.mjs +2 -3
  165. package/templates/custom-mandate/README.md +31 -0
  166. package/templates/custom-mandate/mandates/BoundedCallPermission.sol +8 -2
  167. package/templates/custom-mandate/mandates/SailCalldata.sol +118 -0
  168. package/templates/default/.env.example +20 -0
  169. package/templates/{dca-rebalancer → default}/.github/workflows/agent-tick.yml +8 -7
  170. package/templates/{dca-rebalancer → default}/.sail/config.json +2 -2
  171. package/templates/default/AGENTS.md +171 -0
  172. package/templates/default/README.md +16 -0
  173. package/templates/default/examples/dca/README.md +16 -0
  174. package/templates/default/examples/dca/agent.ts +174 -0
  175. package/templates/{dca-rebalancer/src → default/examples/dca}/mandate.ts +9 -31
  176. package/templates/{dca-rebalancer → default}/package.json +2 -2
  177. package/templates/default/src/agent.ts +37 -0
  178. package/templates/default/src/config.ts +24 -0
  179. package/templates/default/src/mandate.ts +22 -0
  180. package/templates/default/tsconfig.json +17 -0
  181. package/templates/lifi-permissions/README.md +1 -1
  182. package/packages/ui/dist/assets/cursor-DV7rOqbJ.js +0 -3
  183. package/packages/ui/dist/assets/index-CKxgNxS9.css +0 -1
  184. package/packages/ui/dist/assets/index-Q2Yai4Fe.js +0 -2103
  185. package/scripts/postinstall.js +0 -366
  186. package/templates/dca-rebalancer/.env.example +0 -6
  187. package/templates/dca-rebalancer/AGENTS.md +0 -246
  188. package/templates/dca-rebalancer/AGENT_PLAYBOOK.md +0 -110
  189. package/templates/dca-rebalancer/README.md +0 -16
  190. package/templates/dca-rebalancer/src/agent.ts +0 -253
  191. package/templates/dca-rebalancer/src/config.ts +0 -27
  192. package/templates/dca-rebalancer/tsconfig.json +0 -8
  193. /package/templates/{dca-rebalancer → default}/.cursor/rules +0 -0
  194. /package/templates/{dca-rebalancer → default}/.sail/.gitkeep +0 -0
  195. /package/templates/{dca-rebalancer → default}/.sail/README.md +0 -0
  196. /package/templates/{dca-rebalancer → default}/CLAUDE.md +0 -0
  197. /package/templates/{dca-rebalancer → default}/_gitignore +0 -0
  198. /package/templates/{dca-rebalancer → default}/docs/PERMISSION_MODEL.md +0 -0
  199. /package/templates/{dca-rebalancer → default}/ui/README.md +0 -0
@@ -0,0 +1,174 @@
1
+ // Reference example — not the user's strategy. Consult for patterns; author the user's own in src/.
2
+ // Shows: a complete DCA tick loop — USDC→WETH via Uniswap V3 on Base mainnet.
3
+ // To adapt: replace token addresses, protocol ABIs, and swap logic with your target strategy.
4
+
5
+ import type { Agent, AgentContext, Call, Dispatch } from "@sail.money/sailor/sdk";
6
+ import { encodeFunctionData, type PublicClient } from "viem";
7
+ import {
8
+ ALLOWED_TOKENS,
9
+ MIN_USDC_TO_SWAP,
10
+ QUOTER_V2,
11
+ SLIPPAGE_BPS,
12
+ SWAP_AMOUNT_USDC,
13
+ SWAP_FEE_TIER,
14
+ SWAP_ROUTER,
15
+ } from "./mandate.js";
16
+
17
+ // ── ABI fragments ─────────────────────────────────────────────────────────────
18
+
19
+ const ERC20_ABI = [
20
+ {
21
+ name: "allowance",
22
+ type: "function",
23
+ stateMutability: "view",
24
+ inputs: [
25
+ { name: "owner", type: "address" },
26
+ { name: "spender", type: "address" },
27
+ ],
28
+ outputs: [{ type: "uint256" }],
29
+ },
30
+ {
31
+ name: "approve",
32
+ type: "function",
33
+ stateMutability: "nonpayable",
34
+ inputs: [
35
+ { name: "spender", type: "address" },
36
+ { name: "amount", type: "uint256" },
37
+ ],
38
+ outputs: [{ type: "bool" }],
39
+ },
40
+ ] as const;
41
+
42
+ const QUOTER_V2_ABI = [
43
+ {
44
+ name: "quoteExactInputSingle",
45
+ type: "function",
46
+ stateMutability: "nonpayable",
47
+ inputs: [
48
+ {
49
+ name: "params",
50
+ type: "tuple",
51
+ components: [
52
+ { name: "tokenIn", type: "address" },
53
+ { name: "tokenOut", type: "address" },
54
+ { name: "amountIn", type: "uint256" },
55
+ { name: "fee", type: "uint24" },
56
+ { name: "sqrtPriceLimitX96", type: "uint160" },
57
+ ],
58
+ },
59
+ ],
60
+ outputs: [
61
+ { name: "amountOut", type: "uint256" },
62
+ { name: "sqrtPriceX96After", type: "uint160" },
63
+ { name: "initializedTicksCrossed", type: "uint32" },
64
+ { name: "gasEstimate", type: "uint256" },
65
+ ],
66
+ },
67
+ ] as const;
68
+
69
+ const SWAP_ROUTER_ABI = [
70
+ {
71
+ name: "exactInputSingle",
72
+ type: "function",
73
+ stateMutability: "payable",
74
+ inputs: [
75
+ {
76
+ name: "params",
77
+ type: "tuple",
78
+ components: [
79
+ { name: "tokenIn", type: "address" },
80
+ { name: "tokenOut", type: "address" },
81
+ { name: "fee", type: "uint24" },
82
+ { name: "recipient", type: "address" },
83
+ { name: "amountIn", type: "uint256" },
84
+ { name: "amountOutMinimum", type: "uint256" },
85
+ { name: "sqrtPriceLimitX96", type: "uint160" },
86
+ ],
87
+ },
88
+ ],
89
+ outputs: [{ name: "amountOut", type: "uint256" }],
90
+ },
91
+ ] as const;
92
+
93
+ // ── Intent builder ────────────────────────────────────────────────────────────
94
+
95
+ function intent(call: Call): Dispatch {
96
+ return { txHash: "0x", calls: [call], success: false, gasUsed: 0n };
97
+ }
98
+
99
+ // ── Agent ─────────────────────────────────────────────────────────────────────
100
+
101
+ export const agent: Agent = {
102
+ name: "dca-rebalancer",
103
+ description: `DCA into WETH with USDC on Base via Uniswap V3. Slippage tolerance: ${SLIPPAGE_BPS / 100}%.`,
104
+
105
+ async tick(ctx: AgentContext): Promise<Dispatch[]> {
106
+ const { safe } = ctx;
107
+ ctx.log(`tick — block ${ctx.blockNumber}, sma ${safe}`);
108
+
109
+ const pc = ctx.data._publicClient as PublicClient | undefined;
110
+ if (!pc) {
111
+ ctx.log("no publicClient in ctx.data — skipping tick");
112
+ return [];
113
+ }
114
+
115
+ const usdc = ALLOWED_TOKENS[0]!;
116
+ const weth = ALLOWED_TOKENS[1]!;
117
+
118
+ // Step 1: Check USDC balance
119
+ const usdcBalance = await ctx.read.balance(usdc);
120
+ ctx.log(`USDC balance: ${usdcBalance} (min to swap: ${MIN_USDC_TO_SWAP})`);
121
+ if (usdcBalance < MIN_USDC_TO_SWAP) {
122
+ ctx.log("USDC balance below minimum — skipping tick");
123
+ return [];
124
+ }
125
+
126
+ // Step 2: Check allowance — approve first if needed
127
+ const allowance = await pc.readContract({
128
+ address: usdc,
129
+ abi: ERC20_ABI,
130
+ functionName: "allowance",
131
+ args: [safe, SWAP_ROUTER],
132
+ });
133
+ if (allowance < SWAP_AMOUNT_USDC) {
134
+ ctx.log(`allowance (${allowance}) < swap amount — submitting approve`);
135
+ return [intent({
136
+ target: usdc,
137
+ value: 0n,
138
+ data: encodeFunctionData({ abi: ERC20_ABI, functionName: "approve", args: [SWAP_ROUTER, 2n ** 256n - 1n] }),
139
+ })];
140
+ }
141
+
142
+ // Step 3: Quote via QuoterV2 — fail closed on any error
143
+ let expectedOut: bigint;
144
+ try {
145
+ const result = await pc.simulateContract({
146
+ address: QUOTER_V2,
147
+ abi: QUOTER_V2_ABI,
148
+ functionName: "quoteExactInputSingle",
149
+ args: [{ tokenIn: usdc, tokenOut: weth, amountIn: SWAP_AMOUNT_USDC, fee: SWAP_FEE_TIER, sqrtPriceLimitX96: 0n }],
150
+ });
151
+ expectedOut = (result.result as [bigint, bigint, number, bigint])[0];
152
+ } catch (e) {
153
+ ctx.log(`QuoterV2 unavailable: ${(e as Error).message.slice(0, 100)} — skipping`);
154
+ return [];
155
+ }
156
+ if (expectedOut === 0n) {
157
+ ctx.log("QuoterV2 returned 0 — skipping");
158
+ return [];
159
+ }
160
+
161
+ // Step 4: Encode swap with slippage protection
162
+ const minOut = (expectedOut * BigInt(10_000 - SLIPPAGE_BPS)) / 10_000n;
163
+ ctx.log(`quote: ${expectedOut} wei WETH, minOut (${SLIPPAGE_BPS / 100}% slippage): ${minOut}`);
164
+ return [intent({
165
+ target: SWAP_ROUTER,
166
+ value: 0n,
167
+ data: encodeFunctionData({
168
+ abi: SWAP_ROUTER_ABI,
169
+ functionName: "exactInputSingle",
170
+ args: [{ tokenIn: usdc, tokenOut: weth, fee: SWAP_FEE_TIER, recipient: safe, amountIn: SWAP_AMOUNT_USDC, amountOutMinimum: minOut, sqrtPriceLimitX96: 0n }],
171
+ }),
172
+ })];
173
+ },
174
+ };
@@ -1,4 +1,7 @@
1
- import type { Address } from "@sail/sdk";
1
+ // Reference example not the user's strategy. Consult for patterns; author the user's own in src/.
2
+ // Shows: token addresses, swap parameters, and contract addresses for a USDC→WETH DCA on Base mainnet.
3
+
4
+ import type { Address } from "@sail.money/sailor/sdk";
2
5
 
3
6
  // ── Token addresses (Base mainnet) ────────────────────────────────────────────
4
7
 
@@ -14,38 +17,18 @@ export const ALLOWED_TOKENS: Address[] = [
14
17
 
15
18
  // ── Swap parameters ───────────────────────────────────────────────────────────
16
19
 
17
- /**
18
- * Amount of USDC to spend per swap (in USDC base units, 6 decimals).
19
- * Default: 5 USDC. Adjust before deploying to production.
20
- */
20
+ /** Amount of USDC to spend per swap (in USDC base units, 6 decimals). Default: 5 USDC. */
21
21
  export const SWAP_AMOUNT_USDC = 5_000_000n; // 5 USDC
22
22
 
23
- /**
24
- * Minimum USDC balance the SMA must hold before a swap is attempted.
25
- * Must be ≥ SWAP_AMOUNT_USDC. Adds a safety margin so a single swap
26
- * doesn't drain the account to zero.
27
- */
23
+ /** Minimum USDC balance the SMA must hold before a swap is attempted. */
28
24
  export const MIN_USDC_TO_SWAP = 6_000_000n; // 6 USDC
29
25
 
30
- /**
31
- * Slippage tolerance in basis points (100 = 1%).
32
- * amountOutMinimum = quote × (1 − SLIPPAGE_BPS / 10 000).
33
- * If no quote is available, the agent skips the swap entirely (fail closed).
34
- *
35
- * Tighten for large trades or illiquid pairs; loosen if valid swaps are
36
- * frequently denied by price impact. Default 1% is conservative for
37
- * USDC/WETH on Base.
38
- */
26
+ /** Slippage tolerance in basis points (100 = 1%). */
39
27
  export const SLIPPAGE_BPS = 100; // 1%
40
28
 
41
- /**
42
- * Uniswap V3 pool fee tier for the USDC/WETH pool on Base.
43
- * 500 = 0.05% — the most liquid USDC/WETH pool on Base mainnet.
44
- */
29
+ /** Uniswap V3 pool fee tier for the USDC/WETH pool on Base (500 = 0.05%). */
45
30
  export const SWAP_FEE_TIER = 500;
46
31
 
47
- // ── Rebalancing ───────────────────────────────────────────────────────────────
48
-
49
32
  /** Rebalance when allocation drift exceeds this fraction (0.05 = 5%). */
50
33
  export const REBALANCE_THRESHOLD = 0.05;
51
34
 
@@ -57,11 +40,6 @@ export const SWAP_ROUTER: Address = "0x2626664c2603336E57B271c5C0b26F421741e481"
57
40
  /**
58
41
  * Uniswap V3 QuoterV2 on Base.
59
42
  * Called off-chain (via eth_call) to obtain the expected output amount
60
- * before computing amountOutMinimum. If this call fails, the agent skips
61
- * the swap — it never submits with amountOutMinimum = 0.
62
- *
63
- * Note: amountOutMinimum is set by the agent from the QuoterV2 result.
64
- * For defense-in-depth, add an on-chain minimum check in your permission
65
- * contract so the kernel also validates the minimum out requirement.
43
+ * before computing amountOutMinimum.
66
44
  */
67
45
  export const QUOTER_V2: Address = "0x3d4e44Eb1374240CE5F1B871ab261CD16335B76a";
@@ -1,7 +1,7 @@
1
1
  {
2
- "name": "dca-rebalancer",
2
+ "name": "sail-agent",
3
3
  "version": "0.1.0",
4
- "description": "Dollar-cost-average rebalancer — starter Sail Protocol agent",
4
+ "description": "Sail Protocol agent starter",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "typecheck": "tsc -p tsconfig.json --noEmit"
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Your agent — implement your strategy here.
3
+ *
4
+ * The runner calls tick() on every interval. Return an array of Dispatch intents;
5
+ * the runner submits each one through the kernel against a matching registered permission.
6
+ * Return [] to skip this tick (no gas spent).
7
+ *
8
+ * The runner resolves which permission authorizes each call automatically — you
9
+ * express what you want to do; you do not name permissions per call.
10
+ *
11
+ * For a worked end-to-end example (DCA / Uniswap V3 / Base):
12
+ * see examples/dca/agent.ts and examples/dca/mandate.ts
13
+ */
14
+
15
+ import type { Agent, AgentContext, Dispatch } from "@sail.money/sailor/sdk";
16
+
17
+ export const agent: Agent = {
18
+ name: "my-agent",
19
+ description: "Describe your strategy here.",
20
+
21
+ async tick(ctx: AgentContext): Promise<Dispatch[]> {
22
+ ctx.log(`tick — block ${ctx.blockNumber}, sma ${ctx.safe}`);
23
+
24
+ // TODO: implement your strategy.
25
+ // Read on-chain state, decide what to do, return intent dispatches.
26
+ // ctx.read.balance(tokenAddress) — read token balance of the SMA
27
+ // ctx.data._publicClient — viem PublicClient for arbitrary on-chain reads
28
+ // ctx.log(msg) — append a message to the activity log
29
+ //
30
+ // Example (from examples/dca/agent.ts):
31
+ // const balance = await ctx.read.balance(USDC_ADDRESS);
32
+ // if (balance < MIN_AMOUNT) return [];
33
+ // return [{ txHash: "0x", calls: [{ target: ROUTER, value: 0n, data: swapCalldata }], success: false, gasUsed: 0n }];
34
+
35
+ return [];
36
+ },
37
+ };
@@ -0,0 +1,24 @@
1
+ /** Reads RPC_URL and CHAIN_ID from environment (set via .sail/.env.local or GitHub Secrets). */
2
+ export function getEnvConfig(): { rpcUrl: string; chainId: number } {
3
+ const rpcUrl = process.env["RPC_URL"];
4
+ if (!rpcUrl) {
5
+ throw new Error(
6
+ "RPC_URL is not set.\n" +
7
+ "Add RPC_URL to .sail/.env.local or set it as an environment variable.",
8
+ );
9
+ }
10
+
11
+ if (!process.env["CHAIN_ID"]) {
12
+ throw new Error(
13
+ "CHAIN_ID is not set.\n" +
14
+ "Open this folder in your AI coding assistant and say 'start' — Stage 1 will ask\n" +
15
+ "which chain you want and set CHAIN_ID in .sail/.env.local.",
16
+ );
17
+ }
18
+ const chainId = Number(process.env["CHAIN_ID"]);
19
+ if (Number.isNaN(chainId)) {
20
+ throw new Error(`Invalid CHAIN_ID: ${process.env["CHAIN_ID"]}`);
21
+ }
22
+
23
+ return { rpcUrl, chainId };
24
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Strategy parameters and contract addresses for your agent.
3
+ *
4
+ * Put your token addresses, protocol contracts, amounts, and other
5
+ * constants here. Import them into agent.ts.
6
+ *
7
+ * For a worked example (DCA / USDC→WETH / Uniswap V3 / Base):
8
+ * see examples/dca/mandate.ts
9
+ *
10
+ * For protocol-specific permission examples (Uniswap, Aave, GMX, …):
11
+ * see examples/permissions/
12
+ */
13
+
14
+ // TODO: replace with your strategy's parameters.
15
+ // Example structure:
16
+ //
17
+ // import type { Address } from "@sail/sdk";
18
+ //
19
+ // export const INPUT_TOKEN: Address = "0x..."; // token you're spending
20
+ // export const OUTPUT_TOKEN: Address = "0x..."; // token you're acquiring
21
+ // export const MAX_AMOUNT_PER_TICK = 0n; // in input token base units
22
+ // export const PROTOCOL_CONTRACT: Address = "0x..."; // target contract
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "strict": true,
4
+ "target": "ES2022",
5
+ "module": "NodeNext",
6
+ "moduleResolution": "NodeNext",
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "resolveJsonModule": true,
10
+ "noEmit": true,
11
+ "baseUrl": ".",
12
+ "paths": {
13
+ "@sail.money/sailor/sdk": ["../../packages/sdk/src/index.ts"]
14
+ }
15
+ },
16
+ "include": ["src"]
17
+ }
@@ -17,7 +17,7 @@ These follow the `CloneInitializable` pattern: the constructor calls
17
17
  > no Foundry build. Build/deploy happens in that project via
18
18
  > `scripts/deploy-clone-templates.ts`.
19
19
 
20
- > **Note:** The kernels bundled in `@sail/sdk` (Base, Base Sepolia, Arbitrum, Unichain) now all run the **selective** dispatch model — verified on-chain against each kernel's `DISPATCH_TYPEHASH`. These templates were written for the older conjunctive model and include pass-through logic (`return true` for calls outside their domain) that is not required on selective kernels. Review before deploying against a selective kernel; the pass-through logic is harmless but unnecessary.
20
+ > **Note:** The kernels bundled in `@sail.money/sailor/sdk` (Base, Base Sepolia, Arbitrum, Unichain) now all run the **selective** dispatch model — verified on-chain against each kernel's `DISPATCH_TYPEHASH`. These templates were written for the older conjunctive model and include pass-through logic (`return true` for calls outside their domain) that is not required on selective kernels. Review before deploying against a selective kernel; the pass-through logic is harmless but unnecessary.
21
21
 
22
22
  ## Contracts
23
23
 
@@ -1,3 +0,0 @@
1
- import{F as o}from"./core-Ckx_cyuH.js";import"./index-Q2Yai4Fe.js";import"./events-CiKP71cK.js";import"./index.es-C78cE5SI.js";import"./fallback-Ofl6uSnB.js";const l=o` <svg fill="none" viewBox="0 0 13 4">
2
- <path fill="currentColor" d="M.5 0h12L8.9 3.13a3.76 3.76 0 0 1-4.8 0L.5 0Z" />
3
- </svg>`;export{l as cursorSvg};