@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.
- package/AGENTS.md +43 -15
- package/README.md +217 -126
- package/examples/permissions/BoundedApproveAndCallBatch.sol +179 -0
- package/examples/permissions/BoundedBet_Limitless_Base.sol +8 -7
- package/examples/permissions/BoundedBorrow_AaveV3_Arbitrum.sol +13 -13
- package/examples/permissions/BoundedPerp_GMXv2_Arbitrum.sol +50 -39
- package/examples/permissions/BoundedStake_Venice_Base.sol +85 -0
- package/examples/permissions/BoundedSupply_AaveV3_Arbitrum.sol +82 -0
- package/examples/permissions/BoundedSwap_UniswapV3_Base.sol +15 -12
- package/examples/permissions/BoundedSwap_UniswapV4_Unichain.sol +14 -8
- package/examples/permissions/BoundedTransfer_ERC20_Ethereum.sol +6 -6
- package/examples/permissions/BoundedVault_ERC4626_Base.sol +97 -0
- package/examples/permissions/README.md +29 -2
- package/examples/permissions/SailCalldata.sol +118 -0
- package/examples/permissions/interfaces/IBatchPermission.sol +38 -0
- package/package.json +17 -12
- package/packages/cli/dist/index.cjs +4164 -2508
- package/packages/cli/dist/server.cjs +897 -1566
- package/packages/sdk/dist/chains.d.ts +12 -0
- package/packages/sdk/dist/chains.d.ts.map +1 -0
- package/packages/sdk/dist/chains.js +94 -0
- package/packages/sdk/dist/chains.js.map +1 -0
- package/packages/sdk/dist/deployments.d.ts +14 -7
- package/packages/sdk/dist/deployments.d.ts.map +1 -1
- package/packages/sdk/dist/deployments.js +132 -141
- package/packages/sdk/dist/deployments.js.map +1 -1
- package/packages/sdk/dist/index.d.ts +3 -2
- package/packages/sdk/dist/index.d.ts.map +1 -1
- package/packages/sdk/dist/index.js +3 -2
- package/packages/sdk/dist/index.js.map +1 -1
- package/packages/sdk/dist/intelligence.d.ts +1 -1
- package/packages/sdk/dist/intelligence.js +1 -1
- package/packages/sdk/dist/lifi.d.ts +17 -0
- package/packages/sdk/dist/lifi.d.ts.map +1 -1
- package/packages/sdk/dist/lifi.js +24 -0
- package/packages/sdk/dist/lifi.js.map +1 -1
- package/packages/sdk/dist/safe.d.ts +83 -0
- package/packages/sdk/dist/safe.d.ts.map +1 -1
- package/packages/sdk/dist/safe.js +92 -1
- package/packages/sdk/dist/safe.js.map +1 -1
- package/packages/sdk/dist/templates/ammLiquidity.d.ts +24 -11
- package/packages/sdk/dist/templates/ammLiquidity.d.ts.map +1 -1
- package/packages/sdk/dist/templates/ammLiquidity.js +39 -31
- package/packages/sdk/dist/templates/ammLiquidity.js.map +1 -1
- package/packages/sdk/dist/templates/approveAndCallBatch.d.ts +24 -10
- package/packages/sdk/dist/templates/approveAndCallBatch.d.ts.map +1 -1
- package/packages/sdk/dist/templates/approveAndCallBatch.js +36 -23
- package/packages/sdk/dist/templates/approveAndCallBatch.js.map +1 -1
- package/packages/sdk/dist/templates/boundedBorrow.d.ts +19 -9
- package/packages/sdk/dist/templates/boundedBorrow.d.ts.map +1 -1
- package/packages/sdk/dist/templates/boundedBorrow.js +28 -19
- package/packages/sdk/dist/templates/boundedBorrow.js.map +1 -1
- package/packages/sdk/dist/templates/boundedSwap.d.ts +19 -9
- package/packages/sdk/dist/templates/boundedSwap.d.ts.map +1 -1
- package/packages/sdk/dist/templates/boundedSwap.js +30 -20
- package/packages/sdk/dist/templates/boundedSwap.js.map +1 -1
- package/packages/sdk/dist/templates/defiBundle.d.ts +35 -9
- package/packages/sdk/dist/templates/defiBundle.d.ts.map +1 -1
- package/packages/sdk/dist/templates/defiBundle.js +84 -22
- package/packages/sdk/dist/templates/defiBundle.js.map +1 -1
- package/packages/sdk/dist/templates/pendle.d.ts +23 -8
- package/packages/sdk/dist/templates/pendle.d.ts.map +1 -1
- package/packages/sdk/dist/templates/pendle.js +34 -14
- package/packages/sdk/dist/templates/pendle.js.map +1 -1
- package/packages/sdk/dist/templates/transferTarget.d.ts +11 -3
- package/packages/sdk/dist/templates/transferTarget.d.ts.map +1 -1
- package/packages/sdk/dist/templates/transferTarget.js +14 -7
- package/packages/sdk/dist/templates/transferTarget.js.map +1 -1
- package/packages/sdk/dist/types.d.ts +19 -1
- package/packages/sdk/dist/types.d.ts.map +1 -1
- package/packages/sdk/package.json +28 -0
- package/packages/ui/dist/assets/{add-DaJhwIBV.js → add-Dl1etsL9.js} +1 -1
- package/packages/ui/dist/assets/{all-wallets-BUxsqWXi.js → all-wallets-C0eHLOGG.js} +1 -1
- package/packages/ui/dist/assets/{app-store-DkltwTqE.js → app-store-B-VMDEZ3.js} +1 -1
- package/packages/ui/dist/assets/{apple-owVOeaIT.js → apple-DkDXzKns.js} +1 -1
- package/packages/ui/dist/assets/{arrow-bottom-D2mmNJve.js → arrow-bottom-DtPzuS76.js} +1 -1
- package/packages/ui/dist/assets/{arrow-bottom-circle-CbNYijx-.js → arrow-bottom-circle-D7odSAO8.js} +1 -1
- package/packages/ui/dist/assets/{arrow-left-DJB61s4C.js → arrow-left-zJV9tpx0.js} +1 -1
- package/packages/ui/dist/assets/{arrow-right-BBrsQ9R4.js → arrow-right-BOREfe7o.js} +1 -1
- package/packages/ui/dist/assets/{arrow-top-Cil6bOc8.js → arrow-top-CipQc3Af.js} +1 -1
- package/packages/ui/dist/assets/{bank-CbwEmRo3.js → bank-C5s7eoV5.js} +1 -1
- package/packages/ui/dist/assets/{basic-CLNfjw3m.js → basic-D2es4Vq8.js} +1 -1
- package/packages/ui/dist/assets/{browser-B5TtF4Pb.js → browser-DITQWDC9.js} +1 -1
- package/packages/ui/dist/assets/{card-CO7BVB-C.js → card-C3DDkaYK.js} +1 -1
- package/packages/ui/dist/assets/{ccip-2W7K3_J3.js → ccip-UBXL3JiN.js} +1 -1
- package/packages/ui/dist/assets/{checkmark-bold-D9xGHzPE.js → checkmark-bold-D8yW0_K_.js} +1 -1
- package/packages/ui/dist/assets/{checkmark-BEtSHq9m.js → checkmark-ngef3MAl.js} +1 -1
- package/packages/ui/dist/assets/{chevron-bottom-BDztht6i.js → chevron-bottom-C56BipDR.js} +1 -1
- package/packages/ui/dist/assets/{chevron-left-EV4GFNbc.js → chevron-left-BmIPtPl_.js} +1 -1
- package/packages/ui/dist/assets/{chevron-right-B4_bB9oR.js → chevron-right-BnySHQ8h.js} +1 -1
- package/packages/ui/dist/assets/{chevron-top-D54xPNzF.js → chevron-top-BDGZnNW3.js} +1 -1
- package/packages/ui/dist/assets/{chrome-store-DYUpAJJq.js → chrome-store-BYIqJZVF.js} +1 -1
- package/packages/ui/dist/assets/{clock-Ca1T1Soz.js → clock-Bl4mUHAM.js} +1 -1
- package/packages/ui/dist/assets/{close-BZqWjurK.js → close-B9rhEX6U.js} +1 -1
- package/packages/ui/dist/assets/{coinPlaceholder-e6fl2XDo.js → coinPlaceholder-1cO0FQsl.js} +1 -1
- package/packages/ui/dist/assets/{compass-DCLC7zIh.js → compass-7i-VuXu2.js} +1 -1
- package/packages/ui/dist/assets/{copy-Th2AaD-O.js → copy-OqqXix2J.js} +1 -1
- package/packages/ui/dist/assets/{core-Ckx_cyuH.js → core-tX9kIIDJ.js} +3 -3
- package/packages/ui/dist/assets/cursor-BoyeQ9fN.js +3 -0
- package/packages/ui/dist/assets/{cursor-transparent-BKHeABKB.js → cursor-transparent-5aoRH67u.js} +1 -1
- package/packages/ui/dist/assets/{desktop-CBjY8t6F.js → desktop-BaPXK9R6.js} +1 -1
- package/packages/ui/dist/assets/{disconnect-DbSs2cli.js → disconnect-LlK5K1CF.js} +1 -1
- package/packages/ui/dist/assets/{discord-ZlLOAUkM.js → discord-BdcQNWY_.js} +1 -1
- package/packages/ui/dist/assets/{etherscan-CKUrqWYN.js → etherscan-Bb-WxpO1.js} +1 -1
- package/packages/ui/dist/assets/{events-CiKP71cK.js → events-DjdZr6no.js} +1 -1
- package/packages/ui/dist/assets/{exclamation-triangle-DA1QzFiO.js → exclamation-triangle-COx4VtPV.js} +1 -1
- package/packages/ui/dist/assets/{extension-BVJkmvpJ.js → extension-DF63DTWO.js} +1 -1
- package/packages/ui/dist/assets/{external-link-D_bsR7B2.js → external-link-DyghCkQu.js} +1 -1
- package/packages/ui/dist/assets/{facebook-CmFmhojx.js → facebook-Dcg4bZMR.js} +1 -1
- package/packages/ui/dist/assets/{fallback-Ofl6uSnB.js → fallback-DJIr_fH3.js} +1 -1
- package/packages/ui/dist/assets/{farcaster-Co-M3Ss8.js → farcaster-BkmV5HjO.js} +1 -1
- package/packages/ui/dist/assets/{filters-B1WwNaFU.js → filters-DLHj1T_P.js} +1 -1
- package/packages/ui/dist/assets/{github-CP4fP6gn.js → github-BFDCgKrF.js} +1 -1
- package/packages/ui/dist/assets/{google-CsOIXJ6V.js → google-C08SpmIy.js} +1 -1
- package/packages/ui/dist/assets/{help-circle-DiMkomdF.js → help-circle-DU1IFmWp.js} +1 -1
- package/packages/ui/dist/assets/{id-lmscL5LX.js → id-DtDRGf3L.js} +1 -1
- package/packages/ui/dist/assets/{image-B-ubJrY5.js → image-CgCXJEjT.js} +1 -1
- package/packages/ui/dist/assets/{index-Dbh5V1Z0.js → index-8chM4S5Y.js} +1 -1
- package/packages/ui/dist/assets/{index-BaukYv-x.js → index-B5sCtNuq.js} +1 -1
- package/packages/ui/dist/assets/{index-CF0KMmke.js → index-BBfBEazf.js} +3 -3
- package/packages/ui/dist/assets/{index-CZR1Qjhs.js → index-BhXPwltt.js} +1 -1
- package/packages/ui/dist/assets/index-Cm05Py20.css +1 -0
- package/packages/ui/dist/assets/{index-DVgfCzCo.js → index-D37bD6Yt.js} +1 -1
- package/packages/ui/dist/assets/index-DZfBh-cg.js +1775 -0
- package/packages/ui/dist/assets/{index.es-C78cE5SI.js → index.es-BEcNQEn-.js} +4 -4
- package/packages/ui/dist/assets/{info-Cqg57EVo.js → info-ClsdYA4P.js} +1 -1
- package/packages/ui/dist/assets/{info-circle-DkeSWNKV.js → info-circle-DJmn4Bsv.js} +1 -1
- package/packages/ui/dist/assets/{lightbulb-DNlO4qKh.js → lightbulb-CXSftjXS.js} +1 -1
- package/packages/ui/dist/assets/{mail-kVQ8Jb9Y.js → mail-cdYKOl9P.js} +1 -1
- package/packages/ui/dist/assets/{metamask-sdk-CBalSvz7.js → metamask-sdk-Co3aIEln.js} +1 -1
- package/packages/ui/dist/assets/{mobile-BEteuhF7.js → mobile-DEHYlk8L.js} +1 -1
- package/packages/ui/dist/assets/{more-DBWmXQli.js → more-Cq_fo8pI.js} +1 -1
- package/packages/ui/dist/assets/{network-placeholder-Dg1uUHiL.js → network-placeholder-_dLCK4xB.js} +1 -1
- package/packages/ui/dist/assets/{nftPlaceholder-i3AHSiD9.js → nftPlaceholder-Dz4HKEr4.js} +1 -1
- package/packages/ui/dist/assets/{off-BtMm0fi2.js → off-B1k1lhkr.js} +1 -1
- package/packages/ui/dist/assets/{parseSignature-Cb5FlWWg.js → parseSignature-Cr0ptV2X.js} +1 -1
- package/packages/ui/dist/assets/{play-store-iKKkXa6a.js → play-store-lYqe4eeL.js} +1 -1
- package/packages/ui/dist/assets/{plus-CA5NaRtb.js → plus-QMgh1krr.js} +1 -1
- package/packages/ui/dist/assets/{qr-code-D2kiqR7h.js → qr-code-ClVHbZWN.js} +1 -1
- package/packages/ui/dist/assets/{recycle-horizontal-Dcme7R03.js → recycle-horizontal-CxGYnWid.js} +1 -1
- package/packages/ui/dist/assets/{refresh-Dega3sDp.js → refresh-CPysMza_.js} +1 -1
- package/packages/ui/dist/assets/{reown-logo-xNkksyWJ.js → reown-logo-OoL_zJd0.js} +1 -1
- package/packages/ui/dist/assets/{search-HYl7NO8x.js → search-2GaRbf1I.js} +1 -1
- package/packages/ui/dist/assets/{secp256k1-Cxd6_SiH.js → secp256k1-BrB8qSSy.js} +1 -1
- package/packages/ui/dist/assets/{send-CJU8CUAo.js → send-CC2UuIfD.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontal-IMUKiUre.js → swapHorizontal-BRqYwsqT.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalBold-CNYnNJ9-.js → swapHorizontalBold-Bj0GSRq9.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalMedium-B9VxEYsT.js → swapHorizontalMedium-oLOjpU2A.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalRoundedBold-Dz33l_Jh.js → swapHorizontalRoundedBold-TJ652QXb.js} +1 -1
- package/packages/ui/dist/assets/{swapVertical-CHUmjVJ0.js → swapVertical-e0NLyV3x.js} +1 -1
- package/packages/ui/dist/assets/{telegram-kl9S2mbU.js → telegram-WhJHVeoU.js} +1 -1
- package/packages/ui/dist/assets/{three-dots-U5lhA1Am.js → three-dots-BlBAOyW-.js} +1 -1
- package/packages/ui/dist/assets/{twitch-KTEUWXEp.js → twitch-BH7vWmPc.js} +1 -1
- package/packages/ui/dist/assets/{twitterIcon-BHiq8mRg.js → twitterIcon-As0Nkanp.js} +1 -1
- package/packages/ui/dist/assets/{verify-CfN-BXNd.js → verify-BEJ0QuLl.js} +1 -1
- package/packages/ui/dist/assets/{verify-filled-DwZccetj.js → verify-filled-B8Ww2N7z.js} +1 -1
- package/packages/ui/dist/assets/{w3m-modal-CS-PFqPE.js → w3m-modal-5rOSZgOR.js} +1 -1
- package/packages/ui/dist/assets/{wallet-DVlGkhOY.js → wallet-CAfC3aml.js} +1 -1
- package/packages/ui/dist/assets/{wallet-placeholder-CvR_iEWX.js → wallet-placeholder-4RZI464Z.js} +1 -1
- package/packages/ui/dist/assets/{walletconnect-8pZBDvVI.js → walletconnect-BiltKqAe.js} +1 -1
- package/packages/ui/dist/assets/{warning-circle-ylLEE0Yp.js → warning-circle-CI4jqpHo.js} +1 -1
- package/packages/ui/dist/assets/{x-C_TBsTMj.js → x-FBttjBWO.js} +1 -1
- package/packages/ui/dist/index.html +2 -2
- package/scripts/check-init.mjs +2 -3
- package/templates/custom-mandate/README.md +31 -0
- package/templates/custom-mandate/mandates/BoundedCallPermission.sol +8 -2
- package/templates/custom-mandate/mandates/SailCalldata.sol +118 -0
- package/templates/default/.env.example +20 -0
- package/templates/{dca-rebalancer → default}/.github/workflows/agent-tick.yml +8 -7
- package/templates/{dca-rebalancer → default}/.sail/config.json +2 -2
- package/templates/default/AGENTS.md +171 -0
- package/templates/default/README.md +16 -0
- package/templates/default/examples/dca/README.md +16 -0
- package/templates/default/examples/dca/agent.ts +174 -0
- package/templates/{dca-rebalancer/src → default/examples/dca}/mandate.ts +9 -31
- package/templates/{dca-rebalancer → default}/package.json +2 -2
- package/templates/default/src/agent.ts +37 -0
- package/templates/default/src/config.ts +24 -0
- package/templates/default/src/mandate.ts +22 -0
- package/templates/default/tsconfig.json +17 -0
- package/templates/lifi-permissions/README.md +1 -1
- package/packages/ui/dist/assets/cursor-DV7rOqbJ.js +0 -3
- package/packages/ui/dist/assets/index-CKxgNxS9.css +0 -1
- package/packages/ui/dist/assets/index-Q2Yai4Fe.js +0 -2103
- package/scripts/postinstall.js +0 -366
- package/templates/dca-rebalancer/.env.example +0 -6
- package/templates/dca-rebalancer/AGENTS.md +0 -246
- package/templates/dca-rebalancer/AGENT_PLAYBOOK.md +0 -110
- package/templates/dca-rebalancer/README.md +0 -16
- package/templates/dca-rebalancer/src/agent.ts +0 -253
- package/templates/dca-rebalancer/src/config.ts +0 -27
- package/templates/dca-rebalancer/tsconfig.json +0 -8
- /package/templates/{dca-rebalancer → default}/.cursor/rules +0 -0
- /package/templates/{dca-rebalancer → default}/.sail/.gitkeep +0 -0
- /package/templates/{dca-rebalancer → default}/.sail/README.md +0 -0
- /package/templates/{dca-rebalancer → default}/CLAUDE.md +0 -0
- /package/templates/{dca-rebalancer → default}/_gitignore +0 -0
- /package/templates/{dca-rebalancer → default}/docs/PERMISSION_MODEL.md +0 -0
- /package/templates/{dca-rebalancer → default}/ui/README.md +0 -0
|
@@ -10,8 +10,8 @@ pragma solidity 0.8.26;
|
|
|
10
10
|
// Target : Universal Router 0xef740bf23acae26f6492b10de645d6b98dc8eaf3
|
|
11
11
|
// (verify on Uniscan before use)
|
|
12
12
|
//
|
|
13
|
-
//
|
|
14
|
-
// execute(bytes,bytes[])
|
|
13
|
+
// ENFORCES ON-CHAIN (kernel calls evaluate() on every dispatch; false ⇒ dispatch blocked):
|
|
14
|
+
// execute(bytes,bytes[],uint256) selector 0x3593564c / execute(bytes,bytes[]) selector 0x24856bc3
|
|
15
15
|
// • target must be UNIVERSAL_ROUTER
|
|
16
16
|
// • first command byte (masking the allow-failure MSB) must be V4_SWAP (0x10)
|
|
17
17
|
// • exactly one command (single-swap path — disallow multi-hop command strings)
|
|
@@ -19,9 +19,13 @@ pragma solidity 0.8.26;
|
|
|
19
19
|
// • tokenIn (from poolKey, derived by zeroForOne) must be FIXED_CURRENCY_IN
|
|
20
20
|
// • tokenOut must be in ALLOWED_CURRENCIES_OUT
|
|
21
21
|
// • amountIn ≤ MAX_AMOUNT_IN
|
|
22
|
-
// • amountOutMinimum ≥ amountIn × MIN_BPS / 10 000
|
|
22
|
+
// • amountOutMinimum ≥ amountIn × MIN_BPS / 10 000 (slippage floor — see caveat below)
|
|
23
23
|
//
|
|
24
|
-
// NOT
|
|
24
|
+
// AGENT-ENFORCED / NOT BOUNDED HERE (off-chain — can change without redeploying this contract):
|
|
25
|
+
// • real (cross-denomination) slippage — see MIN_BPS caveat in evaluate()
|
|
26
|
+
// • swap frequency / cadence
|
|
27
|
+
//
|
|
28
|
+
// DOCUMENTED LIMITATIONS (on-chain, but intentionally not constrained):
|
|
25
29
|
// • hookData is not inspected (hooks can alter swap behavior on-chain; if the
|
|
26
30
|
// pool uses a hook that significantly changes execution, this permission cannot
|
|
27
31
|
// constrain it. Deploy against pools with address(0) hooks or audited hooks only.)
|
|
@@ -93,12 +97,10 @@ contract BoundedSwap_UniswapV4_Unichain is IPermission {
|
|
|
93
97
|
MAX_AMOUNT_IN = maxAmountIn;
|
|
94
98
|
MIN_BPS = minBps;
|
|
95
99
|
for (uint256 i = 0; i < allowedCurrenciesOut.length; i++) {
|
|
96
|
-
|
|
100
|
+
isAllowedCurrencyOut[allowedCurrenciesOut[i]] = true;
|
|
97
101
|
}
|
|
98
102
|
}
|
|
99
103
|
|
|
100
|
-
mapping(address => bool) private isAllowedCurrenciesOut;
|
|
101
|
-
|
|
102
104
|
function evaluate(bytes calldata txData, Context calldata ctx) external view returns (bool) {
|
|
103
105
|
if (ctx.target != UNIVERSAL_ROUTER) return false;
|
|
104
106
|
if (ctx.selector != SEL_EXECUTE_DEADLINE && ctx.selector != SEL_EXECUTE) return false;
|
|
@@ -133,8 +135,12 @@ contract BoundedSwap_UniswapV4_Unichain is IPermission {
|
|
|
133
135
|
address tokenOut = p.zeroForOne ? p.poolKey.currency1 : p.poolKey.currency0;
|
|
134
136
|
|
|
135
137
|
if (tokenIn != FIXED_CURRENCY_IN) return false;
|
|
136
|
-
if (!
|
|
138
|
+
if (!isAllowedCurrencyOut[tokenOut]) return false;
|
|
137
139
|
if (p.amountIn > MAX_AMOUNT_IN) return false;
|
|
140
|
+
// Slippage floor: amountOutMinimum ≥ amountIn × MIN_BPS / 10 000.
|
|
141
|
+
// WARNING: compares tokenOut against tokenIn base units. For same-price/same-decimal
|
|
142
|
+
// pairs this maps to a slippage %. For cross-price pairs it is trivially satisfied —
|
|
143
|
+
// real slippage is enforced by the agent off-chain, not by this contract.
|
|
138
144
|
if (p.amountOutMinimum < (uint256(p.amountIn) * MIN_BPS) / 10_000) return false;
|
|
139
145
|
|
|
140
146
|
return true;
|
|
@@ -6,13 +6,13 @@ pragma solidity 0.8.26;
|
|
|
6
6
|
// Version : Standard ERC-20 (version-agnostic)
|
|
7
7
|
// Chain : Ethereum mainnet (works on any EVM — the most general example)
|
|
8
8
|
//
|
|
9
|
-
//
|
|
10
|
-
// transfer(address to,
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
9
|
+
// ENFORCES ON-CHAIN (kernel calls evaluate() on every dispatch; false ⇒ dispatch blocked):
|
|
10
|
+
// transfer(address to,uint256 amount) selector 0xa9059cbb
|
|
11
|
+
// • target must be in ALLOWED_TOKENS
|
|
12
|
+
// • recipient (to) must be in ALLOWED_RECIPIENTS
|
|
13
|
+
// • amount ≤ MAX_AMOUNT_PER_TRANSFER
|
|
14
14
|
//
|
|
15
|
-
// NOT
|
|
15
|
+
// AGENT-ENFORCED / NOT BOUNDED HERE (off-chain — can change without redeploying this contract):
|
|
16
16
|
// • transfer frequency / timing
|
|
17
17
|
// • choice of token within ALLOWED_TOKENS
|
|
18
18
|
// • choice of recipient within ALLOWED_RECIPIENTS
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.26;
|
|
3
|
+
|
|
4
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
// Protocol : ERC-4626 (tokenized vault standard)
|
|
6
|
+
// Version : Standard EIP-4626 interface (version-agnostic)
|
|
7
|
+
// Chain : Base mainnet (8453) — reference vault below; works on any EVM
|
|
8
|
+
// Target : Reference vault — Moonwell Flagship USDC (MetaMorpho, ERC-4626)
|
|
9
|
+
// 0xc1256Ae5FF1cf2719D4937adb3bbCCab2E00A2Ca (verified ERC-4626 on Base: asset()=USDC)
|
|
10
|
+
// Written against the STANDARD interface, so it generalizes to any 4626 vault.
|
|
11
|
+
//
|
|
12
|
+
// ENFORCES ON-CHAIN (kernel calls evaluate() on every dispatch; false ⇒ dispatch blocked):
|
|
13
|
+
// deposit(uint256 assets,address receiver) selector 0x6e553f65
|
|
14
|
+
// • target (the vault) must be in ALLOWED_VAULTS
|
|
15
|
+
// • assets ≤ MAX_DEPOSIT_ASSETS
|
|
16
|
+
// • receiver must equal ctx.account (the SMA — shares cannot be minted to another address)
|
|
17
|
+
// withdraw(uint256 assets,address receiver,address owner) selector 0xb460af94
|
|
18
|
+
// redeem(uint256 shares,address receiver,address owner) selector 0xba087652
|
|
19
|
+
// • target (the vault) must be in ALLOWED_VAULTS
|
|
20
|
+
// • receiver must equal ctx.account (assets cannot be sent elsewhere)
|
|
21
|
+
// • owner must equal ctx.account (cannot burn shares the SMA merely has allowance over)
|
|
22
|
+
//
|
|
23
|
+
// AGENT-ENFORCED / NOT BOUNDED HERE (off-chain — can change without redeploying this contract):
|
|
24
|
+
// • Which vault within ALLOWED_VAULTS to use, and deposit/withdraw timing/cadence
|
|
25
|
+
// • Withdraw/redeem amount (only the receiver/owner are bound, not the size — the SMA can
|
|
26
|
+
// always withdraw its own position in full; add a cap here if your strategy needs one)
|
|
27
|
+
// • Underlying ASSET: enforced TRANSITIVELY via ALLOWED_VAULTS, not from calldata. A 4626
|
|
28
|
+
// vault's asset() is fixed at deploy; the deposit/withdraw calldata carries NO asset field,
|
|
29
|
+
// so the asset cannot be read from it. Allowlist only vaults whose asset() you have verified.
|
|
30
|
+
//
|
|
31
|
+
// VERIFY BEFORE USE:
|
|
32
|
+
// • Selectors are the EIP-4626 standard (verified via `cast sig`): deposit 0x6e553f65,
|
|
33
|
+
// withdraw 0xb460af94, redeem 0xba087652. (mint(uint256,address)=0x94bf804d is NOT gated
|
|
34
|
+
// here — add it if your agent mints shares by exact share count.)
|
|
35
|
+
// • Confirm each allowlisted vault actually implements ERC-4626 and its asset() is what you
|
|
36
|
+
// expect (e.g. on the reference vault, asset() == USDC 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913).
|
|
37
|
+
// • A deposit also needs a prior ERC-20 approve of the vault — gate that with a separate
|
|
38
|
+
// approve permission (see BoundedApproveAndCallBatch.sol for the atomic approve→call→reset pattern).
|
|
39
|
+
// • MAX_DEPOSIT_ASSETS is in the asset's base units (USDC = 6 decimals).
|
|
40
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
import {IPermission, Context} from "@sail/interfaces/IPermission.sol";
|
|
43
|
+
|
|
44
|
+
contract BoundedVault_ERC4626_Base is IPermission {
|
|
45
|
+
bytes32 private constant DISCRIMINATOR = keccak256("BoundedVault_ERC4626_Base");
|
|
46
|
+
|
|
47
|
+
mapping(address => bool) public isAllowedVault;
|
|
48
|
+
uint256 public immutable MAX_DEPOSIT_ASSETS;
|
|
49
|
+
|
|
50
|
+
// EIP-4626 standard selectors
|
|
51
|
+
bytes4 private constant SEL_DEPOSIT = 0x6e553f65; // deposit(uint256,address)
|
|
52
|
+
bytes4 private constant SEL_WITHDRAW = 0xb460af94; // withdraw(uint256,address,address)
|
|
53
|
+
bytes4 private constant SEL_REDEEM = 0xba087652; // redeem(uint256,address,address)
|
|
54
|
+
|
|
55
|
+
/// @param allowedVaults ERC-4626 vaults the agent may deposit into / withdraw from
|
|
56
|
+
/// @param maxDepositAssets Per-deposit cap in the asset's base units (must be > 0)
|
|
57
|
+
constructor(address[] memory allowedVaults, uint256 maxDepositAssets) {
|
|
58
|
+
require(allowedVaults.length > 0, "empty vault allowlist");
|
|
59
|
+
require(maxDepositAssets > 0, "zero deposit cap");
|
|
60
|
+
MAX_DEPOSIT_ASSETS = maxDepositAssets;
|
|
61
|
+
for (uint256 i = 0; i < allowedVaults.length; i++) {
|
|
62
|
+
require(allowedVaults[i] != address(0), "zero vault address");
|
|
63
|
+
isAllowedVault[allowedVaults[i]] = true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function evaluate(bytes calldata txData, Context calldata ctx) external view returns (bool) {
|
|
68
|
+
if (!isAllowedVault[ctx.target]) return false;
|
|
69
|
+
if (txData.length < 4) return false;
|
|
70
|
+
|
|
71
|
+
// deposit(uint256 assets, address receiver)
|
|
72
|
+
if (ctx.selector == SEL_DEPOSIT) {
|
|
73
|
+
if (txData.length < 4 + 2 * 32) return false;
|
|
74
|
+
(uint256 assets, address receiver) = abi.decode(txData[4:], (uint256, address));
|
|
75
|
+
if (assets > MAX_DEPOSIT_ASSETS) return false;
|
|
76
|
+
if (receiver != ctx.account) return false; // shares must accrue to the SMA
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// withdraw(uint256 assets, address receiver, address owner)
|
|
81
|
+
// redeem(uint256 shares, address receiver, address owner)
|
|
82
|
+
// Identical parameter layout: (uint256, address, address).
|
|
83
|
+
// Amount (assets/shares) is NOT bounded — the SMA can always exit its full position.
|
|
84
|
+
// Add a maxWithdrawAssets constructor param and check here if your strategy needs a cap.
|
|
85
|
+
if (ctx.selector == SEL_WITHDRAW || ctx.selector == SEL_REDEEM) {
|
|
86
|
+
if (txData.length < 4 + 3 * 32) return false;
|
|
87
|
+
(, address receiver, address owner) = abi.decode(txData[4:], (uint256, address, address));
|
|
88
|
+
if (receiver != ctx.account) return false; // assets must return to the SMA
|
|
89
|
+
if (owner != ctx.account) return false; // only the SMA's own shares may be burned
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function discriminator() external pure returns (bytes32) { return DISCRIMINATOR; }
|
|
97
|
+
}
|
|
@@ -27,14 +27,25 @@ You own what you deploy.
|
|
|
27
27
|
|
|
28
28
|
## Examples
|
|
29
29
|
|
|
30
|
+
Each header has two blocks: **ENFORCES ON-CHAIN** (bounds the kernel checks in `evaluate()` on
|
|
31
|
+
every dispatch — these actually hold) and **AGENT-ENFORCED / NOT BOUNDED HERE** (left to your
|
|
32
|
+
off-chain agent code — these do *not* hold on-chain). Read both before deploying.
|
|
33
|
+
|
|
30
34
|
| File | Protocol | Version | Chain | Status |
|
|
31
35
|
|---|---|---|---|---|
|
|
32
36
|
| `BoundedSwap_UniswapV3_Base.sol` | Uniswap Swap | V3 SwapRouter02 | Base | Full decode |
|
|
33
37
|
| `BoundedSwap_UniswapV4_Unichain.sol` | Uniswap Swap | V4 Universal Router | Unichain | Partial decode — see header |
|
|
34
|
-
| `BoundedBorrow_AaveV3_Arbitrum.sol` | Aave Borrow | V3 Pool | Arbitrum | Full decode
|
|
38
|
+
| `BoundedBorrow_AaveV3_Arbitrum.sol` | Aave Borrow | V3 Pool | Arbitrum | Full decode |
|
|
39
|
+
| `BoundedSupply_AaveV3_Arbitrum.sol` | Aave Supply | V3 Pool | Arbitrum | Full decode |
|
|
40
|
+
| `BoundedVault_ERC4626_Base.sol` | ERC-4626 Vault deposit/withdraw | EIP-4626 standard | Base (any EVM) | Full decode |
|
|
41
|
+
| `BoundedStake_Venice_Base.sol` | Venice (VVV) staking | sVVV staking | Base | Full decode |
|
|
35
42
|
| `BoundedTransfer_ERC20_Ethereum.sol` | ERC-20 Transfer | — | Ethereum (any EVM) | Full decode |
|
|
36
|
-
| `BoundedPerp_GMXv2_Arbitrum.sol` | GMX Perpetuals | V2 ExchangeRouter | Arbitrum |
|
|
43
|
+
| `BoundedPerp_GMXv2_Arbitrum.sol` | GMX Perpetuals | V2 ExchangeRouter | Arbitrum | Reference pattern — verify selector/struct/router against live GMX ABI (see header) |
|
|
37
44
|
| `BoundedBet_Limitless_Base.sol` | Limitless Prediction | CTF Exchange | Base | UNVERIFIED ABI — see header |
|
|
45
|
+
| `BoundedApproveAndCallBatch.sol` | Atomic approve→call→reset | Sail `IBatchPermission` | Any selective kernel | Full decode — batch-only (see note below) |
|
|
46
|
+
|
|
47
|
+
The `interfaces/` directory holds `IPermission.sol` (single-call permissions) and
|
|
48
|
+
`IBatchPermission.sol` (batch permissions — see the batch example).
|
|
38
49
|
|
|
39
50
|
## Using these examples
|
|
40
51
|
|
|
@@ -50,3 +61,19 @@ To deploy within a Sailor project (copy the .sol file to `mandates/` first):
|
|
|
50
61
|
```bash
|
|
51
62
|
sailor mandate deploy --contract <Name> --args '[...]' --attach --sma <SMA>
|
|
52
63
|
```
|
|
64
|
+
|
|
65
|
+
## Verify before you authorize
|
|
66
|
+
|
|
67
|
+
Prove a permission accepts the calls you want and rejects the ones you don't — before paying
|
|
68
|
+
registration gas — with `sailor mandate simulate` (off-chain `eth_call`, no gas, signs nothing):
|
|
69
|
+
```bash
|
|
70
|
+
sailor mandate simulate --address <permission> --calls calls.json
|
|
71
|
+
```
|
|
72
|
+
Every example here was checked this way (valid calls PASS, out-of-bounds calls FAIL).
|
|
73
|
+
|
|
74
|
+
**Batch permissions are different.** `BoundedApproveAndCallBatch.sol` implements `IBatchPermission`
|
|
75
|
+
and is gated by the kernel's `dispatchBatch`, not single `dispatch` — its single-call `evaluate()`
|
|
76
|
+
deliberately returns `false`. `sailor mandate simulate` only probes single `evaluate()`, so it
|
|
77
|
+
**cannot** verify a batch permission. Until simulate gains a batch mode, verify a batch permission
|
|
78
|
+
by calling its `evaluateBatch(calls, ctx)` view directly (e.g. `cast call`) with the call sequences
|
|
79
|
+
you expect to pass and fail — same fail-closed `eth_call` semantics, the correct entrypoint.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.26;
|
|
3
|
+
|
|
4
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
// SailCalldata — safe static-parameter extraction for IPermission.evaluate()
|
|
6
|
+
//
|
|
7
|
+
// PROBLEM
|
|
8
|
+
// evaluate(bytes calldata txData, Context calldata ctx) receives raw ABI-
|
|
9
|
+
// encoded calldata. Extracting parameters by hand is the most dangerous part
|
|
10
|
+
// of permission writing:
|
|
11
|
+
// • Forgetting the length check before decoding → silent wrong value or
|
|
12
|
+
// revert instead of clean `return false`.
|
|
13
|
+
// • Wrong slot index → decoding the wrong parameter (type-checks, still wrong).
|
|
14
|
+
// • Truncation bugs when casting uint256 slots to address (padding bits).
|
|
15
|
+
//
|
|
16
|
+
// SOLUTION
|
|
17
|
+
// This library centralises the three operations into named helpers:
|
|
18
|
+
// 1. hasParams() — explicit length guard (call once, at the top of evaluate)
|
|
19
|
+
// 2. asAddress() — extract + mask to 20 bytes
|
|
20
|
+
// 3. asUint256(), asInt256(), asBytes32(), asBool(), asUint128() — typed slots
|
|
21
|
+
//
|
|
22
|
+
// All helpers are view/pure and add zero gas overhead beyond the slice itself.
|
|
23
|
+
//
|
|
24
|
+
// USAGE (replace abi.decode pattern)
|
|
25
|
+
//
|
|
26
|
+
// // Before:
|
|
27
|
+
// if (txData.length < 4 + 3 * 32) return false;
|
|
28
|
+
// (address asset, uint256 amount, address onBehalfOf) =
|
|
29
|
+
// abi.decode(txData[4:], (address, uint256, address));
|
|
30
|
+
//
|
|
31
|
+
// // After:
|
|
32
|
+
// if (!SailCalldata.hasParams(txData, 3)) return false;
|
|
33
|
+
// address asset = SailCalldata.asAddress(txData, 0);
|
|
34
|
+
// uint256 amount = SailCalldata.asUint256(txData, 1);
|
|
35
|
+
// address onBehalfOf = SailCalldata.asAddress(txData, 2);
|
|
36
|
+
//
|
|
37
|
+
// LIMITATIONS
|
|
38
|
+
// Only covers static (fixed-size) ABI types. Dynamic types (bytes, string,
|
|
39
|
+
// arrays) use pointer indirection — decode them with abi.decode(txData[4:], ...)
|
|
40
|
+
// after the hasParams() guard, or write a dedicated extractor.
|
|
41
|
+
//
|
|
42
|
+
// SLOT INDEXING
|
|
43
|
+
// Slots are 0-indexed from the first constructor parameter (after the 4-byte
|
|
44
|
+
// selector). Slot 0 = bytes [4..35], slot 1 = bytes [36..67], etc.
|
|
45
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
library SailCalldata {
|
|
48
|
+
// ── Guards ────────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
/// @notice Returns true when txData is long enough to hold `params` static
|
|
51
|
+
/// 32-byte ABI slots after the 4-byte selector.
|
|
52
|
+
/// @dev Call this once at the top of evaluate() before any slot access.
|
|
53
|
+
/// Returns false (not revert) so evaluate() can return false cleanly.
|
|
54
|
+
function hasParams(bytes calldata txData, uint256 params) internal pure returns (bool) {
|
|
55
|
+
return txData.length >= 4 + params * 32;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ── Static-type extractors ────────────────────────────────────────────────
|
|
59
|
+
// All assume hasParams() has already been checked for the relevant slot.
|
|
60
|
+
// Accessing an out-of-bounds slot will cause the calldata slice to revert —
|
|
61
|
+
// always guard with hasParams() first.
|
|
62
|
+
|
|
63
|
+
/// @notice Extract an address from ABI slot `i` (0-indexed after selector).
|
|
64
|
+
/// Masks to 20 bytes, discarding the ABI zero-padding in the upper 12.
|
|
65
|
+
function asAddress(bytes calldata txData, uint256 i) internal pure returns (address) {
|
|
66
|
+
return address(uint160(uint256(bytes32(txData[4 + i * 32 : 4 + (i + 1) * 32]))));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/// @notice Extract a uint256 from ABI slot `i`.
|
|
70
|
+
function asUint256(bytes calldata txData, uint256 i) internal pure returns (uint256) {
|
|
71
|
+
return uint256(bytes32(txData[4 + i * 32 : 4 + (i + 1) * 32]));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// @notice Extract an int256 from ABI slot `i`.
|
|
75
|
+
function asInt256(bytes calldata txData, uint256 i) internal pure returns (int256) {
|
|
76
|
+
return int256(uint256(bytes32(txData[4 + i * 32 : 4 + (i + 1) * 32])));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// @notice Extract a bytes32 from ABI slot `i`.
|
|
80
|
+
function asBytes32(bytes calldata txData, uint256 i) internal pure returns (bytes32) {
|
|
81
|
+
return bytes32(txData[4 + i * 32 : 4 + (i + 1) * 32]);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// @notice Extract a bool from ABI slot `i` (true when slot value is non-zero).
|
|
85
|
+
function asBool(bytes calldata txData, uint256 i) internal pure returns (bool) {
|
|
86
|
+
return asUint256(txData, i) != 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// @notice Extract a uint128 from ABI slot `i` (lower 128 bits of the slot).
|
|
90
|
+
function asUint128(bytes calldata txData, uint256 i) internal pure returns (uint128) {
|
|
91
|
+
return uint128(asUint256(txData, i));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// @notice Extract a uint64 from ABI slot `i`.
|
|
95
|
+
function asUint64(bytes calldata txData, uint256 i) internal pure returns (uint64) {
|
|
96
|
+
return uint64(asUint256(txData, i));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// @notice Extract a uint32 from ABI slot `i`.
|
|
100
|
+
function asUint32(bytes calldata txData, uint256 i) internal pure returns (uint32) {
|
|
101
|
+
return uint32(asUint256(txData, i));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/// @notice Extract a uint24 from ABI slot `i` (e.g. Uniswap fee tier).
|
|
105
|
+
function asUint24(bytes calldata txData, uint256 i) internal pure returns (uint24) {
|
|
106
|
+
return uint24(asUint256(txData, i));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// @notice Extract a uint16 from ABI slot `i` (e.g. Aave referral code).
|
|
110
|
+
function asUint16(bytes calldata txData, uint256 i) internal pure returns (uint16) {
|
|
111
|
+
return uint16(asUint256(txData, i));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/// @notice Extract bytes4 (a function selector) from ABI slot `i`.
|
|
115
|
+
function asBytes4(bytes calldata txData, uint256 i) internal pure returns (bytes4) {
|
|
116
|
+
return bytes4(asBytes32(txData, i));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.26;
|
|
3
|
+
|
|
4
|
+
// Mirrors SailProtocol contracts/interfaces/IBatchPermission.sol. A batch-aware
|
|
5
|
+
// permission gates `dispatchBatch` — an ordered array of subcalls executed as one
|
|
6
|
+
// atomic transaction. Exactly ONE batch permission is consulted per batch dispatch
|
|
7
|
+
// (selective model), and it owns validation of every subcall AND the cross-call
|
|
8
|
+
// invariants (ordering, matching amounts, mandatory cleanup) that no per-call
|
|
9
|
+
// IPermission can express.
|
|
10
|
+
|
|
11
|
+
/// @notice A single subcall in a batch dispatch.
|
|
12
|
+
/// @dev The kernel executes each Call as safe.execTransactionFromModule(target, value, data, 0)
|
|
13
|
+
/// — operation is always CALL, never DELEGATECALL.
|
|
14
|
+
struct Call {
|
|
15
|
+
address target; // MUST NOT equal the kernel (enforced by the kernel)
|
|
16
|
+
uint256 value; // native ETH forwarded (wei)
|
|
17
|
+
bytes data; // calldata for the subcall
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/// @notice Execution context passed to evaluateBatch on each batch dispatch (read-only snapshot).
|
|
21
|
+
struct BatchContext {
|
|
22
|
+
address account; // the Safe (SMA) whose assets are moved
|
|
23
|
+
address manager; // the delegated signer who authorised the batch
|
|
24
|
+
address submitter; // msg.sender of the dispatch (may differ from manager via a relayer)
|
|
25
|
+
address permission; // this batch permission contract
|
|
26
|
+
bytes32 batchHash; // keccak256(abi.encode(calls)) — stable id for the exact sequence
|
|
27
|
+
uint256 blockTimestamp; // block.timestamp at dispatch
|
|
28
|
+
uint256 blockNumber; // block.number at dispatch
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface IBatchPermission {
|
|
32
|
+
/// @notice Validate an entire batch of subcalls. Called via staticcall under a gas cap;
|
|
33
|
+
/// a revert / OOG / malformed return is treated as `false` (fail-closed).
|
|
34
|
+
function evaluateBatch(Call[] calldata calls, BatchContext calldata ctx) external view returns (bool);
|
|
35
|
+
|
|
36
|
+
/// @notice Marker the kernel uses (try/catch) to detect batch-aware permissions. MUST return true.
|
|
37
|
+
function isBatchPermission() external pure returns (bool);
|
|
38
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dev.sail.money/sailor",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0-38",
|
|
4
4
|
"description": "Operator toolkit for Sail Protocol",
|
|
5
5
|
"bin": {
|
|
6
6
|
"sailor": "packages/cli/dist/index.cjs"
|
|
7
7
|
},
|
|
8
|
+
"exports": {
|
|
9
|
+
"./sdk": {
|
|
10
|
+
"import": "./packages/sdk/dist/index.js",
|
|
11
|
+
"types": "./packages/sdk/dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
8
14
|
"files": [
|
|
9
15
|
"packages/cli/dist",
|
|
10
16
|
"packages/sdk/dist",
|
|
@@ -17,23 +23,22 @@
|
|
|
17
23
|
"README.md",
|
|
18
24
|
"AGENTS.md"
|
|
19
25
|
],
|
|
20
|
-
"devDependencies": {
|
|
21
|
-
"@biomejs/biome": "^1.9.0",
|
|
22
|
-
"typescript": "^5.5.0"
|
|
23
|
-
},
|
|
24
|
-
"dependencies": {
|
|
25
|
-
"tsx": "^4.22.4"
|
|
26
|
-
},
|
|
27
26
|
"scripts": {
|
|
28
|
-
"build": "pnpm --filter @sail/sdk build && pnpm --filter
|
|
27
|
+
"build": "pnpm --filter @sail/sdk build && pnpm --filter sailor build && pnpm --filter sailor-ui build",
|
|
29
28
|
"test": "pnpm --filter sailor-ui test",
|
|
30
29
|
"test:ui": "pnpm --filter sailor-ui test:ui",
|
|
31
30
|
"link:cli": "pnpm link --global",
|
|
32
|
-
"
|
|
33
|
-
"typecheck": "pnpm --filter @sail/sdk build && pnpm --filter @sail/chains build && pnpm -r typecheck",
|
|
31
|
+
"typecheck": "pnpm --filter @sail/sdk build && pnpm -r typecheck",
|
|
34
32
|
"docs:check": "node scripts/check-docs.mjs",
|
|
35
33
|
"init:check": "node scripts/check-init.mjs",
|
|
36
34
|
"eval": "node evals/run.mjs",
|
|
37
35
|
"lint": "biome check ."
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@biomejs/biome": "^1.9.0",
|
|
39
|
+
"typescript": "^5.5.0"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"tsx": "^4.22.4"
|
|
38
43
|
}
|
|
39
|
-
}
|
|
44
|
+
}
|