@dev.sail.money/sailor 0.1.0-local → 1.0.0-39
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 +139 -140
- package/LICENSE +21 -21
- package/README.md +428 -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 +44 -45
- package/packages/cli/README.md +34 -34
- package/packages/cli/dist/index.cjs +734 -705
- package/packages/cli/dist/server.cjs +627 -538
- 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-BxpXfVWe.js → add-Gzf62xlX.js} +1 -1
- package/packages/ui/dist/assets/{all-wallets-BKTn_sWK.js → all-wallets-O-pI4o8v.js} +1 -1
- package/packages/ui/dist/assets/{app-store-CfuKbwxR.js → app-store-CeSLaOaQ.js} +1 -1
- package/packages/ui/dist/assets/{apple-BKSBbNYg.js → apple-FGNyQM-D.js} +1 -1
- package/packages/ui/dist/assets/{arrow-bottom-D4bG6gZi.js → arrow-bottom-C1fusORF.js} +1 -1
- package/packages/ui/dist/assets/{arrow-bottom-circle-BNTs1p0T.js → arrow-bottom-circle-AvK1VEpN.js} +1 -1
- package/packages/ui/dist/assets/{arrow-left-2uee3vYv.js → arrow-left-Bu-hq4Ep.js} +1 -1
- package/packages/ui/dist/assets/{arrow-right-BktjMV6h.js → arrow-right-XbZESmct.js} +1 -1
- package/packages/ui/dist/assets/{arrow-top-Izu28fX4.js → arrow-top-DvkVHbhX.js} +1 -1
- package/packages/ui/dist/assets/{bank-USBaAyFM.js → bank-DTThWRvC.js} +1 -1
- package/packages/ui/dist/assets/{basic-C_9KjTEH.js → basic-B9AMgqFE.js} +1 -1
- package/packages/ui/dist/assets/{browser-DAEMAKV7.js → browser-Bhnivm4i.js} +1 -1
- package/packages/ui/dist/assets/{card-DT8yDkKN.js → card-DjIlyU55.js} +1 -1
- package/packages/ui/dist/assets/{ccip-CkqfGSxX.js → ccip-DPAiKntc.js} +1 -1
- package/packages/ui/dist/assets/{checkmark-CsgdEXFj.js → checkmark-DSVYfoVl.js} +1 -1
- package/packages/ui/dist/assets/{checkmark-bold-D2gjOQo2.js → checkmark-bold-BFkw_Q5g.js} +1 -1
- package/packages/ui/dist/assets/{chevron-bottom-tprFynYV.js → chevron-bottom-CyCgyOwY.js} +1 -1
- package/packages/ui/dist/assets/{chevron-left-D2Zj1gNB.js → chevron-left-DTuO2WLr.js} +1 -1
- package/packages/ui/dist/assets/{chevron-right-D1rRuAVe.js → chevron-right-DwB5FZj8.js} +1 -1
- package/packages/ui/dist/assets/{chevron-top-24dL1mbL.js → chevron-top-DKukdWvg.js} +1 -1
- package/packages/ui/dist/assets/{chrome-store-Vy-5niYX.js → chrome-store-Csz4L9Ls.js} +1 -1
- package/packages/ui/dist/assets/{clock-qBjLnVdJ.js → clock-Bg6488Gw.js} +1 -1
- package/packages/ui/dist/assets/{close-DARDwgcu.js → close-BxAJGBxP.js} +1 -1
- package/packages/ui/dist/assets/{coinPlaceholder-BvpIbPlD.js → coinPlaceholder-CCJVgW9w.js} +1 -1
- package/packages/ui/dist/assets/{compass-BMTO0ayt.js → compass-CSQSZaqJ.js} +1 -1
- package/packages/ui/dist/assets/{copy-PaXeRHza.js → copy-CqlzXVB-.js} +1 -1
- package/packages/ui/dist/assets/{core-BFnStQd-.js → core-ClvdTrpG.js} +3 -3
- package/packages/ui/dist/assets/cursor-CKKwWhGQ.js +3 -0
- package/packages/ui/dist/assets/{cursor-transparent-BEMdi-8q.js → cursor-transparent-C1VOGz11.js} +1 -1
- package/packages/ui/dist/assets/{desktop-CfuLLThw.js → desktop-QiLednKV.js} +1 -1
- package/packages/ui/dist/assets/{disconnect-DhwgJMiR.js → disconnect-Bx2TgkML.js} +1 -1
- package/packages/ui/dist/assets/{discord-po8qoN1s.js → discord-6MWX5Rbb.js} +1 -1
- package/packages/ui/dist/assets/{etherscan-BEsz0_yx.js → etherscan-CodIrmJK.js} +1 -1
- package/packages/ui/dist/assets/{events-Bz33Unzu.js → events-DOEm-LTy.js} +1 -1
- package/packages/ui/dist/assets/{exclamation-triangle-7CjTAGOQ.js → exclamation-triangle-Dwr5oCsh.js} +1 -1
- package/packages/ui/dist/assets/{extension-CmxjEWEt.js → extension-C-SoZx1s.js} +1 -1
- package/packages/ui/dist/assets/{external-link-CmQ--bNS.js → external-link-BiDYH90C.js} +1 -1
- package/packages/ui/dist/assets/{facebook-CIBn9b65.js → facebook-Bm27AlfS.js} +1 -1
- package/packages/ui/dist/assets/{fallback-DATyrQlb.js → fallback-Bwpmpy13.js} +1 -1
- package/packages/ui/dist/assets/{farcaster-OJ3Jasxg.js → farcaster-CSW-SjzS.js} +1 -1
- package/packages/ui/dist/assets/{filters-D4x09zeL.js → filters-j3dR7AJK.js} +1 -1
- package/packages/ui/dist/assets/{github-ZlIuMArp.js → github-CQMTSSgW.js} +1 -1
- package/packages/ui/dist/assets/{google-Gwg85sfv.js → google-BBIVBfAd.js} +1 -1
- package/packages/ui/dist/assets/{help-circle-D1uOWYcX.js → help-circle-CEvTLelF.js} +1 -1
- package/packages/ui/dist/assets/{id-C0-5UdYk.js → id-CYRVgSgN.js} +1 -1
- package/packages/ui/dist/assets/{image-D_DUsv8-.js → image-Cm9Ep5G0.js} +1 -1
- package/packages/ui/dist/assets/{index-DdbJhIdl.js → index-4wdo7Ga_.js} +3 -3
- package/packages/ui/dist/assets/{index-CrYzBWfD.js → index-BrP8m1ZI.js} +1 -1
- package/packages/ui/dist/assets/index-C2PQCECq.css +1 -0
- package/packages/ui/dist/assets/{index-DiojfeVM.js → index-DZ07nuwB.js} +1 -1
- package/packages/ui/dist/assets/{index-BCzex_R6.js → index-De_P6mNS.js} +1 -1
- package/packages/ui/dist/assets/index-DrQ9A8dp.js +1775 -0
- package/packages/ui/dist/assets/{index-izd7vu_r.js → index-Z55BVE94.js} +1 -1
- package/packages/ui/dist/assets/{index.es-DdkHhQAj.js → index.es-DnT9Uzwt.js} +4 -4
- package/packages/ui/dist/assets/{info-CiRd_kEG.js → info-DdYqiFMu.js} +1 -1
- package/packages/ui/dist/assets/{info-circle-ypxjqarK.js → info-circle-DkD9oY-S.js} +1 -1
- package/packages/ui/dist/assets/{lightbulb-B-pxLxd8.js → lightbulb-7Q3AhpSP.js} +1 -1
- package/packages/ui/dist/assets/{mail-BYmicuVZ.js → mail-CPagdnfp.js} +1 -1
- package/packages/ui/dist/assets/{metamask-sdk-Ccl6DG7Q.js → metamask-sdk-CQ4tzR6A.js} +1 -1
- package/packages/ui/dist/assets/{mobile-CtP5PqVT.js → mobile-01GVSpey.js} +1 -1
- package/packages/ui/dist/assets/{more-6C2733we.js → more-BPXPYrvy.js} +1 -1
- package/packages/ui/dist/assets/{network-placeholder-CdhxMzqd.js → network-placeholder-CII8WrTF.js} +1 -1
- package/packages/ui/dist/assets/{nftPlaceholder-DVmTWEAY.js → nftPlaceholder-DG5rjRzx.js} +1 -1
- package/packages/ui/dist/assets/{off-DNYLughs.js → off-eemo7R2q.js} +1 -1
- package/packages/ui/dist/assets/{parseSignature-Dq2B5Bu3.js → parseSignature-CaRSntRi.js} +1 -1
- package/packages/ui/dist/assets/{play-store-D7Qut5ta.js → play-store-SIqMwLur.js} +1 -1
- package/packages/ui/dist/assets/{plus-kqMyjt3q.js → plus-DemTM2Nx.js} +1 -1
- package/packages/ui/dist/assets/{qr-code-DiUCWRbz.js → qr-code-BjolKhQv.js} +1 -1
- package/packages/ui/dist/assets/{recycle-horizontal-Boe3XiS-.js → recycle-horizontal-BluxSqWj.js} +1 -1
- package/packages/ui/dist/assets/{refresh-CrBgBQYO.js → refresh-Cto5auO0.js} +1 -1
- package/packages/ui/dist/assets/{reown-logo-CFZCCHSx.js → reown-logo-lrWmaeNj.js} +1 -1
- package/packages/ui/dist/assets/{search-ChTDrghU.js → search-SNmrxLL7.js} +1 -1
- package/packages/ui/dist/assets/{secp256k1-DAV5Q_FR.js → secp256k1-SI0Bxirn.js} +1 -1
- package/packages/ui/dist/assets/{send-DLFbBFe1.js → send-Do8kdKTu.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontal-BEs3emfG.js → swapHorizontal-HBP1koQV.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalBold-CC-Hfa7W.js → swapHorizontalBold-B9g1LqXn.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalMedium-BmR0H8DC.js → swapHorizontalMedium-C8JebI_2.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalRoundedBold-BdP5NGIH.js → swapHorizontalRoundedBold-37eEYoAp.js} +1 -1
- package/packages/ui/dist/assets/{swapVertical-CPrGEJPY.js → swapVertical-DmacpIGs.js} +1 -1
- package/packages/ui/dist/assets/{telegram-CxNoZ80Q.js → telegram-Dq_CUch4.js} +1 -1
- package/packages/ui/dist/assets/{three-dots-BRa6SBpL.js → three-dots-DbkqGUCU.js} +1 -1
- package/packages/ui/dist/assets/{twitch-BC338bG5.js → twitch-iG0Ncwwy.js} +1 -1
- package/packages/ui/dist/assets/{twitterIcon-BGZmt2i9.js → twitterIcon-CNbKQx87.js} +1 -1
- package/packages/ui/dist/assets/{verify-CEstW0zw.js → verify-BfXT7L3L.js} +1 -1
- package/packages/ui/dist/assets/{verify-filled-OkZb0weU.js → verify-filled-BxqxA6xc.js} +1 -1
- package/packages/ui/dist/assets/{w3m-modal-pS09ECwE.js → w3m-modal-DeDYqwYJ.js} +1 -1
- package/packages/ui/dist/assets/{wallet-BXVKCgC9.js → wallet-BXsUR9Tj.js} +1 -1
- package/packages/ui/dist/assets/{wallet-placeholder-C_kNhB1c.js → wallet-placeholder-bbWbfkZu.js} +1 -1
- package/packages/ui/dist/assets/{walletconnect-CRKIuUHH.js → walletconnect-WsTWE17z.js} +1 -1
- package/packages/ui/dist/assets/{warning-circle-DB2NnwlJ.js → warning-circle-cNSUigh6.js} +1 -1
- package/packages/ui/dist/assets/{x-DT4RmwL5.js → x-gNtNGE0N.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/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-BDvw-B17.js +0 -3
- package/packages/ui/dist/assets/index-BUhrHLpY.js +0 -1775
- package/packages/ui/dist/assets/index-Cq02kQmy.css +0 -1
- package/scripts/postinstall.js +0 -81
|
@@ -1,118 +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
|
+
// 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`
|
|
@@ -1,20 +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
|
+
# 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,33 +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: 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
|
+
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": "sail-agent",
|
|
4
|
-
"chainId": null,
|
|
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
|
+
}
|