@dev.sail.money/sailor 0.0.2-23 → 0.0.2-24
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/examples/permissions/BoundedSupply_AaveV3_Arbitrum.sol +6 -8
- package/examples/permissions/SailCalldata.sol +118 -0
- package/package.json +1 -1
- package/packages/cli/dist/index.cjs +152 -19
- package/packages/sdk/dist/index.d.ts +1 -1
- package/packages/sdk/dist/index.d.ts.map +1 -1
- package/packages/sdk/dist/index.js +1 -1
- 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/types.d.ts +17 -1
- package/packages/sdk/dist/types.d.ts.map +1 -1
- package/packages/ui/dist/assets/{add-BcGCle88.js → add--OaWHMEX.js} +1 -1
- package/packages/ui/dist/assets/{all-wallets-8Jcfw5Qj.js → all-wallets-BH_4qsJ0.js} +1 -1
- package/packages/ui/dist/assets/{app-store-R1-af7b6.js → app-store-j8XNWdo_.js} +1 -1
- package/packages/ui/dist/assets/{apple-Cxitz1aK.js → apple-DoNsugim.js} +1 -1
- package/packages/ui/dist/assets/{arrow-bottom-n-1aGCMa.js → arrow-bottom-D_enDpNq.js} +1 -1
- package/packages/ui/dist/assets/{arrow-bottom-circle-BGQ9w6zi.js → arrow-bottom-circle-WWFGXKiz.js} +1 -1
- package/packages/ui/dist/assets/{arrow-left-BngH26cQ.js → arrow-left-BUJGpX55.js} +1 -1
- package/packages/ui/dist/assets/{arrow-right-Bd1vyPNZ.js → arrow-right-D5mkI_SK.js} +1 -1
- package/packages/ui/dist/assets/{arrow-top-nst6Ttr2.js → arrow-top-BXhKZNjN.js} +1 -1
- package/packages/ui/dist/assets/{bank-3aeQOng_.js → bank-Dkkf0tum.js} +1 -1
- package/packages/ui/dist/assets/{basic-Ds-ESc-H.js → basic-DOdQ4iGr.js} +1 -1
- package/packages/ui/dist/assets/{browser-COcw6X9p.js → browser-NOeeokaH.js} +1 -1
- package/packages/ui/dist/assets/{card-B8clsijS.js → card-cdDhvSTA.js} +1 -1
- package/packages/ui/dist/assets/{ccip-SgLYYQWq.js → ccip-Dsn_0RCo.js} +1 -1
- package/packages/ui/dist/assets/{checkmark-CxpPWG40.js → checkmark-9fzIA8S7.js} +1 -1
- package/packages/ui/dist/assets/{checkmark-bold-3THr8VIu.js → checkmark-bold-DDvnKkth.js} +1 -1
- package/packages/ui/dist/assets/{chevron-bottom-HfWcF1bE.js → chevron-bottom-CZ0cAj9w.js} +1 -1
- package/packages/ui/dist/assets/{chevron-left-CkXbcOst.js → chevron-left-cbciRp76.js} +1 -1
- package/packages/ui/dist/assets/{chevron-right-Bt7ATlLQ.js → chevron-right-aaIM8CMM.js} +1 -1
- package/packages/ui/dist/assets/{chevron-top-BZc4xycM.js → chevron-top-Cv9x0gjk.js} +1 -1
- package/packages/ui/dist/assets/{chrome-store-IT1ftwJy.js → chrome-store-DlOKVEJe.js} +1 -1
- package/packages/ui/dist/assets/{clock-DpBvmJiH.js → clock-5QviMwVt.js} +1 -1
- package/packages/ui/dist/assets/{close-BoRwHijk.js → close-myWpcxkH.js} +1 -1
- package/packages/ui/dist/assets/{coinPlaceholder-Dba1nosr.js → coinPlaceholder-D3UzHxQZ.js} +1 -1
- package/packages/ui/dist/assets/{compass-D4QnmoWi.js → compass-DN7a9rAJ.js} +1 -1
- package/packages/ui/dist/assets/{copy-iBRvR2f1.js → copy-5dhhqdUd.js} +1 -1
- package/packages/ui/dist/assets/{core-B0JxSbAV.js → core-BlknpGLl.js} +3 -3
- package/packages/ui/dist/assets/cursor-Ckhq1uuc.js +3 -0
- package/packages/ui/dist/assets/{cursor-transparent-Djp2Lulv.js → cursor-transparent-hWLSc0KZ.js} +1 -1
- package/packages/ui/dist/assets/{desktop-DF6t43QS.js → desktop-CZ-Yyu9E.js} +1 -1
- package/packages/ui/dist/assets/{disconnect-BUeUrh3r.js → disconnect-BkFpHyPA.js} +1 -1
- package/packages/ui/dist/assets/{discord-DWtvIwBI.js → discord-BQr8vCO7.js} +1 -1
- package/packages/ui/dist/assets/{etherscan-CrPMVPiO.js → etherscan-BRQnPWan.js} +1 -1
- package/packages/ui/dist/assets/{events-DC84dMPF.js → events-BPX61gpp.js} +1 -1
- package/packages/ui/dist/assets/{exclamation-triangle-9IcrwUru.js → exclamation-triangle-DgiBPDRx.js} +1 -1
- package/packages/ui/dist/assets/{extension-_qe-89Jo.js → extension-DqOhrhYM.js} +1 -1
- package/packages/ui/dist/assets/{external-link-Bsar6cmv.js → external-link-CN3oX5O9.js} +1 -1
- package/packages/ui/dist/assets/{facebook-CZnM2tIe.js → facebook-Y4EwFoke.js} +1 -1
- package/packages/ui/dist/assets/{fallback-BOfZ_bwu.js → fallback-DTghxJb4.js} +1 -1
- package/packages/ui/dist/assets/{farcaster-DyBlkt7c.js → farcaster-Bk5F07SC.js} +1 -1
- package/packages/ui/dist/assets/{filters-DwCepBxH.js → filters-Cz-QDvgT.js} +1 -1
- package/packages/ui/dist/assets/{github-DgFcdJPt.js → github-Cxkp89Tq.js} +1 -1
- package/packages/ui/dist/assets/{google-Csj-pWAm.js → google-DYqw-KYU.js} +1 -1
- package/packages/ui/dist/assets/{help-circle-CF8XrZBx.js → help-circle-BXMNYoIA.js} +1 -1
- package/packages/ui/dist/assets/{id-YqFg_Hnr.js → id-BaD71KYD.js} +1 -1
- package/packages/ui/dist/assets/{image-DvX7Dg9U.js → image-B23hGUdW.js} +1 -1
- package/packages/ui/dist/assets/{index-BiN726SD.js → index-BN6XqPDp.js} +1 -1
- package/packages/ui/dist/assets/{index-3OLndEW6.js → index-BQ4JZ1HB.js} +3 -3
- package/packages/ui/dist/assets/{index-DsS2DJdh.js → index-C3IX4alt.js} +1 -1
- package/packages/ui/dist/assets/index-CL1Pp-W8.js +1775 -0
- package/packages/ui/dist/assets/{index-Da0fOMbp.js → index-DXwK5tlD.js} +1 -1
- package/packages/ui/dist/assets/{index-BR_6qS4k.js → index-Diq2KQvQ.js} +1 -1
- package/packages/ui/dist/assets/{index.es-BiIWW5o1.js → index.es-5g6Py2iz.js} +4 -4
- package/packages/ui/dist/assets/{info-zvmQXfcd.js → info-DXuKXV9d.js} +1 -1
- package/packages/ui/dist/assets/{info-circle-poBAGMBs.js → info-circle-CYkuEbJC.js} +1 -1
- package/packages/ui/dist/assets/{lightbulb-Dce_oD8K.js → lightbulb-CuI54_aI.js} +1 -1
- package/packages/ui/dist/assets/{mail-dmvvhqN6.js → mail--8E_kt-f.js} +1 -1
- package/packages/ui/dist/assets/{metamask-sdk-BnCx3I3r.js → metamask-sdk-yzZWWcs3.js} +1 -1
- package/packages/ui/dist/assets/{mobile-B8JxvnR1.js → mobile-CZymO9zO.js} +1 -1
- package/packages/ui/dist/assets/{more-BwL7SLTo.js → more-BYAwsmOZ.js} +1 -1
- package/packages/ui/dist/assets/{network-placeholder-BGXRLaM_.js → network-placeholder-08UzyBww.js} +1 -1
- package/packages/ui/dist/assets/{nftPlaceholder-YKLlOwZU.js → nftPlaceholder-BYQcom9C.js} +1 -1
- package/packages/ui/dist/assets/{off-BdCZJUYq.js → off-CYMrTsdm.js} +1 -1
- package/packages/ui/dist/assets/{parseSignature-D9i4CKzP.js → parseSignature-WNjhiGa-.js} +1 -1
- package/packages/ui/dist/assets/{play-store-3UXzYs09.js → play-store-C8qwnge4.js} +1 -1
- package/packages/ui/dist/assets/{plus-42VUC7wg.js → plus-D44RqQir.js} +1 -1
- package/packages/ui/dist/assets/{qr-code-W99JyxAx.js → qr-code-B2Zo3ucm.js} +1 -1
- package/packages/ui/dist/assets/{recycle-horizontal-B2t10e4m.js → recycle-horizontal-GWYpxQB9.js} +1 -1
- package/packages/ui/dist/assets/{refresh-DEFqVlG3.js → refresh-MgpEHHa3.js} +1 -1
- package/packages/ui/dist/assets/{reown-logo-BpYt7vTo.js → reown-logo-CteGf0y0.js} +1 -1
- package/packages/ui/dist/assets/{search-C38Hy_cf.js → search-tUzIsCFS.js} +1 -1
- package/packages/ui/dist/assets/{secp256k1-IKD5pd64.js → secp256k1-DmFqeUJ_.js} +1 -1
- package/packages/ui/dist/assets/{send-gSwkftFg.js → send-C9UNMMEg.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontal-C1XbgPr5.js → swapHorizontal-Bhb2KCgj.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalBold-CtEf5r93.js → swapHorizontalBold-Dx8XHsp8.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalMedium-CHP_nvzn.js → swapHorizontalMedium-BU5Bg66C.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalRoundedBold-Crnco-Af.js → swapHorizontalRoundedBold-DZtiRbnr.js} +1 -1
- package/packages/ui/dist/assets/{swapVertical-Ozkl0BQE.js → swapVertical-Da4jr_Iy.js} +1 -1
- package/packages/ui/dist/assets/{telegram-BdfzLFDW.js → telegram-2JwKMtW5.js} +1 -1
- package/packages/ui/dist/assets/{three-dots-H2XRHPIG.js → three-dots-BvVnpuAg.js} +1 -1
- package/packages/ui/dist/assets/{twitch-DvbgB-BL.js → twitch-C6DOrjYX.js} +1 -1
- package/packages/ui/dist/assets/{twitterIcon-BBsTYQzn.js → twitterIcon-BQu41-nP.js} +1 -1
- package/packages/ui/dist/assets/{verify-3A_7IJxL.js → verify-CFZQJntQ.js} +1 -1
- package/packages/ui/dist/assets/{verify-filled-B4sP0yi_.js → verify-filled-Cy6vpeJk.js} +1 -1
- package/packages/ui/dist/assets/{w3m-modal-Bfua7kiP.js → w3m-modal-De9EZPA2.js} +1 -1
- package/packages/ui/dist/assets/{wallet-BbJ989Xh.js → wallet-BfWc3N5d.js} +1 -1
- package/packages/ui/dist/assets/{wallet-placeholder-CQ2v8t-c.js → wallet-placeholder-BuxnWFqL.js} +1 -1
- package/packages/ui/dist/assets/{walletconnect-BuShZt17.js → walletconnect-CjirfANF.js} +1 -1
- package/packages/ui/dist/assets/{warning-circle-DMs8QNCr.js → warning-circle-CqS0eXEs.js} +1 -1
- package/packages/ui/dist/assets/{x-DzP75KD1.js → x-DQeWAjll.js} +1 -1
- package/packages/ui/dist/index.html +1 -1
- 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/AGENTS.md +51 -2
- package/packages/ui/dist/assets/cursor-D3cYdnOt.js +0 -3
- package/packages/ui/dist/assets/index-yQSvDbVa.js +0 -1775
|
@@ -33,6 +33,7 @@ pragma solidity 0.8.26;
|
|
|
33
33
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
34
34
|
|
|
35
35
|
import {IPermission, Context} from "@sail/interfaces/IPermission.sol";
|
|
36
|
+
import {SailCalldata} from "./SailCalldata.sol";
|
|
36
37
|
|
|
37
38
|
contract BoundedSupply_AaveV3_Arbitrum is IPermission {
|
|
38
39
|
bytes32 private constant DISCRIMINATOR = keccak256("BoundedSupply_AaveV3_Arbitrum");
|
|
@@ -63,15 +64,12 @@ contract BoundedSupply_AaveV3_Arbitrum is IPermission {
|
|
|
63
64
|
if (ctx.target != AAVE_POOL) return false;
|
|
64
65
|
if (ctx.selector != SEL_SUPPLY) return false;
|
|
65
66
|
// supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode)
|
|
66
|
-
|
|
67
|
-
if (txData.length < 4 + 4 * 32) return false;
|
|
67
|
+
if (!SailCalldata.hasParams(txData, 4)) return false;
|
|
68
68
|
|
|
69
|
-
(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
/* uint16 referralCode — not bounded */
|
|
74
|
-
) = abi.decode(txData[4:], (address, uint256, address, uint16));
|
|
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
|
|
75
73
|
|
|
76
74
|
if (!isAllowedAsset[asset]) return false;
|
|
77
75
|
if (amount > MAX_SUPPLY_AMOUNT) return false;
|
|
@@ -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
|
+
}
|
package/package.json
CHANGED
|
@@ -39907,11 +39907,29 @@ init_esm2();
|
|
|
39907
39907
|
async function checkContractExists(pc, address) {
|
|
39908
39908
|
try {
|
|
39909
39909
|
const code = await pc.getCode({ address });
|
|
39910
|
-
|
|
39910
|
+
const hasCode = !!code && code !== "0x";
|
|
39911
|
+
return { address, hasCode, bytecode: hasCode ? code : void 0 };
|
|
39911
39912
|
} catch (err) {
|
|
39912
39913
|
return { address, hasCode: false, error: err.message.split("\n")[0] };
|
|
39913
39914
|
}
|
|
39914
39915
|
}
|
|
39916
|
+
function checkSelectorRoutes(calldata, bytecode) {
|
|
39917
|
+
if (calldata.length < 10) {
|
|
39918
|
+
return { selector: "", routes: null, reason: "calldata shorter than 4 bytes \u2014 no selector" };
|
|
39919
|
+
}
|
|
39920
|
+
const selector = calldata.slice(2, 10).toLowerCase();
|
|
39921
|
+
const body = bytecode.slice(2).toLowerCase();
|
|
39922
|
+
if (body.length < 100) {
|
|
39923
|
+
return { selector, routes: null, reason: "proxy or minimal contract \u2014 routing not determinable from bytecode" };
|
|
39924
|
+
}
|
|
39925
|
+
if (body.includes("360894a13ba1a321")) {
|
|
39926
|
+
return { selector, routes: null, reason: "EIP-1967 proxy detected \u2014 routing is in the implementation contract" };
|
|
39927
|
+
}
|
|
39928
|
+
if (body.includes("a3f0ad74e5423aeb")) {
|
|
39929
|
+
return { selector, routes: null, reason: "EIP-1967 beacon proxy detected \u2014 routing is in the beacon implementation" };
|
|
39930
|
+
}
|
|
39931
|
+
return { selector, routes: body.includes(selector) };
|
|
39932
|
+
}
|
|
39915
39933
|
|
|
39916
39934
|
// src/lib/permission-resolver.ts
|
|
39917
39935
|
var IPERMISSION_ABI = [
|
|
@@ -40783,14 +40801,48 @@ var MandateStore = class {
|
|
|
40783
40801
|
(m) => m.address.toLowerCase() === needle || m.name === addressOrName
|
|
40784
40802
|
);
|
|
40785
40803
|
}
|
|
40786
|
-
/**
|
|
40804
|
+
/**
|
|
40805
|
+
* Append a newly deployed mandate (replacing any prior record at the same address).
|
|
40806
|
+
* When another mandate with the same name already exists on the same chain, the
|
|
40807
|
+
* incoming mandate's name is suffixed with `[2]`, `[3]`, … to keep names unique.
|
|
40808
|
+
*/
|
|
40787
40809
|
add(mandate2) {
|
|
40788
40810
|
const data = this.read();
|
|
40789
40811
|
data.mandates = data.mandates.filter(
|
|
40790
40812
|
(m) => m.address.toLowerCase() !== mandate2.address.toLowerCase()
|
|
40791
40813
|
);
|
|
40814
|
+
const baseName = mandate2.name;
|
|
40815
|
+
const sameName = (m) => m.name === mandate2.name && m.chainId === mandate2.chainId;
|
|
40816
|
+
if (data.mandates.some(sameName)) {
|
|
40817
|
+
let n = 2;
|
|
40818
|
+
while (data.mandates.some((m) => m.name === `${baseName}[${n}]` && m.chainId === mandate2.chainId)) {
|
|
40819
|
+
n++;
|
|
40820
|
+
}
|
|
40821
|
+
mandate2 = { ...mandate2, name: `${baseName}[${n}]` };
|
|
40822
|
+
}
|
|
40792
40823
|
data.mandates.push(mandate2);
|
|
40793
40824
|
this.write(data);
|
|
40825
|
+
return mandate2;
|
|
40826
|
+
}
|
|
40827
|
+
/** Update mutable metadata fields on a tracked mandate (name, sourcePath, artifactPath). */
|
|
40828
|
+
update(addressOrName, patch) {
|
|
40829
|
+
const data = this.read();
|
|
40830
|
+
const needle = addressOrName.toLowerCase();
|
|
40831
|
+
const mandate2 = data.mandates.find(
|
|
40832
|
+
(m) => m.address.toLowerCase() === needle || m.name === addressOrName
|
|
40833
|
+
);
|
|
40834
|
+
if (!mandate2) throw new Error(`No tracked mandate found for: ${addressOrName}`);
|
|
40835
|
+
if (patch.name !== void 0 && patch.name !== mandate2.name) {
|
|
40836
|
+
const conflict = data.mandates.find(
|
|
40837
|
+
(m) => m.name === patch.name && m.chainId === mandate2.chainId && m.address.toLowerCase() !== mandate2.address.toLowerCase()
|
|
40838
|
+
);
|
|
40839
|
+
if (conflict) throw new Error(`Name "${patch.name}" is already used by ${conflict.address} on chain ${mandate2.chainId}`);
|
|
40840
|
+
mandate2.name = patch.name;
|
|
40841
|
+
}
|
|
40842
|
+
if (patch.sourcePath !== void 0) mandate2.sourcePath = patch.sourcePath;
|
|
40843
|
+
if (patch.artifactPath !== void 0) mandate2.artifactPath = patch.artifactPath;
|
|
40844
|
+
this.write(data);
|
|
40845
|
+
return mandate2;
|
|
40794
40846
|
}
|
|
40795
40847
|
/** Record that a tracked mandate was attached to an SMA. */
|
|
40796
40848
|
recordAttachment(address, attachment) {
|
|
@@ -41405,7 +41457,18 @@ async function runDeploy(project, channel, options) {
|
|
|
41405
41457
|
);
|
|
41406
41458
|
}
|
|
41407
41459
|
const { abi: abi2, bytecode, contractName, artifactPath } = resolveArtifact(options);
|
|
41408
|
-
|
|
41460
|
+
let argsJson;
|
|
41461
|
+
if (options.argsFile) {
|
|
41462
|
+
const argsFilePath = (0, import_node_path9.resolve)(options.argsFile);
|
|
41463
|
+
try {
|
|
41464
|
+
argsJson = (0, import_node_fs10.readFileSync)(argsFilePath, "utf8").trim();
|
|
41465
|
+
} catch {
|
|
41466
|
+
throw new Error(`Cannot read --args-file: ${argsFilePath}`);
|
|
41467
|
+
}
|
|
41468
|
+
} else {
|
|
41469
|
+
argsJson = options.args;
|
|
41470
|
+
}
|
|
41471
|
+
const args = coerceConstructorArgs(abi2, argsJson);
|
|
41409
41472
|
const deployData = encodeDeployData({ abi: abi2, bytecode, args });
|
|
41410
41473
|
const chainId = project.chainId;
|
|
41411
41474
|
const publicClient = publicClientFor(project);
|
|
@@ -41428,7 +41491,10 @@ async function runDeploy(project, channel, options) {
|
|
|
41428
41491
|
data: deployData,
|
|
41429
41492
|
details: [
|
|
41430
41493
|
{ label: "Contract", value: contractName },
|
|
41431
|
-
{
|
|
41494
|
+
{
|
|
41495
|
+
label: options.argsFile ? "Constructor args (from file)" : "Constructor args",
|
|
41496
|
+
value: argsJson ? argsJson : "(none)"
|
|
41497
|
+
}
|
|
41432
41498
|
]
|
|
41433
41499
|
});
|
|
41434
41500
|
if (response.status === "rejected") {
|
|
@@ -41454,13 +41520,13 @@ async function runDeploy(project, channel, options) {
|
|
|
41454
41520
|
deployedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
41455
41521
|
};
|
|
41456
41522
|
const store = new MandateStore();
|
|
41457
|
-
store.add(record);
|
|
41458
|
-
say(() => console.log("Tracked in .sail/state/mandates.json"));
|
|
41523
|
+
const stored = store.add(record);
|
|
41524
|
+
say(() => console.log("Tracked in .sail/state/mandates.json" + (stored.name !== record.name ? ` as "${stored.name}"` : "")));
|
|
41459
41525
|
appendActivity({
|
|
41460
41526
|
ts: nowIso(),
|
|
41461
41527
|
actor: "owner",
|
|
41462
41528
|
type: "mandate_deployed",
|
|
41463
|
-
name:
|
|
41529
|
+
name: stored.name,
|
|
41464
41530
|
address: deployed,
|
|
41465
41531
|
txHash: response.txHash,
|
|
41466
41532
|
chainId
|
|
@@ -41474,7 +41540,7 @@ async function runDeploy(project, channel, options) {
|
|
|
41474
41540
|
publicClient,
|
|
41475
41541
|
sma,
|
|
41476
41542
|
deployed,
|
|
41477
|
-
|
|
41543
|
+
stored.name,
|
|
41478
41544
|
json
|
|
41479
41545
|
);
|
|
41480
41546
|
store.recordAttachment(deployed, { sma, txHash: attachTxHash });
|
|
@@ -41489,7 +41555,7 @@ Register it later with: sailor mandate attach --address ${deployed} --sma <SMA>`
|
|
|
41489
41555
|
emit(json, () => {
|
|
41490
41556
|
}, {
|
|
41491
41557
|
status: "ok",
|
|
41492
|
-
mandate: { name:
|
|
41558
|
+
mandate: { name: stored.name, address: deployed, txHash: response.txHash, chainId },
|
|
41493
41559
|
attached: options.attach ? { sma: getAddress(options.sma), txHash: attachTxHash } : null
|
|
41494
41560
|
});
|
|
41495
41561
|
}
|
|
@@ -41751,7 +41817,7 @@ Connect the owner wallet (mandate signer) in the browser \u2014 the agent wallet
|
|
|
41751
41817
|
}
|
|
41752
41818
|
say(() => console.log("\u2713", `Deployed + registered ${spec.label} at ${clone}`));
|
|
41753
41819
|
const store = new MandateStore();
|
|
41754
|
-
store.add({
|
|
41820
|
+
const storedClone = store.add({
|
|
41755
41821
|
name: label,
|
|
41756
41822
|
address: clone,
|
|
41757
41823
|
txHash,
|
|
@@ -41764,7 +41830,7 @@ Connect the owner wallet (mandate signer) in the browser \u2014 the agent wallet
|
|
|
41764
41830
|
actor: "agent",
|
|
41765
41831
|
type: "permission_registered",
|
|
41766
41832
|
permission: clone,
|
|
41767
|
-
name:
|
|
41833
|
+
name: storedClone.name,
|
|
41768
41834
|
sma,
|
|
41769
41835
|
txHash,
|
|
41770
41836
|
chainId: project.chainId
|
|
@@ -42085,6 +42151,21 @@ function mandateContractsList() {
|
|
|
42085
42151
|
}
|
|
42086
42152
|
}
|
|
42087
42153
|
}
|
|
42154
|
+
function mandateUpdate(options) {
|
|
42155
|
+
const { address, name, sourcePath, artifactPath, json } = options;
|
|
42156
|
+
if (!name && !sourcePath && !artifactPath) {
|
|
42157
|
+
throw new Error("Provide at least one of --name, --source-path, or --artifact-path");
|
|
42158
|
+
}
|
|
42159
|
+
const store = new MandateStore();
|
|
42160
|
+
const updated = store.update(address, { name, sourcePath, artifactPath });
|
|
42161
|
+
emit(!!json, () => {
|
|
42162
|
+
const changes = [];
|
|
42163
|
+
if (name) changes.push(`name \u2192 ${updated.name}`);
|
|
42164
|
+
if (sourcePath) changes.push(`sourcePath \u2192 ${updated.sourcePath}`);
|
|
42165
|
+
if (artifactPath) changes.push(`artifactPath \u2192 ${updated.artifactPath}`);
|
|
42166
|
+
console.log(`Updated ${updated.address}: ${changes.join(", ")}`);
|
|
42167
|
+
}, { status: "ok", mandate: updated });
|
|
42168
|
+
}
|
|
42088
42169
|
function resolveArtifact(options) {
|
|
42089
42170
|
let artifactPath = options.artifact;
|
|
42090
42171
|
let contractName = options.contract ?? options.name ?? "";
|
|
@@ -42094,7 +42175,7 @@ function resolveArtifact(options) {
|
|
|
42094
42175
|
}
|
|
42095
42176
|
const resolved = (0, import_node_path9.resolve)(artifactPath);
|
|
42096
42177
|
const projectRoot = (0, import_node_path9.resolve)(process.cwd());
|
|
42097
|
-
if (!resolved.startsWith(projectRoot +
|
|
42178
|
+
if (!resolved.startsWith(projectRoot + import_node_path9.sep) && resolved !== projectRoot) {
|
|
42098
42179
|
throw new Error(
|
|
42099
42180
|
`Artifact path must be inside the project directory.
|
|
42100
42181
|
Resolved: ${resolved}`
|
|
@@ -42539,6 +42620,7 @@ async function mandateSimulate(options) {
|
|
|
42539
42620
|
checkContractExists(pc, c.target)
|
|
42540
42621
|
]);
|
|
42541
42622
|
const result = probe.accepted ? "pass" : "fail";
|
|
42623
|
+
const selectorCheck = codeCheck.hasCode && codeCheck.bytecode ? checkSelectorRoutes(c.data, codeCheck.bytecode) : { selector: "", routes: null, reason: codeCheck.hasCode ? "bytecode unavailable" : void 0 };
|
|
42542
42624
|
return {
|
|
42543
42625
|
index: i,
|
|
42544
42626
|
label: c.label,
|
|
@@ -42550,7 +42632,10 @@ async function mandateSimulate(options) {
|
|
|
42550
42632
|
expect: c.expect ?? null,
|
|
42551
42633
|
match: c.expect ? c.expect === result : null,
|
|
42552
42634
|
targetHasCode: codeCheck.hasCode,
|
|
42553
|
-
targetCheckError: codeCheck.error
|
|
42635
|
+
targetCheckError: codeCheck.error,
|
|
42636
|
+
selectorRoutes: selectorCheck.routes,
|
|
42637
|
+
selector: selectorCheck.selector,
|
|
42638
|
+
selectorRoutesReason: selectorCheck.reason
|
|
42554
42639
|
};
|
|
42555
42640
|
})
|
|
42556
42641
|
);
|
|
@@ -42581,7 +42666,10 @@ async function mandateSimulate(options) {
|
|
|
42581
42666
|
expect: r.expect,
|
|
42582
42667
|
match: r.match,
|
|
42583
42668
|
targetHasCode: r.targetHasCode,
|
|
42584
|
-
targetCheckError: r.targetCheckError
|
|
42669
|
+
targetCheckError: r.targetCheckError,
|
|
42670
|
+
selector: r.selector,
|
|
42671
|
+
selectorRoutes: r.selectorRoutes,
|
|
42672
|
+
selectorRoutesReason: r.selectorRoutesReason
|
|
42585
42673
|
})),
|
|
42586
42674
|
mismatches: mismatches.length,
|
|
42587
42675
|
noCodeTargets: noCodeTargets.map((r) => r.target),
|
|
@@ -42607,7 +42695,9 @@ async function mandateSimulate(options) {
|
|
|
42607
42695
|
const expectStr = r.expect === null ? "" : r.match ? ` expected ${r.expect} \u2713 MATCH` : ` expected ${r.expect} \u2717 MISMATCH`;
|
|
42608
42696
|
console.log(`[${r.index + 1}] ${verdict} ${r.label}${expectStr}`);
|
|
42609
42697
|
const codeNote = r.targetCheckError ? `\u26A0 could not verify contract code (${r.targetCheckError})` : r.targetHasCode ? "\u2713 contract present" : `\u26A0 NO contract code on chain ${chainId} \u2014 this call would fail on-chain regardless of the permission`;
|
|
42698
|
+
const selectorNote = r.selectorRoutes === true ? `\u2713 selector 0x${r.selector} routes` : r.selectorRoutes === false ? `\u26A0 selector 0x${r.selector} NOT found in bytecode \u2014 call would likely revert with unknown selector` : r.selectorRoutesReason ? `~ selector check skipped (${r.selectorRoutesReason})` : null;
|
|
42610
42699
|
console.log(` target ${r.target} ${codeNote}`);
|
|
42700
|
+
if (selectorNote) console.log(` ${selectorNote}`);
|
|
42611
42701
|
if (r.reverted && r.revertReason) {
|
|
42612
42702
|
console.log(` evaluate() reverted: ${r.revertReason}`);
|
|
42613
42703
|
}
|
|
@@ -43225,13 +43315,30 @@ function isProcessAlive(pid) {
|
|
|
43225
43315
|
// src/commands/run.ts
|
|
43226
43316
|
var DEFAULT_INTERVAL_SEC = 60;
|
|
43227
43317
|
var sleep2 = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
43228
|
-
var
|
|
43318
|
+
var ERC20_READ_ABI = [
|
|
43229
43319
|
{
|
|
43230
43320
|
type: "function",
|
|
43231
43321
|
name: "balanceOf",
|
|
43232
43322
|
stateMutability: "view",
|
|
43233
43323
|
inputs: [{ name: "account", type: "address" }],
|
|
43234
43324
|
outputs: [{ name: "", type: "uint256" }]
|
|
43325
|
+
},
|
|
43326
|
+
{
|
|
43327
|
+
type: "function",
|
|
43328
|
+
name: "allowance",
|
|
43329
|
+
stateMutability: "view",
|
|
43330
|
+
inputs: [
|
|
43331
|
+
{ name: "owner", type: "address" },
|
|
43332
|
+
{ name: "spender", type: "address" }
|
|
43333
|
+
],
|
|
43334
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
43335
|
+
},
|
|
43336
|
+
{
|
|
43337
|
+
type: "function",
|
|
43338
|
+
name: "decimals",
|
|
43339
|
+
stateMutability: "view",
|
|
43340
|
+
inputs: [],
|
|
43341
|
+
outputs: [{ name: "", type: "uint8" }]
|
|
43235
43342
|
}
|
|
43236
43343
|
];
|
|
43237
43344
|
function loadAgentData(filePath) {
|
|
@@ -43251,7 +43358,8 @@ async function loadAgent() {
|
|
|
43251
43358
|
let mod2;
|
|
43252
43359
|
if (abs.endsWith(".ts")) {
|
|
43253
43360
|
const { tsImport } = await import("tsx/esm/api");
|
|
43254
|
-
|
|
43361
|
+
const absUrl = (0, import_node_url.pathToFileURL)(abs).href;
|
|
43362
|
+
mod2 = await tsImport(absUrl, absUrl);
|
|
43255
43363
|
} else {
|
|
43256
43364
|
mod2 = await import((0, import_node_url.pathToFileURL)(abs).href);
|
|
43257
43365
|
}
|
|
@@ -43378,11 +43486,30 @@ Configure the chain in @sail/chains or set KERNEL_ADDRESS in .sail/.env.local.`
|
|
|
43378
43486
|
}
|
|
43379
43487
|
return publicClient.readContract({
|
|
43380
43488
|
address: token,
|
|
43381
|
-
abi:
|
|
43489
|
+
abi: ERC20_READ_ABI,
|
|
43382
43490
|
functionName: "balanceOf",
|
|
43383
43491
|
args: [accountAddr]
|
|
43384
43492
|
});
|
|
43385
43493
|
};
|
|
43494
|
+
const readAllowance = (token, owner2, spender) => publicClient.readContract({
|
|
43495
|
+
address: token,
|
|
43496
|
+
abi: ERC20_READ_ABI,
|
|
43497
|
+
functionName: "allowance",
|
|
43498
|
+
args: [owner2, spender]
|
|
43499
|
+
});
|
|
43500
|
+
const decimalsCache = /* @__PURE__ */ new Map();
|
|
43501
|
+
const readDecimals = async (token) => {
|
|
43502
|
+
const key = getAddress(token);
|
|
43503
|
+
const cached = decimalsCache.get(key);
|
|
43504
|
+
if (cached !== void 0) return cached;
|
|
43505
|
+
const d = await publicClient.readContract({
|
|
43506
|
+
address: token,
|
|
43507
|
+
abi: ERC20_READ_ABI,
|
|
43508
|
+
functionName: "decimals"
|
|
43509
|
+
});
|
|
43510
|
+
decimalsCache.set(key, d);
|
|
43511
|
+
return d;
|
|
43512
|
+
};
|
|
43386
43513
|
async function runTick() {
|
|
43387
43514
|
appendActivity({ ts: nowIso(), actor: "agent", type: "tick_start" });
|
|
43388
43515
|
let blockInfo = { number: 0n, timestamp: 0n };
|
|
@@ -43412,10 +43539,15 @@ Configure the chain in @sail/chains or set KERNEL_ADDRESS in .sail/.env.local.`
|
|
|
43412
43539
|
dispatch: execClient.dispatch,
|
|
43413
43540
|
strategy: execClient.strategy
|
|
43414
43541
|
}),
|
|
43542
|
+
publicClient,
|
|
43415
43543
|
manager: agentManager,
|
|
43416
43544
|
log,
|
|
43417
43545
|
data: agentData,
|
|
43418
|
-
read: {
|
|
43546
|
+
read: {
|
|
43547
|
+
balance: readBalance,
|
|
43548
|
+
allowance: readAllowance,
|
|
43549
|
+
decimals: readDecimals
|
|
43550
|
+
}
|
|
43419
43551
|
};
|
|
43420
43552
|
let dispatches;
|
|
43421
43553
|
try {
|
|
@@ -44040,7 +44172,7 @@ account.command("rotate-signer").description("Rotate the SMA's delegated signer
|
|
|
44040
44172
|
var mandate = program2.command("mandate").description("Manage mandates");
|
|
44041
44173
|
mandate.command("prepare").description("Prepare a mandate draft for review and signing in the UI (MetaMask)").action(action(mandatePrepare));
|
|
44042
44174
|
mandate.command("sign").description("Review and confirm the permissions authorized for your SMA").option("--yes", "Skip the confirmation prompt (for non-interactive / CI use)").action(actionWith(mandateSign));
|
|
44043
|
-
mandate.command("deploy").description("Deploy a Foundry-compiled permission contract via the browser signing UI").option("--artifact <path>", "Path to the Foundry artifact JSON (out/<Name>.sol/<Name>.json)").option("--contract <name>", "Contract name; resolves to <out>/<name>.sol/<name>.json").option("--out <dir>", "Foundry output directory", "out").option("--name <label>", "Label to track this permission under (defaults to contract name)").option("--args <json>", `Constructor args as
|
|
44175
|
+
mandate.command("deploy").description("Deploy a Foundry-compiled permission contract via the browser signing UI").option("--artifact <path>", "Path to the Foundry artifact JSON (out/<Name>.sol/<Name>.json)").option("--contract <name>", "Contract name; resolves to <out>/<name>.sol/<name>.json").option("--out <dir>", "Foundry output directory", "out").option("--name <label>", "Label to track this permission under (defaults to contract name)").option("--args <json>", `Constructor args as JSON array. Bash: '["0x..","1"]'. PowerShell: '[\\"0x..\\",\\"1\\"]'. Use --args-file to avoid quoting.`).option("--args-file <path>", "Path to a JSON file containing constructor args array (recommended on PowerShell)").option("--build", "Run `forge build` before deploying").option("--attach", "After deploy, register the permission on --sma").option("--sma <address>", "SMA to register on (required with --attach)").option("--json", "Emit machine-readable JSON").action(actionWith(mandateDeploy));
|
|
44044
44176
|
mandate.command("attach").description("Register an already-deployed permission on an SMA (EIP-712 RegisterPermission)").requiredOption("--address <mandateOrName>", "Permission address, or a name tracked locally").requiredOption("--sma <address>", "SMA to register the permission on").option("--label <label>", "Human-readable label shown in the signing UI").option("--json", "Emit machine-readable JSON").action(actionWith(mandateAttach));
|
|
44045
44177
|
mandate.command("deploy-clone").description("Deploy + register a standalone clone permission (e.g. boundedApprove) via the signing UI").requiredOption("--template <key>", "Standalone clone template key (e.g. boundedApprove)").requiredOption("--sma <address>", "SMA to deploy the clone for and register it on").option("--tokens <csv>", "Comma-separated allowed token addresses").option("--spenders <csv>", "Comma-separated allowed spender addresses").option("--max <amount>", "Max amount per tx in base units (default: uint256 max)").option("--label <label>", "Human-readable label to track this permission under").option("--json", "Emit machine-readable JSON").action(actionWith(mandateDeployClone));
|
|
44046
44178
|
mandate.command("revoke").description("Revoke permission(s) from an SMA (EIP-712 RevokePermissions, owner-authorized)").option("--address <permissionOrName>", "Permission address, or a name tracked locally").requiredOption("--sma <address>", "Safe (SMA) to revoke the permission(s) from").option("--all", "Revoke every permission currently registered on the SMA").option("--json", "Output JSON").action(actionWith(mandateRevoke));
|
|
@@ -44048,6 +44180,7 @@ mandate.command("templates").description("Show how to author your own permission
|
|
|
44048
44180
|
mandate.command("simulate").description(
|
|
44049
44181
|
"Probe a permission against sample calls off-chain (eth_call, NO gas) \u2014 prove it accepts the calls you want and rejects the ones you don't, before authorizing on-chain"
|
|
44050
44182
|
).requiredOption("--address <permissionOrName>", "Permission to probe (address or tracked name)").option("--sma <address>", "SMA to probe as (ctx.account; defaults to .sail/account.json)").option("--target <address>", "Inline single call: target contract address").option("--calldata <hex>", "Inline single call: 0x-prefixed calldata").option("--value <wei>", "Inline single call: ETH value in wei (default 0)").option("--expect <pass|fail>", "Inline single call: expected outcome (sets non-zero exit on mismatch)").option("--label <text>", "Inline single call: human-readable label").option("--calls <file>", "Batch: JSON array of { target, calldata, value?, expect?, label? }").option("--json", "Emit machine-readable JSON").action(actionWith(mandateSimulate));
|
|
44183
|
+
mandate.command("update").description("Update metadata for a tracked permission contract (rename, source path, artifact path)").requiredOption("--address <mandateOrName>", "Permission address or tracked name to update").option("--name <label>", "New tracking label (must be unique within the same chain)").option("--source-path <path>", "Update the relative path to the Solidity source file").option("--artifact-path <path>", "Update the relative path to the Foundry artifact JSON").option("--json", "Emit machine-readable JSON").action(actionWith(mandateUpdate));
|
|
44051
44184
|
mandate.command("list").description("List permission contracts deployed from this project").action(action(async () => mandateContractsList()));
|
|
44052
44185
|
program2.command("onboard").description("Set up an SMA, register a permission, confirm the agent is operational").option("--sma <address>", "Use a specific SMA address instead of prompting").option("--new-sma", "Create a new SMA via SailKernel").option("--salt <n>", "CREATE2 salt for deterministic Safe address (default: 0; use 0 for first SMA, increment for subsequent)").option("--template <kindOrAddress>", "Register this permission contract (kind, label, or address)").option("--skip-mandate", "Skip the permission registration step").option("--json", "Emit machine-readable JSON (implies non-interactive)").action(actionWith(onboard));
|
|
44053
44186
|
var station = program2.command("station").description("Manage the persistent signing station (browser signing daemon)");
|
|
@@ -17,7 +17,7 @@ export { KERNEL_ERROR_ABI, KERNEL_ERROR_SIGNATURES, decodeKernelError, explainKe
|
|
|
17
17
|
export { DISPATCH_EIP712_FIELDS, REGISTER_PERMISSION_TYPES, REGISTER_PERMISSION_TYPES_NO_DEADLINE, REGISTER_PERMISSIONS_BATCH_TYPES, buildDispatchSignature, buildRegisterPermissionTypedData, buildRegisterPermissionsBatchTypedData, sailKernelDomain, signRegisterPermission, } from "./eip712.js";
|
|
18
18
|
export { estimatePermissionFee } from "./fees.js";
|
|
19
19
|
export type { FetchLifiQuoteParams, LifiSwapQuote } from "./lifi.js";
|
|
20
|
-
export { DEFAULT_SLIPPAGE, LIFI_QUOTE_URL, LIFI_ROUTERS, encodeApprove, fetchLifiQuote, } from "./lifi.js";
|
|
20
|
+
export { DEFAULT_SLIPPAGE, LIFI_QUOTE_URL, LIFI_ROUTERS, encodeApprove, fetchLifiQuote, minTokenOut, } from "./lifi.js";
|
|
21
21
|
export type { AllocationItem, AllocationRequest, AllocationResponse, BenchmarkResponse, ComparePosition, CompareRequest, CompareResponse, ExplainRequest, ExplainResponse, InstitutionalRequest, InstitutionalResponse, OpportunitiesResponse, PortfolioCheckResponse, RebalanceRequest, RebalanceResponse, RisksSummaryResponse, SafeCheckResponse, SailIntelligenceOptions, ScreenRequest, ScreenResponse, ValidateRequest, ValidateResponse, VaultRiskResponse, VaultScreenResult, YieldOpportunity, YieldSourceItem, YieldSourcesResponse, } from "./intelligence.js";
|
|
22
22
|
export { SAIL_INTELLIGENCE_BASE_URL, SAIL_INTELLIGENCE_DOCS_URL, SailIntelligence, } from "./intelligence.js";
|
|
23
23
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,YAAY,EACV,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,SAAS,EACT,0BAA0B,EAC1B,yBAAyB,EACzB,8BAA8B,EAC9B,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,qBAAqB,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAC;AACrF,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,gCAAgC,EAChC,8BAA8B,EAC9B,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,qCAAqC,EACrC,gCAAgC,EAChC,sBAAsB,EACtB,gCAAgC,EAChC,sCAAsC,EACtC,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAClD,YAAY,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,YAAY,EACV,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,SAAS,EACT,0BAA0B,EAC1B,yBAAyB,EACzB,8BAA8B,EAC9B,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,qBAAqB,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAC;AACrF,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,cAAc,GACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,gCAAgC,EAChC,8BAA8B,EAC9B,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,qCAAqC,EACrC,gCAAgC,EAChC,sBAAsB,EACtB,gCAAgC,EAChC,sCAAsC,EACtC,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAClD,YAAY,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,cAAc,EACd,WAAW,GACZ,MAAM,WAAW,CAAC;AAGnB,YAAY,EACV,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,eAAe,EACf,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,uBAAuB,EACvB,aAAa,EACb,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,gBAAgB,GACjB,MAAM,mBAAmB,CAAC"}
|
|
@@ -11,6 +11,6 @@ export { DISPATCH_TYPE_STRINGS, DISPATCH_TYPEHASHES, REGISTER_PERMISSION_TYPE_ST
|
|
|
11
11
|
export { KERNEL_ERROR_ABI, KERNEL_ERROR_SIGNATURES, decodeKernelError, explainKernelRevert, } from "./errors.js";
|
|
12
12
|
export { DISPATCH_EIP712_FIELDS, REGISTER_PERMISSION_TYPES, REGISTER_PERMISSION_TYPES_NO_DEADLINE, REGISTER_PERMISSIONS_BATCH_TYPES, buildDispatchSignature, buildRegisterPermissionTypedData, buildRegisterPermissionsBatchTypedData, sailKernelDomain, signRegisterPermission, } from "./eip712.js";
|
|
13
13
|
export { estimatePermissionFee } from "./fees.js";
|
|
14
|
-
export { DEFAULT_SLIPPAGE, LIFI_QUOTE_URL, LIFI_ROUTERS, encodeApprove, fetchLifiQuote, } from "./lifi.js";
|
|
14
|
+
export { DEFAULT_SLIPPAGE, LIFI_QUOTE_URL, LIFI_ROUTERS, encodeApprove, fetchLifiQuote, minTokenOut, } from "./lifi.js";
|
|
15
15
|
export { SAIL_INTELLIGENCE_BASE_URL, SAIL_INTELLIGENCE_DOCS_URL, SailIntelligence, } from "./intelligence.js";
|
|
16
16
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAc7D,OAAO,EACL,SAAS,EACT,0BAA0B,EAC1B,yBAAyB,EACzB,8BAA8B,EAC9B,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,qBAAqB,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAC;AAQrF,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,gCAAgC,EAChC,8BAA8B,EAC9B,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,qCAAqC,EACrC,gCAAgC,EAChC,sBAAsB,EACtB,gCAAgC,EAChC,sCAAsC,EACtC,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAElD,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAc7D,OAAO,EACL,SAAS,EACT,0BAA0B,EAC1B,yBAAyB,EACzB,8BAA8B,EAC9B,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,qBAAqB,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAC;AAQrF,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,gCAAgC,EAChC,8BAA8B,EAC9B,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,qCAAqC,EACrC,gCAAgC,EAChC,sBAAsB,EACtB,gCAAgC,EAChC,sCAAsC,EACtC,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAElD,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,aAAa,EACb,cAAc,EACd,WAAW,GACZ,MAAM,WAAW,CAAC;AAgCnB,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,EAC1B,gBAAgB,GACjB,MAAM,mBAAmB,CAAC"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Do not edit manually — run `pnpm build` to regenerate.
|
|
6
6
|
*
|
|
7
7
|
* Spec version : 1.2.0
|
|
8
|
-
* Generated at : 2026-06-
|
|
8
|
+
* Generated at : 2026-06-10T16:18:17.533Z
|
|
9
9
|
*/
|
|
10
10
|
export declare const SAIL_INTELLIGENCE_BASE_URL = "https://api.sail.money";
|
|
11
11
|
export declare const SAIL_INTELLIGENCE_DOCS_URL = "https://api.sail.money/docs";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Do not edit manually — run `pnpm build` to regenerate.
|
|
6
6
|
*
|
|
7
7
|
* Spec version : 1.2.0
|
|
8
|
-
* Generated at : 2026-06-
|
|
8
|
+
* Generated at : 2026-06-10T16:18:17.533Z
|
|
9
9
|
*/
|
|
10
10
|
export const SAIL_INTELLIGENCE_BASE_URL = "https://api.sail.money";
|
|
11
11
|
export const SAIL_INTELLIGENCE_DOCS_URL = "https://api.sail.money/docs";
|
|
@@ -75,6 +75,23 @@ export type FetchLifiQuoteParams = {
|
|
|
75
75
|
/** Optional fetch implementation (defaults to global fetch). */
|
|
76
76
|
fetchFn?: typeof fetch;
|
|
77
77
|
};
|
|
78
|
+
/**
|
|
79
|
+
* Compute the minimum acceptable output amount for a swap, given a quoted
|
|
80
|
+
* `toAmount` and a slippage tolerance.
|
|
81
|
+
*
|
|
82
|
+
* Use this to enforce slippage protection when building dispatch calldata —
|
|
83
|
+
* pass the result as `amountOutMinimum` (Uniswap) or `minAmount` (LiFi).
|
|
84
|
+
*
|
|
85
|
+
* @param toAmount Expected output in the destination token's base units
|
|
86
|
+
* (from `LifiSwapQuote.toAmount` or a Quoter call).
|
|
87
|
+
* @param slippage Maximum acceptable price movement as a fraction (0.03 = 3%).
|
|
88
|
+
* Defaults to `DEFAULT_SLIPPAGE` (3%).
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* const quote = await fetchLifiQuote({ ... });
|
|
92
|
+
* const minOut = minTokenOut(quote.toAmount, 0.005); // 0.5% slippage
|
|
93
|
+
*/
|
|
94
|
+
export declare function minTokenOut(toAmount: bigint, slippage?: number): bigint;
|
|
78
95
|
/**
|
|
79
96
|
* Fetch an executable LiFi quote for a same-chain swap. Throws with the API's
|
|
80
97
|
* error body when the quote is unavailable or malformed.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifi.d.ts","sourceRoot":"","sources":["../src/lifi.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAsB,KAAK,GAAG,EAAY,MAAM,MAAM,CAAC;AAE5E,2BAA2B;AAC3B,eAAO,MAAM,cAAc,8BAA8B,CAAC;AAE1D;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAIhD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAOrC,2CAA2C;AAC3C,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;EAAY,CAAC;AAE7C,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,CAEnE;AAED,0DAA0D;AAC1D,MAAM,MAAM,aAAa,GAAG;IAC1B,uDAAuD;IACvD,MAAM,EAAE,OAAO,CAAC;IAChB,qBAAqB;IACrB,IAAI,EAAE,GAAG,CAAC;IACV,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,8CAA8C;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB,CAAC;AAEF;;;GAGG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC,CAoCzF"}
|
|
1
|
+
{"version":3,"file":"lifi.d.ts","sourceRoot":"","sources":["../src/lifi.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,OAAO,EAAsB,KAAK,GAAG,EAAY,MAAM,MAAM,CAAC;AAE5E,2BAA2B;AAC3B,eAAO,MAAM,cAAc,8BAA8B,CAAC;AAE1D;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAIhD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAOrC,2CAA2C;AAC3C,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;EAAY,CAAC;AAE7C,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,CAEnE;AAED,0DAA0D;AAC1D,MAAM,MAAM,aAAa,GAAG;IAC1B,uDAAuD;IACvD,MAAM,EAAE,OAAO,CAAC;IAChB,qBAAqB;IACrB,IAAI,EAAE,GAAG,CAAC;IACV,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,8CAA8C;IAC9C,UAAU,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAyB,GAAG,MAAM,CAMzF;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC,CAoCzF"}
|
|
@@ -32,6 +32,30 @@ export const ERC20_ALLOWANCE_ABI = ERC20_ABI;
|
|
|
32
32
|
export function encodeApprove(spender, amount) {
|
|
33
33
|
return encodeFunctionData({ abi: ERC20_ABI, functionName: "approve", args: [spender, amount] });
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* Compute the minimum acceptable output amount for a swap, given a quoted
|
|
37
|
+
* `toAmount` and a slippage tolerance.
|
|
38
|
+
*
|
|
39
|
+
* Use this to enforce slippage protection when building dispatch calldata —
|
|
40
|
+
* pass the result as `amountOutMinimum` (Uniswap) or `minAmount` (LiFi).
|
|
41
|
+
*
|
|
42
|
+
* @param toAmount Expected output in the destination token's base units
|
|
43
|
+
* (from `LifiSwapQuote.toAmount` or a Quoter call).
|
|
44
|
+
* @param slippage Maximum acceptable price movement as a fraction (0.03 = 3%).
|
|
45
|
+
* Defaults to `DEFAULT_SLIPPAGE` (3%).
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* const quote = await fetchLifiQuote({ ... });
|
|
49
|
+
* const minOut = minTokenOut(quote.toAmount, 0.005); // 0.5% slippage
|
|
50
|
+
*/
|
|
51
|
+
export function minTokenOut(toAmount, slippage = DEFAULT_SLIPPAGE) {
|
|
52
|
+
if (slippage < 0 || slippage >= 1)
|
|
53
|
+
throw new Error(`slippage must be in [0, 1) — got ${slippage}`);
|
|
54
|
+
// Scale to 1e6 precision to avoid floating-point truncation on small amounts.
|
|
55
|
+
const scale = 1000000n;
|
|
56
|
+
const keepFraction = BigInt(Math.round((1 - slippage) * 1_000_000));
|
|
57
|
+
return (toAmount * keepFraction) / scale;
|
|
58
|
+
}
|
|
35
59
|
/**
|
|
36
60
|
* Fetch an executable LiFi quote for a same-chain swap. Throws with the API's
|
|
37
61
|
* error body when the quote is unavailable or malformed.
|