@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,82 +1,82 @@
|
|
|
1
|
-
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity 0.8.26;
|
|
3
|
-
|
|
4
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
5
|
-
// Protocol : Aave V3
|
|
6
|
-
// Version : Pool (proxy) — fully on-chain, oracle-based accounting
|
|
7
|
-
// Chain : Arbitrum mainnet (42161)
|
|
8
|
-
// Target : Aave V3 Pool 0x794a61358D6845594F94dc1DB02A252b5b4814aD (verified on Arbiscan;
|
|
9
|
-
// same Pool as BoundedBorrow_AaveV3_Arbitrum — supply 0x617ba037 confirmed present
|
|
10
|
-
// in the live implementation 0xf05fd3cc...)
|
|
11
|
-
//
|
|
12
|
-
// ENFORCES ON-CHAIN (kernel calls evaluate() on every dispatch; false ⇒ dispatch blocked):
|
|
13
|
-
// supply(address asset,uint256 amount,address onBehalfOf,uint16 referralCode) selector 0x617ba037
|
|
14
|
-
// • target must be AAVE_POOL
|
|
15
|
-
// • asset must be in ALLOWED_ASSETS
|
|
16
|
-
// • amount ≤ MAX_SUPPLY_AMOUNT
|
|
17
|
-
// • onBehalfOf must equal ctx.account (the SMA — collateral is credited to the SMA, not
|
|
18
|
-
// to another account)
|
|
19
|
-
//
|
|
20
|
-
// AGENT-ENFORCED / NOT BOUNDED HERE (off-chain — can change without redeploying this contract):
|
|
21
|
-
// • referralCode (informational only, does not affect fund safety)
|
|
22
|
-
// • Supply timing / cadence and choice of asset within ALLOWED_ASSETS
|
|
23
|
-
// • Withdrawal: not gated here (add a withdraw(asset,amount,to) permission with to==SMA if needed)
|
|
24
|
-
// • A supply also needs a prior ERC-20 approve of the Pool — gate that separately
|
|
25
|
-
// (see BoundedApproveAndCallBatch.sol for the atomic approve→call→reset pattern).
|
|
26
|
-
//
|
|
27
|
-
// VERIFY BEFORE USE:
|
|
28
|
-
// • Selector 0x617ba037 = supply(address,uint256,address,uint16) — verified via `cast sig`
|
|
29
|
-
// and confirmed present in the deployed Pool implementation on Arbitrum.
|
|
30
|
-
// • This is the canonical Aave V3 Pool.supply. (V3.x also exposes supplyWithPermit — not
|
|
31
|
-
// gated here; add it if your agent supplies via EIP-2612 permits.)
|
|
32
|
-
// • MAX_SUPPLY_AMOUNT is in the asset's base units (e.g. USDC = 6 decimals).
|
|
33
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
34
|
-
|
|
35
|
-
import {IPermission, Context} from "@sail/interfaces/IPermission.sol";
|
|
36
|
-
import {SailCalldata} from "./SailCalldata.sol";
|
|
37
|
-
|
|
38
|
-
contract BoundedSupply_AaveV3_Arbitrum is IPermission {
|
|
39
|
-
bytes32 private constant DISCRIMINATOR = keccak256("BoundedSupply_AaveV3_Arbitrum");
|
|
40
|
-
|
|
41
|
-
address public immutable AAVE_POOL;
|
|
42
|
-
mapping(address => bool) public isAllowedAsset;
|
|
43
|
-
uint256 public immutable MAX_SUPPLY_AMOUNT;
|
|
44
|
-
|
|
45
|
-
// supply(address,uint256,address,uint16) — verified 0x617ba037
|
|
46
|
-
bytes4 private constant SEL_SUPPLY = 0x617ba037;
|
|
47
|
-
|
|
48
|
-
/// @param aavePool Aave V3 Pool proxy address
|
|
49
|
-
/// @param allowedAssets Assets the agent may supply (must be non-empty)
|
|
50
|
-
/// @param maxSupplyAmount Per-call supply cap in asset base units (must be > 0)
|
|
51
|
-
constructor(address aavePool, address[] memory allowedAssets, uint256 maxSupplyAmount) {
|
|
52
|
-
require(aavePool != address(0), "zero pool address");
|
|
53
|
-
require(allowedAssets.length > 0, "empty asset allowlist");
|
|
54
|
-
require(maxSupplyAmount > 0, "zero supply cap");
|
|
55
|
-
AAVE_POOL = aavePool;
|
|
56
|
-
MAX_SUPPLY_AMOUNT = maxSupplyAmount;
|
|
57
|
-
for (uint256 i = 0; i < allowedAssets.length; i++) {
|
|
58
|
-
require(allowedAssets[i] != address(0), "zero asset address");
|
|
59
|
-
isAllowedAsset[allowedAssets[i]] = true;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function evaluate(bytes calldata txData, Context calldata ctx) external view returns (bool) {
|
|
64
|
-
if (ctx.target != AAVE_POOL) return false;
|
|
65
|
-
if (ctx.selector != SEL_SUPPLY) return false;
|
|
66
|
-
// supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode)
|
|
67
|
-
if (!SailCalldata.hasParams(txData, 4)) return false;
|
|
68
|
-
|
|
69
|
-
address asset = SailCalldata.asAddress(txData, 0);
|
|
70
|
-
uint256 amount = SailCalldata.asUint256(txData, 1);
|
|
71
|
-
address onBehalfOf = SailCalldata.asAddress(txData, 2);
|
|
72
|
-
// slot 3 = referralCode (uint16) — informational, not bounded
|
|
73
|
-
|
|
74
|
-
if (!isAllowedAsset[asset]) return false;
|
|
75
|
-
if (amount > MAX_SUPPLY_AMOUNT) return false;
|
|
76
|
-
if (onBehalfOf != ctx.account) return false; // collateral credited only to the SMA
|
|
77
|
-
|
|
78
|
-
return true;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function discriminator() external pure returns (bytes32) { return DISCRIMINATOR; }
|
|
82
|
-
}
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.26;
|
|
3
|
+
|
|
4
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
// Protocol : Aave V3
|
|
6
|
+
// Version : Pool (proxy) — fully on-chain, oracle-based accounting
|
|
7
|
+
// Chain : Arbitrum mainnet (42161)
|
|
8
|
+
// Target : Aave V3 Pool 0x794a61358D6845594F94dc1DB02A252b5b4814aD (verified on Arbiscan;
|
|
9
|
+
// same Pool as BoundedBorrow_AaveV3_Arbitrum — supply 0x617ba037 confirmed present
|
|
10
|
+
// in the live implementation 0xf05fd3cc...)
|
|
11
|
+
//
|
|
12
|
+
// ENFORCES ON-CHAIN (kernel calls evaluate() on every dispatch; false ⇒ dispatch blocked):
|
|
13
|
+
// supply(address asset,uint256 amount,address onBehalfOf,uint16 referralCode) selector 0x617ba037
|
|
14
|
+
// • target must be AAVE_POOL
|
|
15
|
+
// • asset must be in ALLOWED_ASSETS
|
|
16
|
+
// • amount ≤ MAX_SUPPLY_AMOUNT
|
|
17
|
+
// • onBehalfOf must equal ctx.account (the SMA — collateral is credited to the SMA, not
|
|
18
|
+
// to another account)
|
|
19
|
+
//
|
|
20
|
+
// AGENT-ENFORCED / NOT BOUNDED HERE (off-chain — can change without redeploying this contract):
|
|
21
|
+
// • referralCode (informational only, does not affect fund safety)
|
|
22
|
+
// • Supply timing / cadence and choice of asset within ALLOWED_ASSETS
|
|
23
|
+
// • Withdrawal: not gated here (add a withdraw(asset,amount,to) permission with to==SMA if needed)
|
|
24
|
+
// • A supply also needs a prior ERC-20 approve of the Pool — gate that separately
|
|
25
|
+
// (see BoundedApproveAndCallBatch.sol for the atomic approve→call→reset pattern).
|
|
26
|
+
//
|
|
27
|
+
// VERIFY BEFORE USE:
|
|
28
|
+
// • Selector 0x617ba037 = supply(address,uint256,address,uint16) — verified via `cast sig`
|
|
29
|
+
// and confirmed present in the deployed Pool implementation on Arbitrum.
|
|
30
|
+
// • This is the canonical Aave V3 Pool.supply. (V3.x also exposes supplyWithPermit — not
|
|
31
|
+
// gated here; add it if your agent supplies via EIP-2612 permits.)
|
|
32
|
+
// • MAX_SUPPLY_AMOUNT is in the asset's base units (e.g. USDC = 6 decimals).
|
|
33
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
import {IPermission, Context} from "@sail/interfaces/IPermission.sol";
|
|
36
|
+
import {SailCalldata} from "./SailCalldata.sol";
|
|
37
|
+
|
|
38
|
+
contract BoundedSupply_AaveV3_Arbitrum is IPermission {
|
|
39
|
+
bytes32 private constant DISCRIMINATOR = keccak256("BoundedSupply_AaveV3_Arbitrum");
|
|
40
|
+
|
|
41
|
+
address public immutable AAVE_POOL;
|
|
42
|
+
mapping(address => bool) public isAllowedAsset;
|
|
43
|
+
uint256 public immutable MAX_SUPPLY_AMOUNT;
|
|
44
|
+
|
|
45
|
+
// supply(address,uint256,address,uint16) — verified 0x617ba037
|
|
46
|
+
bytes4 private constant SEL_SUPPLY = 0x617ba037;
|
|
47
|
+
|
|
48
|
+
/// @param aavePool Aave V3 Pool proxy address
|
|
49
|
+
/// @param allowedAssets Assets the agent may supply (must be non-empty)
|
|
50
|
+
/// @param maxSupplyAmount Per-call supply cap in asset base units (must be > 0)
|
|
51
|
+
constructor(address aavePool, address[] memory allowedAssets, uint256 maxSupplyAmount) {
|
|
52
|
+
require(aavePool != address(0), "zero pool address");
|
|
53
|
+
require(allowedAssets.length > 0, "empty asset allowlist");
|
|
54
|
+
require(maxSupplyAmount > 0, "zero supply cap");
|
|
55
|
+
AAVE_POOL = aavePool;
|
|
56
|
+
MAX_SUPPLY_AMOUNT = maxSupplyAmount;
|
|
57
|
+
for (uint256 i = 0; i < allowedAssets.length; i++) {
|
|
58
|
+
require(allowedAssets[i] != address(0), "zero asset address");
|
|
59
|
+
isAllowedAsset[allowedAssets[i]] = true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function evaluate(bytes calldata txData, Context calldata ctx) external view returns (bool) {
|
|
64
|
+
if (ctx.target != AAVE_POOL) return false;
|
|
65
|
+
if (ctx.selector != SEL_SUPPLY) return false;
|
|
66
|
+
// supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode)
|
|
67
|
+
if (!SailCalldata.hasParams(txData, 4)) return false;
|
|
68
|
+
|
|
69
|
+
address asset = SailCalldata.asAddress(txData, 0);
|
|
70
|
+
uint256 amount = SailCalldata.asUint256(txData, 1);
|
|
71
|
+
address onBehalfOf = SailCalldata.asAddress(txData, 2);
|
|
72
|
+
// slot 3 = referralCode (uint16) — informational, not bounded
|
|
73
|
+
|
|
74
|
+
if (!isAllowedAsset[asset]) return false;
|
|
75
|
+
if (amount > MAX_SUPPLY_AMOUNT) return false;
|
|
76
|
+
if (onBehalfOf != ctx.account) return false; // collateral credited only to the SMA
|
|
77
|
+
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function discriminator() external pure returns (bytes32) { return DISCRIMINATOR; }
|
|
82
|
+
}
|
|
@@ -1,116 +1,116 @@
|
|
|
1
|
-
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity 0.8.26;
|
|
3
|
-
|
|
4
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
5
|
-
// Protocol : Uniswap V3
|
|
6
|
-
// Version : SwapRouter02 (NOT the older SwapRouter — different selectors)
|
|
7
|
-
// Chain : Base mainnet (8453)
|
|
8
|
-
// Target : SwapRouter02 0x2626664c2603336E57B271c5C0b26F421741e481 (verified on Basescan)
|
|
9
|
-
//
|
|
10
|
-
// ENFORCES ON-CHAIN (kernel calls evaluate() on every dispatch; false ⇒ dispatch blocked):
|
|
11
|
-
// exactInputSingle((address,address,uint24,address,uint256,uint256,uint160)) selector 0x04e45aaf
|
|
12
|
-
// • target must be SWAP_ROUTER
|
|
13
|
-
// • tokenIn must equal FIXED_TOKEN_IN
|
|
14
|
-
// • tokenOut must be in ALLOWED_TOKENS_OUT
|
|
15
|
-
// • amountIn ≤ MAX_AMOUNT_IN
|
|
16
|
-
// • amountOutMinimum ≥ amountIn × MIN_BPS / 10 000 (slippage floor — see caveat below)
|
|
17
|
-
// approve(address,uint256) selector 0x095ea7b3
|
|
18
|
-
// • target must be FIXED_TOKEN_IN (the ERC-20 being approved)
|
|
19
|
-
// • spender must be SWAP_ROUTER
|
|
20
|
-
// • amount ≤ MAX_AMOUNT_IN
|
|
21
|
-
//
|
|
22
|
-
// AGENT-ENFORCED / NOT BOUNDED HERE (off-chain — can change without redeploying this contract):
|
|
23
|
-
// • fee tier, sqrtPriceLimitX96, recipient address
|
|
24
|
-
// • swap frequency / cadence
|
|
25
|
-
// • real (cross-denomination) slippage — see MIN_BPS caveat in evaluate()
|
|
26
|
-
//
|
|
27
|
-
// VERIFY BEFORE USE:
|
|
28
|
-
// • Confirm SwapRouter02 address on your chain (Base default shown above; verified on Basescan).
|
|
29
|
-
// • Selector 0x04e45aaf = exactInputSingle((address,address,uint24,address,uint256,uint256,uint160))
|
|
30
|
-
// on SwapRouter02 (verified via `cast sig`). The OLDER SwapRouter's exactInputSingle (the
|
|
31
|
-
// deadline variant) is 0x414bf389 — a different selector; do not confuse the two.
|
|
32
|
-
// • Confirm that amountOutMinimum slippage floor fits your price-impact expectations for the
|
|
33
|
-
// chosen pool. Large amountIn values may trigger price impact beyond MIN_BPS.
|
|
34
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
35
|
-
|
|
36
|
-
import {IPermission, Context} from "@sail/interfaces/IPermission.sol";
|
|
37
|
-
|
|
38
|
-
contract BoundedSwap_UniswapV3_Base is IPermission {
|
|
39
|
-
bytes32 private constant DISCRIMINATOR = keccak256("BoundedSwap_UniswapV3_Base");
|
|
40
|
-
|
|
41
|
-
address public immutable SWAP_ROUTER;
|
|
42
|
-
address public immutable FIXED_TOKEN_IN;
|
|
43
|
-
mapping(address => bool) public isAllowedTokenOut;
|
|
44
|
-
uint256 public immutable MAX_AMOUNT_IN;
|
|
45
|
-
/// @dev Slippage floor in basis points. 9 900 = allow at most 1% slippage.
|
|
46
|
-
uint256 public immutable MIN_BPS;
|
|
47
|
-
|
|
48
|
-
bytes4 private constant SEL_EXACT_INPUT_SINGLE = 0x04e45aaf;
|
|
49
|
-
bytes4 private constant SEL_APPROVE = 0x095ea7b3;
|
|
50
|
-
|
|
51
|
-
struct ExactInputSingleParams {
|
|
52
|
-
address tokenIn;
|
|
53
|
-
address tokenOut;
|
|
54
|
-
uint24 fee;
|
|
55
|
-
address recipient;
|
|
56
|
-
uint256 amountIn;
|
|
57
|
-
uint256 amountOutMinimum;
|
|
58
|
-
uint160 sqrtPriceLimitX96;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/// @param swapRouter SwapRouter02 address (0x2626... on Base)
|
|
62
|
-
/// @param fixedTokenIn The one token the agent is allowed to sell
|
|
63
|
-
/// @param allowedTokensOut Tokens the agent may receive
|
|
64
|
-
/// @param maxAmountIn Per-call spend cap in fixedTokenIn base units
|
|
65
|
-
/// @param minBps Minimum amountOutMinimum as fraction of amountIn ×10 000
|
|
66
|
-
/// e.g. 9900 → require amountOutMinimum ≥ 99% of amountIn
|
|
67
|
-
/// (denominated in fixedTokenIn units, not USD — set per your pair)
|
|
68
|
-
constructor(
|
|
69
|
-
address swapRouter,
|
|
70
|
-
address fixedTokenIn,
|
|
71
|
-
address[] memory allowedTokensOut,
|
|
72
|
-
uint256 maxAmountIn,
|
|
73
|
-
uint256 minBps
|
|
74
|
-
) {
|
|
75
|
-
require(minBps <= 10_000, "minBps > 10000");
|
|
76
|
-
SWAP_ROUTER = swapRouter;
|
|
77
|
-
FIXED_TOKEN_IN = fixedTokenIn;
|
|
78
|
-
MAX_AMOUNT_IN = maxAmountIn;
|
|
79
|
-
MIN_BPS = minBps;
|
|
80
|
-
for (uint256 i = 0; i < allowedTokensOut.length; i++) {
|
|
81
|
-
isAllowedTokenOut[allowedTokensOut[i]] = true;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function evaluate(bytes calldata txData, Context calldata ctx) external view returns (bool) {
|
|
86
|
-
if (txData.length < 4) return false;
|
|
87
|
-
|
|
88
|
-
// exactInputSingle: agent swaps FIXED_TOKEN_IN for an allowed output token
|
|
89
|
-
if (ctx.target == SWAP_ROUTER && ctx.selector == SEL_EXACT_INPUT_SINGLE) {
|
|
90
|
-
if (txData.length < 4 + 7 * 32) return false;
|
|
91
|
-
ExactInputSingleParams memory p = abi.decode(txData[4:], (ExactInputSingleParams));
|
|
92
|
-
if (p.tokenIn != FIXED_TOKEN_IN) return false;
|
|
93
|
-
if (!isAllowedTokenOut[p.tokenOut]) return false;
|
|
94
|
-
if (p.amountIn > MAX_AMOUNT_IN) return false;
|
|
95
|
-
// Slippage floor: amountOutMinimum ≥ amountIn × MIN_BPS / 10 000.
|
|
96
|
-
// WARNING: compares tokenOut against tokenIn base units. For same-price/same-decimal
|
|
97
|
-
// pairs this maps to a slippage %. For cross-price pairs (e.g. USDC→WETH) it is
|
|
98
|
-
// trivially satisfied — real slippage is enforced by the agent off-chain, not here.
|
|
99
|
-
if (p.amountOutMinimum < (p.amountIn * MIN_BPS) / 10_000) return false;
|
|
100
|
-
return true;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// approve: agent approves the router to spend FIXED_TOKEN_IN
|
|
104
|
-
if (ctx.target == FIXED_TOKEN_IN && ctx.selector == SEL_APPROVE) {
|
|
105
|
-
if (txData.length < 4 + 2 * 32) return false;
|
|
106
|
-
(address spender, uint256 amount) = abi.decode(txData[4:], (address, uint256));
|
|
107
|
-
if (spender != SWAP_ROUTER) return false;
|
|
108
|
-
if (amount > MAX_AMOUNT_IN) return false;
|
|
109
|
-
return true;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function discriminator() external pure returns (bytes32) { return DISCRIMINATOR; }
|
|
116
|
-
}
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.26;
|
|
3
|
+
|
|
4
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
// Protocol : Uniswap V3
|
|
6
|
+
// Version : SwapRouter02 (NOT the older SwapRouter — different selectors)
|
|
7
|
+
// Chain : Base mainnet (8453)
|
|
8
|
+
// Target : SwapRouter02 0x2626664c2603336E57B271c5C0b26F421741e481 (verified on Basescan)
|
|
9
|
+
//
|
|
10
|
+
// ENFORCES ON-CHAIN (kernel calls evaluate() on every dispatch; false ⇒ dispatch blocked):
|
|
11
|
+
// exactInputSingle((address,address,uint24,address,uint256,uint256,uint160)) selector 0x04e45aaf
|
|
12
|
+
// • target must be SWAP_ROUTER
|
|
13
|
+
// • tokenIn must equal FIXED_TOKEN_IN
|
|
14
|
+
// • tokenOut must be in ALLOWED_TOKENS_OUT
|
|
15
|
+
// • amountIn ≤ MAX_AMOUNT_IN
|
|
16
|
+
// • amountOutMinimum ≥ amountIn × MIN_BPS / 10 000 (slippage floor — see caveat below)
|
|
17
|
+
// approve(address,uint256) selector 0x095ea7b3
|
|
18
|
+
// • target must be FIXED_TOKEN_IN (the ERC-20 being approved)
|
|
19
|
+
// • spender must be SWAP_ROUTER
|
|
20
|
+
// • amount ≤ MAX_AMOUNT_IN
|
|
21
|
+
//
|
|
22
|
+
// AGENT-ENFORCED / NOT BOUNDED HERE (off-chain — can change without redeploying this contract):
|
|
23
|
+
// • fee tier, sqrtPriceLimitX96, recipient address
|
|
24
|
+
// • swap frequency / cadence
|
|
25
|
+
// • real (cross-denomination) slippage — see MIN_BPS caveat in evaluate()
|
|
26
|
+
//
|
|
27
|
+
// VERIFY BEFORE USE:
|
|
28
|
+
// • Confirm SwapRouter02 address on your chain (Base default shown above; verified on Basescan).
|
|
29
|
+
// • Selector 0x04e45aaf = exactInputSingle((address,address,uint24,address,uint256,uint256,uint160))
|
|
30
|
+
// on SwapRouter02 (verified via `cast sig`). The OLDER SwapRouter's exactInputSingle (the
|
|
31
|
+
// deadline variant) is 0x414bf389 — a different selector; do not confuse the two.
|
|
32
|
+
// • Confirm that amountOutMinimum slippage floor fits your price-impact expectations for the
|
|
33
|
+
// chosen pool. Large amountIn values may trigger price impact beyond MIN_BPS.
|
|
34
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
import {IPermission, Context} from "@sail/interfaces/IPermission.sol";
|
|
37
|
+
|
|
38
|
+
contract BoundedSwap_UniswapV3_Base is IPermission {
|
|
39
|
+
bytes32 private constant DISCRIMINATOR = keccak256("BoundedSwap_UniswapV3_Base");
|
|
40
|
+
|
|
41
|
+
address public immutable SWAP_ROUTER;
|
|
42
|
+
address public immutable FIXED_TOKEN_IN;
|
|
43
|
+
mapping(address => bool) public isAllowedTokenOut;
|
|
44
|
+
uint256 public immutable MAX_AMOUNT_IN;
|
|
45
|
+
/// @dev Slippage floor in basis points. 9 900 = allow at most 1% slippage.
|
|
46
|
+
uint256 public immutable MIN_BPS;
|
|
47
|
+
|
|
48
|
+
bytes4 private constant SEL_EXACT_INPUT_SINGLE = 0x04e45aaf;
|
|
49
|
+
bytes4 private constant SEL_APPROVE = 0x095ea7b3;
|
|
50
|
+
|
|
51
|
+
struct ExactInputSingleParams {
|
|
52
|
+
address tokenIn;
|
|
53
|
+
address tokenOut;
|
|
54
|
+
uint24 fee;
|
|
55
|
+
address recipient;
|
|
56
|
+
uint256 amountIn;
|
|
57
|
+
uint256 amountOutMinimum;
|
|
58
|
+
uint160 sqrtPriceLimitX96;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// @param swapRouter SwapRouter02 address (0x2626... on Base)
|
|
62
|
+
/// @param fixedTokenIn The one token the agent is allowed to sell
|
|
63
|
+
/// @param allowedTokensOut Tokens the agent may receive
|
|
64
|
+
/// @param maxAmountIn Per-call spend cap in fixedTokenIn base units
|
|
65
|
+
/// @param minBps Minimum amountOutMinimum as fraction of amountIn ×10 000
|
|
66
|
+
/// e.g. 9900 → require amountOutMinimum ≥ 99% of amountIn
|
|
67
|
+
/// (denominated in fixedTokenIn units, not USD — set per your pair)
|
|
68
|
+
constructor(
|
|
69
|
+
address swapRouter,
|
|
70
|
+
address fixedTokenIn,
|
|
71
|
+
address[] memory allowedTokensOut,
|
|
72
|
+
uint256 maxAmountIn,
|
|
73
|
+
uint256 minBps
|
|
74
|
+
) {
|
|
75
|
+
require(minBps <= 10_000, "minBps > 10000");
|
|
76
|
+
SWAP_ROUTER = swapRouter;
|
|
77
|
+
FIXED_TOKEN_IN = fixedTokenIn;
|
|
78
|
+
MAX_AMOUNT_IN = maxAmountIn;
|
|
79
|
+
MIN_BPS = minBps;
|
|
80
|
+
for (uint256 i = 0; i < allowedTokensOut.length; i++) {
|
|
81
|
+
isAllowedTokenOut[allowedTokensOut[i]] = true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function evaluate(bytes calldata txData, Context calldata ctx) external view returns (bool) {
|
|
86
|
+
if (txData.length < 4) return false;
|
|
87
|
+
|
|
88
|
+
// exactInputSingle: agent swaps FIXED_TOKEN_IN for an allowed output token
|
|
89
|
+
if (ctx.target == SWAP_ROUTER && ctx.selector == SEL_EXACT_INPUT_SINGLE) {
|
|
90
|
+
if (txData.length < 4 + 7 * 32) return false;
|
|
91
|
+
ExactInputSingleParams memory p = abi.decode(txData[4:], (ExactInputSingleParams));
|
|
92
|
+
if (p.tokenIn != FIXED_TOKEN_IN) return false;
|
|
93
|
+
if (!isAllowedTokenOut[p.tokenOut]) return false;
|
|
94
|
+
if (p.amountIn > MAX_AMOUNT_IN) return false;
|
|
95
|
+
// Slippage floor: amountOutMinimum ≥ amountIn × MIN_BPS / 10 000.
|
|
96
|
+
// WARNING: compares tokenOut against tokenIn base units. For same-price/same-decimal
|
|
97
|
+
// pairs this maps to a slippage %. For cross-price pairs (e.g. USDC→WETH) it is
|
|
98
|
+
// trivially satisfied — real slippage is enforced by the agent off-chain, not here.
|
|
99
|
+
if (p.amountOutMinimum < (p.amountIn * MIN_BPS) / 10_000) return false;
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// approve: agent approves the router to spend FIXED_TOKEN_IN
|
|
104
|
+
if (ctx.target == FIXED_TOKEN_IN && ctx.selector == SEL_APPROVE) {
|
|
105
|
+
if (txData.length < 4 + 2 * 32) return false;
|
|
106
|
+
(address spender, uint256 amount) = abi.decode(txData[4:], (address, uint256));
|
|
107
|
+
if (spender != SWAP_ROUTER) return false;
|
|
108
|
+
if (amount > MAX_AMOUNT_IN) return false;
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function discriminator() external pure returns (bytes32) { return DISCRIMINATOR; }
|
|
116
|
+
}
|