@dev.sail.money/sailor 0.0.2-31 → 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.
- package/AGENTS.md +140 -140
- package/LICENSE +21 -21
- package/README.md +430 -430
- package/docs/PERMISSION_MODEL.md +93 -93
- package/examples/permissions/BoundedApproveAndCallBatch.sol +179 -179
- package/examples/permissions/BoundedBet_Limitless_Base.sol +97 -97
- package/examples/permissions/BoundedBorrow_AaveV3_Arbitrum.sol +94 -94
- package/examples/permissions/BoundedPerp_GMXv2_Arbitrum.sol +154 -154
- package/examples/permissions/BoundedStake_Venice_Base.sol +85 -85
- package/examples/permissions/BoundedSupply_AaveV3_Arbitrum.sol +82 -82
- package/examples/permissions/BoundedSwap_UniswapV3_Base.sol +116 -116
- package/examples/permissions/BoundedSwap_UniswapV4_Unichain.sol +150 -150
- package/examples/permissions/BoundedTransfer_ERC20_Ethereum.sol +73 -73
- package/examples/permissions/BoundedVault_ERC4626_Base.sol +97 -97
- package/examples/permissions/README.md +79 -79
- package/examples/permissions/SailCalldata.sol +118 -118
- package/examples/permissions/foundry.toml +10 -10
- package/examples/permissions/interfaces/IBatchPermission.sol +38 -38
- package/examples/permissions/interfaces/IPermission.sol +18 -18
- package/package.json +45 -45
- package/packages/cli/README.md +34 -34
- package/packages/cli/dist/index.cjs +705 -687
- package/packages/cli/dist/server.cjs +506 -495
- package/packages/sdk/README.md +65 -65
- package/packages/sdk/dist/intelligence.d.ts +1 -1
- package/packages/sdk/dist/intelligence.js +1 -1
- package/packages/sdk/package.json +80 -80
- package/packages/ui/dist/assets/{add-B0J2XPqD.js → add-BxpXfVWe.js} +1 -1
- package/packages/ui/dist/assets/{all-wallets-DAWTUGbI.js → all-wallets-BKTn_sWK.js} +1 -1
- package/packages/ui/dist/assets/{app-store-B-bz9zO1.js → app-store-CfuKbwxR.js} +1 -1
- package/packages/ui/dist/assets/{apple-CW_aatUl.js → apple-BKSBbNYg.js} +1 -1
- package/packages/ui/dist/assets/{arrow-bottom-D9xphoWP.js → arrow-bottom-D4bG6gZi.js} +1 -1
- package/packages/ui/dist/assets/{arrow-bottom-circle-D-N3HlXh.js → arrow-bottom-circle-BNTs1p0T.js} +1 -1
- package/packages/ui/dist/assets/{arrow-left-DofAd9ta.js → arrow-left-2uee3vYv.js} +1 -1
- package/packages/ui/dist/assets/{arrow-right-CLBZVLVF.js → arrow-right-BktjMV6h.js} +1 -1
- package/packages/ui/dist/assets/{arrow-top-B47Y4sI6.js → arrow-top-Izu28fX4.js} +1 -1
- package/packages/ui/dist/assets/{bank-CVHPZeNC.js → bank-USBaAyFM.js} +1 -1
- package/packages/ui/dist/assets/{basic-CijTV8XG.js → basic-C_9KjTEH.js} +1 -1
- package/packages/ui/dist/assets/{browser-D12J6hPl.js → browser-DAEMAKV7.js} +1 -1
- package/packages/ui/dist/assets/{card-suad8wBG.js → card-DT8yDkKN.js} +1 -1
- package/packages/ui/dist/assets/{ccip-Bev57e2Y.js → ccip-CkqfGSxX.js} +1 -1
- package/packages/ui/dist/assets/{checkmark-DSzbM9ge.js → checkmark-CsgdEXFj.js} +1 -1
- package/packages/ui/dist/assets/{checkmark-bold-Ctlpy8fR.js → checkmark-bold-D2gjOQo2.js} +1 -1
- package/packages/ui/dist/assets/{chevron-bottom-DD4PYpsh.js → chevron-bottom-tprFynYV.js} +1 -1
- package/packages/ui/dist/assets/{chevron-left-HJzgI5fr.js → chevron-left-D2Zj1gNB.js} +1 -1
- package/packages/ui/dist/assets/{chevron-right-BAJMtoWG.js → chevron-right-D1rRuAVe.js} +1 -1
- package/packages/ui/dist/assets/{chevron-top-CSTGBRNq.js → chevron-top-24dL1mbL.js} +1 -1
- package/packages/ui/dist/assets/{chrome-store-CSgmzP0o.js → chrome-store-Vy-5niYX.js} +1 -1
- package/packages/ui/dist/assets/{clock-BGKXrbjA.js → clock-qBjLnVdJ.js} +1 -1
- package/packages/ui/dist/assets/{close-B-9LI-cc.js → close-DARDwgcu.js} +1 -1
- package/packages/ui/dist/assets/{coinPlaceholder-C9zB6O8f.js → coinPlaceholder-BvpIbPlD.js} +1 -1
- package/packages/ui/dist/assets/{compass-DNbNVsgN.js → compass-BMTO0ayt.js} +1 -1
- package/packages/ui/dist/assets/{copy-r_J027hY.js → copy-PaXeRHza.js} +1 -1
- package/packages/ui/dist/assets/{core-CuWvvvu4.js → core-BFnStQd-.js} +3 -3
- package/packages/ui/dist/assets/cursor-BDvw-B17.js +3 -0
- package/packages/ui/dist/assets/{cursor-transparent-By6KxbOE.js → cursor-transparent-BEMdi-8q.js} +1 -1
- package/packages/ui/dist/assets/{desktop-DRMmsjrd.js → desktop-CfuLLThw.js} +1 -1
- package/packages/ui/dist/assets/{disconnect-C69Z8KUW.js → disconnect-DhwgJMiR.js} +1 -1
- package/packages/ui/dist/assets/{discord-p3AKvqDk.js → discord-po8qoN1s.js} +1 -1
- package/packages/ui/dist/assets/{etherscan-C2zTiWaN.js → etherscan-BEsz0_yx.js} +1 -1
- package/packages/ui/dist/assets/{events-DKTfpIHs.js → events-Bz33Unzu.js} +1 -1
- package/packages/ui/dist/assets/{exclamation-triangle-D4IJznwI.js → exclamation-triangle-7CjTAGOQ.js} +1 -1
- package/packages/ui/dist/assets/{extension-C0y2g1tg.js → extension-CmxjEWEt.js} +1 -1
- package/packages/ui/dist/assets/{external-link-fkbBBTcW.js → external-link-CmQ--bNS.js} +1 -1
- package/packages/ui/dist/assets/{facebook-nsIgKROR.js → facebook-CIBn9b65.js} +1 -1
- package/packages/ui/dist/assets/{fallback-DHv3hSPW.js → fallback-DATyrQlb.js} +1 -1
- package/packages/ui/dist/assets/{farcaster-CzBHn8fo.js → farcaster-OJ3Jasxg.js} +1 -1
- package/packages/ui/dist/assets/{filters-gW1TGI8D.js → filters-D4x09zeL.js} +1 -1
- package/packages/ui/dist/assets/{github-D9UuzE25.js → github-ZlIuMArp.js} +1 -1
- package/packages/ui/dist/assets/{google-DxUfChw6.js → google-Gwg85sfv.js} +1 -1
- package/packages/ui/dist/assets/{help-circle-2dNDsXrX.js → help-circle-D1uOWYcX.js} +1 -1
- package/packages/ui/dist/assets/{id-rNBDU8mz.js → id-C0-5UdYk.js} +1 -1
- package/packages/ui/dist/assets/{image-C9Peu4QW.js → image-D_DUsv8-.js} +1 -1
- package/packages/ui/dist/assets/{index-B1wosqUU.js → index-BCzex_R6.js} +1 -1
- package/packages/ui/dist/assets/index-BUhrHLpY.js +1775 -0
- package/packages/ui/dist/assets/index-Cq02kQmy.css +1 -0
- package/packages/ui/dist/assets/{index-B1aFIpJ0.js → index-CrYzBWfD.js} +1 -1
- package/packages/ui/dist/assets/{index-_F9WbMAT.js → index-DdbJhIdl.js} +3 -3
- package/packages/ui/dist/assets/{index-JwrWbcaz.js → index-DiojfeVM.js} +1 -1
- package/packages/ui/dist/assets/{index-4lrTXbkY.js → index-izd7vu_r.js} +1 -1
- package/packages/ui/dist/assets/{index.es-wlYgJouQ.js → index.es-DdkHhQAj.js} +4 -4
- package/packages/ui/dist/assets/{info-cGbqKpFv.js → info-CiRd_kEG.js} +1 -1
- package/packages/ui/dist/assets/{info-circle-B8Xfr9A0.js → info-circle-ypxjqarK.js} +1 -1
- package/packages/ui/dist/assets/{lightbulb-CM2m-PnZ.js → lightbulb-B-pxLxd8.js} +1 -1
- package/packages/ui/dist/assets/{mail-_qO7Zcxu.js → mail-BYmicuVZ.js} +1 -1
- package/packages/ui/dist/assets/{metamask-sdk-Dy961bnw.js → metamask-sdk-Ccl6DG7Q.js} +1 -1
- package/packages/ui/dist/assets/{mobile-C6TDJh2K.js → mobile-CtP5PqVT.js} +1 -1
- package/packages/ui/dist/assets/{more-3pPTR0Gx.js → more-6C2733we.js} +1 -1
- package/packages/ui/dist/assets/{network-placeholder-BtFT2yZA.js → network-placeholder-CdhxMzqd.js} +1 -1
- package/packages/ui/dist/assets/{nftPlaceholder-BfBZEH1N.js → nftPlaceholder-DVmTWEAY.js} +1 -1
- package/packages/ui/dist/assets/{off-Bg5cnmyC.js → off-DNYLughs.js} +1 -1
- package/packages/ui/dist/assets/{parseSignature-CSIsnC1G.js → parseSignature-Dq2B5Bu3.js} +1 -1
- package/packages/ui/dist/assets/{play-store-Dg32m5PL.js → play-store-D7Qut5ta.js} +1 -1
- package/packages/ui/dist/assets/{plus-Ce97GbOa.js → plus-kqMyjt3q.js} +1 -1
- package/packages/ui/dist/assets/{qr-code-D3KdZWUh.js → qr-code-DiUCWRbz.js} +1 -1
- package/packages/ui/dist/assets/{recycle-horizontal-DOKfyzVh.js → recycle-horizontal-Boe3XiS-.js} +1 -1
- package/packages/ui/dist/assets/{refresh-DSjW7q17.js → refresh-CrBgBQYO.js} +1 -1
- package/packages/ui/dist/assets/{reown-logo-B0n-8waR.js → reown-logo-CFZCCHSx.js} +1 -1
- package/packages/ui/dist/assets/{search-CL2iyGid.js → search-ChTDrghU.js} +1 -1
- package/packages/ui/dist/assets/{secp256k1-DdqDRGog.js → secp256k1-DAV5Q_FR.js} +1 -1
- package/packages/ui/dist/assets/{send-C_Rm4fzj.js → send-DLFbBFe1.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontal-0d_94RdY.js → swapHorizontal-BEs3emfG.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalBold-BukSRa8V.js → swapHorizontalBold-CC-Hfa7W.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalMedium-DvroDkEf.js → swapHorizontalMedium-BmR0H8DC.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalRoundedBold-BAehcn9y.js → swapHorizontalRoundedBold-BdP5NGIH.js} +1 -1
- package/packages/ui/dist/assets/{swapVertical-kblIte_7.js → swapVertical-CPrGEJPY.js} +1 -1
- package/packages/ui/dist/assets/{telegram-DHLO89MI.js → telegram-CxNoZ80Q.js} +1 -1
- package/packages/ui/dist/assets/{three-dots-ctb5FHLw.js → three-dots-BRa6SBpL.js} +1 -1
- package/packages/ui/dist/assets/{twitch-CK_fCqNu.js → twitch-BC338bG5.js} +1 -1
- package/packages/ui/dist/assets/{twitterIcon-BCngN3WD.js → twitterIcon-BGZmt2i9.js} +1 -1
- package/packages/ui/dist/assets/{verify-Dy-B59vy.js → verify-CEstW0zw.js} +1 -1
- package/packages/ui/dist/assets/{verify-filled-DHDHx8Lk.js → verify-filled-OkZb0weU.js} +1 -1
- package/packages/ui/dist/assets/{w3m-modal-DRNXP3Ww.js → w3m-modal-pS09ECwE.js} +1 -1
- package/packages/ui/dist/assets/{wallet-DriPOF7d.js → wallet-BXVKCgC9.js} +1 -1
- package/packages/ui/dist/assets/{wallet-placeholder-B4ukOjpR.js → wallet-placeholder-C_kNhB1c.js} +1 -1
- package/packages/ui/dist/assets/{walletconnect-Cjl1Ki75.js → walletconnect-CRKIuUHH.js} +1 -1
- package/packages/ui/dist/assets/{warning-circle-C7eCTFhJ.js → warning-circle-DB2NnwlJ.js} +1 -1
- package/packages/ui/dist/assets/{x-B8jYZY9t.js → x-DT4RmwL5.js} +1 -1
- package/packages/ui/dist/index.html +14 -14
- package/scripts/check-docs.mjs +262 -262
- package/scripts/check-init.mjs +108 -108
- package/scripts/postinstall.js +81 -56
- package/templates/custom-mandate/.sail/contracts/interfaces/IPermission.sol +18 -18
- package/templates/custom-mandate/README.md +116 -116
- package/templates/custom-mandate/foundry.toml +8 -8
- package/templates/custom-mandate/mandates/BoundedCallPermission.sol +41 -41
- package/templates/custom-mandate/mandates/README.md +16 -16
- package/templates/custom-mandate/mandates/SailCalldata.sol +118 -118
- package/templates/default/.cursor/rules +25 -25
- package/templates/default/.env.example +20 -20
- package/templates/default/.github/workflows/agent-tick.yml +33 -33
- package/templates/default/.sail/README.md +13 -13
- package/templates/default/.sail/config.json +10 -10
- package/templates/default/AGENTS.md +171 -171
- package/templates/default/CLAUDE.md +2 -2
- package/templates/default/README.md +16 -16
- package/templates/default/_gitignore +13 -13
- package/templates/default/docs/PERMISSION_MODEL.md +93 -93
- package/templates/default/examples/dca/README.md +16 -16
- package/templates/default/examples/dca/agent.ts +174 -174
- package/templates/default/examples/dca/mandate.ts +45 -45
- package/templates/default/package.json +17 -17
- package/templates/default/src/agent.ts +37 -37
- package/templates/default/src/config.ts +24 -24
- package/templates/default/src/mandate.ts +22 -22
- package/templates/default/tsconfig.json +17 -17
- package/templates/default/ui/README.md +3 -3
- package/templates/lifi-permissions/LifiBoundedApprovePermissionCloneable.sol +84 -84
- package/templates/lifi-permissions/LifiDiamondSwapPermissionCloneable.sol +97 -97
- package/templates/lifi-permissions/README.md +53 -53
- package/packages/ui/dist/assets/cursor-0ZcCqvYy.js +0 -3
- package/packages/ui/dist/assets/index-BzT0MJhc.js +0 -1775
- package/packages/ui/dist/assets/index-n8bp1ZEc.css +0 -1
|
@@ -1,174 +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
|
+
// 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,45 +1,45 @@
|
|
|
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";
|
|
5
|
-
|
|
6
|
-
// ── Token addresses (Base mainnet) ────────────────────────────────────────────
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Tokens in the DCA basket.
|
|
10
|
-
* ALLOWED_TOKENS[0] = USDC (input — what the agent spends)
|
|
11
|
-
* ALLOWED_TOKENS[1] = WETH (output — what the agent accumulates)
|
|
12
|
-
*/
|
|
13
|
-
export const ALLOWED_TOKENS: Address[] = [
|
|
14
|
-
"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC on Base (6 decimals)
|
|
15
|
-
"0x4200000000000000000000000000000000000006", // WETH on Base (18 decimals)
|
|
16
|
-
];
|
|
17
|
-
|
|
18
|
-
// ── Swap parameters ───────────────────────────────────────────────────────────
|
|
19
|
-
|
|
20
|
-
/** Amount of USDC to spend per swap (in USDC base units, 6 decimals). Default: 5 USDC. */
|
|
21
|
-
export const SWAP_AMOUNT_USDC = 5_000_000n; // 5 USDC
|
|
22
|
-
|
|
23
|
-
/** Minimum USDC balance the SMA must hold before a swap is attempted. */
|
|
24
|
-
export const MIN_USDC_TO_SWAP = 6_000_000n; // 6 USDC
|
|
25
|
-
|
|
26
|
-
/** Slippage tolerance in basis points (100 = 1%). */
|
|
27
|
-
export const SLIPPAGE_BPS = 100; // 1%
|
|
28
|
-
|
|
29
|
-
/** Uniswap V3 pool fee tier for the USDC/WETH pool on Base (500 = 0.05%). */
|
|
30
|
-
export const SWAP_FEE_TIER = 500;
|
|
31
|
-
|
|
32
|
-
/** Rebalance when allocation drift exceeds this fraction (0.05 = 5%). */
|
|
33
|
-
export const REBALANCE_THRESHOLD = 0.05;
|
|
34
|
-
|
|
35
|
-
// ── Contract addresses (Base mainnet) ─────────────────────────────────────────
|
|
36
|
-
|
|
37
|
-
/** Uniswap SwapRouter02 on Base. Target for exactInputSingle swaps. */
|
|
38
|
-
export const SWAP_ROUTER: Address = "0x2626664c2603336E57B271c5C0b26F421741e481";
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Uniswap V3 QuoterV2 on Base.
|
|
42
|
-
* Called off-chain (via eth_call) to obtain the expected output amount
|
|
43
|
-
* before computing amountOutMinimum.
|
|
44
|
-
*/
|
|
45
|
-
export const QUOTER_V2: Address = "0x3d4e44Eb1374240CE5F1B871ab261CD16335B76a";
|
|
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";
|
|
5
|
+
|
|
6
|
+
// ── Token addresses (Base mainnet) ────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Tokens in the DCA basket.
|
|
10
|
+
* ALLOWED_TOKENS[0] = USDC (input — what the agent spends)
|
|
11
|
+
* ALLOWED_TOKENS[1] = WETH (output — what the agent accumulates)
|
|
12
|
+
*/
|
|
13
|
+
export const ALLOWED_TOKENS: Address[] = [
|
|
14
|
+
"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC on Base (6 decimals)
|
|
15
|
+
"0x4200000000000000000000000000000000000006", // WETH on Base (18 decimals)
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
// ── Swap parameters ───────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
/** Amount of USDC to spend per swap (in USDC base units, 6 decimals). Default: 5 USDC. */
|
|
21
|
+
export const SWAP_AMOUNT_USDC = 5_000_000n; // 5 USDC
|
|
22
|
+
|
|
23
|
+
/** Minimum USDC balance the SMA must hold before a swap is attempted. */
|
|
24
|
+
export const MIN_USDC_TO_SWAP = 6_000_000n; // 6 USDC
|
|
25
|
+
|
|
26
|
+
/** Slippage tolerance in basis points (100 = 1%). */
|
|
27
|
+
export const SLIPPAGE_BPS = 100; // 1%
|
|
28
|
+
|
|
29
|
+
/** Uniswap V3 pool fee tier for the USDC/WETH pool on Base (500 = 0.05%). */
|
|
30
|
+
export const SWAP_FEE_TIER = 500;
|
|
31
|
+
|
|
32
|
+
/** Rebalance when allocation drift exceeds this fraction (0.05 = 5%). */
|
|
33
|
+
export const REBALANCE_THRESHOLD = 0.05;
|
|
34
|
+
|
|
35
|
+
// ── Contract addresses (Base mainnet) ─────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
/** Uniswap SwapRouter02 on Base. Target for exactInputSingle swaps. */
|
|
38
|
+
export const SWAP_ROUTER: Address = "0x2626664c2603336E57B271c5C0b26F421741e481";
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Uniswap V3 QuoterV2 on Base.
|
|
42
|
+
* Called off-chain (via eth_call) to obtain the expected output amount
|
|
43
|
+
* before computing amountOutMinimum.
|
|
44
|
+
*/
|
|
45
|
+
export const QUOTER_V2: Address = "0x3d4e44Eb1374240CE5F1B871ab261CD16335B76a";
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "sail-agent",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"description": "Sail Protocol agent starter",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
8
|
-
},
|
|
9
|
-
"dependencies": {
|
|
10
|
-
"@sail/sdk": "workspace:*",
|
|
11
|
-
"viem": "^2.21.0"
|
|
12
|
-
},
|
|
13
|
-
"devDependencies": {
|
|
14
|
-
"@types/node": "^22.0.0",
|
|
15
|
-
"typescript": "^5.5.0"
|
|
16
|
-
}
|
|
17
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "sail-agent",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Sail Protocol agent starter",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@sail/sdk": "workspace:*",
|
|
11
|
+
"viem": "^2.21.0"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@types/node": "^22.0.0",
|
|
15
|
+
"typescript": "^5.5.0"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -1,37 +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
|
-
};
|
|
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
|
+
};
|
|
@@ -1,24 +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
|
-
}
|
|
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
|
+
}
|
|
@@ -1,22 +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
|
|
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
|