@continuumdao/ctm-mpc-defi 0.2.26 → 0.2.28

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.
Files changed (38) hide show
  1. package/dist/agent/catalog.cjs +681 -46
  2. package/dist/agent/catalog.cjs.map +1 -1
  3. package/dist/agent/catalog.d.ts +1209 -21
  4. package/dist/agent/catalog.js +668 -47
  5. package/dist/agent/catalog.js.map +1 -1
  6. package/dist/agent/skills/morpho/SKILL.md +38 -6
  7. package/dist/agent/skills/uniswap-v4/SKILL.md +15 -0
  8. package/dist/core/index.cjs +19 -0
  9. package/dist/core/index.cjs.map +1 -1
  10. package/dist/core/index.d.ts +8 -1
  11. package/dist/core/index.js +17 -1
  12. package/dist/core/index.js.map +1 -1
  13. package/dist/index.cjs +363 -22
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.js +361 -23
  17. package/dist/index.js.map +1 -1
  18. package/dist/protocols/evm/aave-v4/index.cjs.map +1 -1
  19. package/dist/protocols/evm/aave-v4/index.js.map +1 -1
  20. package/dist/protocols/evm/euler-v2/index.cjs.map +1 -1
  21. package/dist/protocols/evm/euler-v2/index.js.map +1 -1
  22. package/dist/protocols/evm/maple/index.cjs.map +1 -1
  23. package/dist/protocols/evm/maple/index.js.map +1 -1
  24. package/dist/protocols/evm/morpho/index.cjs +1153 -142
  25. package/dist/protocols/evm/morpho/index.cjs.map +1 -1
  26. package/dist/protocols/evm/morpho/index.d.ts +314 -8
  27. package/dist/protocols/evm/morpho/index.js +1131 -144
  28. package/dist/protocols/evm/morpho/index.js.map +1 -1
  29. package/dist/protocols/evm/permit2/index.cjs.map +1 -1
  30. package/dist/protocols/evm/permit2/index.js.map +1 -1
  31. package/dist/protocols/evm/sky/index.cjs.map +1 -1
  32. package/dist/protocols/evm/sky/index.js.map +1 -1
  33. package/dist/protocols/evm/uniswap-v4/index.cjs +863 -25
  34. package/dist/protocols/evm/uniswap-v4/index.cjs.map +1 -1
  35. package/dist/protocols/evm/uniswap-v4/index.d.ts +266 -9
  36. package/dist/protocols/evm/uniswap-v4/index.js +834 -27
  37. package/dist/protocols/evm/uniswap-v4/index.js.map +1 -1
  38. package/package.json +2 -1
package/dist/index.cjs CHANGED
@@ -121,6 +121,13 @@ function setMorphoGraphqlProxyUrl(url) {
121
121
  function getMorphoGraphqlProxyUrl() {
122
122
  return morphoGraphqlProxyUrl;
123
123
  }
124
+ var morphoMidnightRestProxyUrl;
125
+ function setMorphoMidnightRestProxyUrl(url) {
126
+ morphoMidnightRestProxyUrl = url?.trim() || void 0;
127
+ }
128
+ function getMorphoMidnightRestProxyUrl() {
129
+ return morphoMidnightRestProxyUrl;
130
+ }
124
131
  function setCoingeckoProxyUrl(url) {
125
132
  coingeckoProxyUrl = url?.trim() || void 0;
126
133
  }
@@ -152,6 +159,15 @@ async function postJsonViaOptionalProxy(args) {
152
159
  }
153
160
  return await r.json();
154
161
  }
162
+ async function getJsonViaOptionalProxy(args) {
163
+ const url = args.proxyUrl?.trim() || args.directUrl;
164
+ const r = await fetch(url, { method: "GET", headers: { accept: "application/json" } });
165
+ if (!r.ok) {
166
+ const t = await r.text().catch(() => "");
167
+ throw new Error(t ? `HTTP ${r.status}: ${t.slice(0, 200)}` : `HTTP ${r.status}`);
168
+ }
169
+ return await r.json();
170
+ }
155
171
  var EIP712_SIGN_REQUEST_KIND = "eip712";
156
172
  function msgHashNo0x(digest) {
157
173
  return digest.startsWith("0x") ? digest.slice(2) : digest;
@@ -673,11 +689,23 @@ var UNIVERSAL_ROUTER_SPENDER = {
673
689
  /** Robinhood Chain mainnet — Universal Router v2.1.1 (Trade API / interface) */
674
690
  4663: "0x8876789976decbfcbbbe364623c63652db8c0904"
675
691
  };
692
+ var UNIVERSAL_ROUTER_V2_2_SPENDER = {
693
+ 1: "0xCb640A86855f1A828c27241bA364348de28abe66",
694
+ 11155111: "0xB0C89059d7190EDb17eFF19829cc009cEe923916"
695
+ };
676
696
  var UNIVERSAL_ROUTER_VERSION_BY_CHAIN = {
677
697
  /** Robinhood has UR 2.1.1 only in the supported-chains table (no 2.0 Trade API column). */
678
698
  4663: "2.1.1"
679
699
  };
680
- function getUniswapUniversalRouterVersion(chainId) {
700
+ var UNISWAP_UNIVERSAL_ROUTER_VERSION_DEFAULT = "2.0";
701
+ var UNISWAP_UNIVERSAL_ROUTER_VERSION_PERMISSIONED = "2.2.0";
702
+ function getUniswapUniversalRouterVersion(chainId, opts) {
703
+ if (opts?.permissioned) {
704
+ const v22 = UNIVERSAL_ROUTER_V2_2_SPENDER[chainId];
705
+ if (typeof v22 === "string" && v22.startsWith("0x")) {
706
+ return UNISWAP_UNIVERSAL_ROUTER_VERSION_PERMISSIONED;
707
+ }
708
+ }
681
709
  const v = UNIVERSAL_ROUTER_VERSION_BY_CHAIN[chainId];
682
710
  return typeof v === "string" && v.trim() ? v.trim() : UNISWAP_UNIVERSAL_ROUTER_VERSION_DEFAULT;
683
711
  }
@@ -688,7 +716,11 @@ function isUniswapV4ChainSupported(chainId) {
688
716
  const a = UNIVERSAL_ROUTER_SPENDER[n];
689
717
  return typeof a === "string" && a.startsWith("0x");
690
718
  }
691
- function getUniswapUniversalRouterSpenderOrThrow(chainId) {
719
+ function getUniswapUniversalRouterSpenderOrThrow(chainId, opts) {
720
+ if (opts?.permissioned) {
721
+ const raw22 = UNIVERSAL_ROUTER_V2_2_SPENDER[chainId];
722
+ if (raw22 && raw22.startsWith("0x")) return viem.getAddress(raw22);
723
+ }
692
724
  const raw = UNIVERSAL_ROUTER_SPENDER[chainId];
693
725
  if (!raw || !raw.startsWith("0x")) {
694
726
  throw new Error(
@@ -701,7 +733,6 @@ var MAX_UINT160 = (1n << 160n) - 1n;
701
733
  var MAX_UINT48 = (1n << 48n) - 1n;
702
734
  var UNISWAP_UNIVERSAL_ROUTER_DEFAULT_GAS_UNITS = 1500000n;
703
735
  var UNISWAP_TRADE_BASE_DEFAULT = "https://trade-api.gateway.uniswap.org/v1";
704
- var UNISWAP_UNIVERSAL_ROUTER_VERSION_DEFAULT = "2.0";
705
736
  var UNISWAP_SWAP_DEFAULT_EXPIRY_MINUTES = 30;
706
737
  var UNISWAP_LP_DEFAULT_EXPIRY_MINUTES = 30;
707
738
  var UNISWAP_LP_DEFAULT_DEADLINE_SEC_OFFSET = UNISWAP_LP_DEFAULT_EXPIRY_MINUTES * 60;
@@ -719,6 +750,10 @@ var POSITION_MANAGER_BY_CHAIN = {
719
750
  /** Robinhood Chain mainnet */
720
751
  4663: "0x58daec3116aae6D93017bAAea7749052E8a04fA7"
721
752
  };
753
+ var PERMISSIONED_POSITION_MANAGER_BY_CHAIN = {
754
+ 1: "0x89628C9B4CE81951a9BC1F36F0688Fad6A6ee248",
755
+ 11155111: "0x68fC145BB20b388965bED184Df5ef912215bb3C7"
756
+ };
722
757
  function getUniswapV4PositionManagerOrThrow(chainId) {
723
758
  const raw = POSITION_MANAGER_BY_CHAIN[chainId];
724
759
  if (!raw || !raw.startsWith("0x") || raw.length !== 42) {
@@ -728,6 +763,32 @@ function getUniswapV4PositionManagerOrThrow(chainId) {
728
763
  }
729
764
  return viem.getAddress(raw);
730
765
  }
766
+ function getUniswapV4PermissionedPositionManagerOrThrow(chainId) {
767
+ const raw = PERMISSIONED_POSITION_MANAGER_BY_CHAIN[chainId];
768
+ if (!raw || !raw.startsWith("0x") || raw.length !== 42) {
769
+ throw new Error(
770
+ `No Uniswap V4 Permissioned PositionManager is configured for chainId ${chainId}.`
771
+ );
772
+ }
773
+ return viem.getAddress(raw);
774
+ }
775
+ function tryGetUniswapV4PermissionedPositionManager(chainId) {
776
+ try {
777
+ return getUniswapV4PermissionedPositionManagerOrThrow(chainId);
778
+ } catch {
779
+ return null;
780
+ }
781
+ }
782
+ function isUniswapV4PermissionedPositionManager(chainId, address) {
783
+ if (!address?.toString().trim()) return false;
784
+ const perm = tryGetUniswapV4PermissionedPositionManager(chainId);
785
+ if (!perm) return false;
786
+ try {
787
+ return viem.getAddress(address) === perm;
788
+ } catch {
789
+ return false;
790
+ }
791
+ }
731
792
  var POSITION_MANAGER_DEPLOY_BLOCK_BY_CHAIN = {
732
793
  /** Ethereum mainnet — Uniswap v4 launch (Jan 2025). */
733
794
  1: 22938741n
@@ -743,6 +804,152 @@ var UNISWAP_V4_LP_DECREASE_DEFAULT_GAS_UNITS = 1200000n;
743
804
  var UNISWAP_V4_LP_COLLECT_DEFAULT_GAS_UNITS = 900000n;
744
805
  var UNISWAP_V4_LP_ERC20_APPROVE_FALLBACK = 100000n;
745
806
  var UNISWAP_V4_LP_WETH_DEPOSIT_FALLBACK = 120000n;
807
+ var UNISWAP_V4_LP_PERMIT2_APPROVE_FALLBACK = 100000n;
808
+ viem.parseAbi([
809
+ "function isAllowed(address account, uint8 permission) view returns (bool)",
810
+ "function PERMISSIONED_TOKEN() view returns (address)"
811
+ ]);
812
+ viem.parseAbi([
813
+ "function verifiedPermissionsAdapterOf(address permissionsAdapter) view returns (address)"
814
+ ]);
815
+ function normalizeTokenList(tokens) {
816
+ const out = [];
817
+ const seen = /* @__PURE__ */ new Set();
818
+ for (const raw of tokens) {
819
+ const t = (raw ?? "").toString().trim();
820
+ if (!t) continue;
821
+ if (isUniswapNativeTokenAddress(t)) continue;
822
+ let addr;
823
+ try {
824
+ addr = viem.getAddress(t);
825
+ } catch {
826
+ continue;
827
+ }
828
+ if (addr === viem.zeroAddress) continue;
829
+ const key = addr.toLowerCase();
830
+ if (seen.has(key)) continue;
831
+ seen.add(key);
832
+ out.push(addr);
833
+ if (out.length >= 2) break;
834
+ }
835
+ return out;
836
+ }
837
+ function parsePermissionRow(raw) {
838
+ const tokenRaw = (raw.token ?? raw.address ?? "").toString().trim();
839
+ if (!tokenRaw) return null;
840
+ let token;
841
+ try {
842
+ token = viem.getAddress(tokenRaw);
843
+ } catch {
844
+ return null;
845
+ }
846
+ const isPermissioned = Boolean(raw.isPermissioned);
847
+ const isAllowlisted = isPermissioned ? Boolean(raw.isAllowlisted) : true;
848
+ let adapterTokenAddress;
849
+ const adapterRaw = (raw.adapterTokenAddress ?? "").toString().trim();
850
+ if (adapterRaw) {
851
+ try {
852
+ adapterTokenAddress = viem.getAddress(adapterRaw);
853
+ } catch {
854
+ }
855
+ }
856
+ const kycUrl = typeof raw.kycUrl === "string" && raw.kycUrl.trim() ? raw.kycUrl.trim() : void 0;
857
+ const issuer = typeof raw.issuer === "string" && raw.issuer.trim() ? raw.issuer.trim() : void 0;
858
+ return { token, isPermissioned, isAllowlisted, adapterTokenAddress, kycUrl, issuer };
859
+ }
860
+ async function uniswapV4CheckPermissions(args) {
861
+ const apiKey = args.uniswapApiKey?.trim();
862
+ if (!apiKey) throw new Error("uniswapApiKey is required for POST /v1/permissions");
863
+ const walletAddress = viem.getAddress(args.walletAddress.trim());
864
+ const chainId = parseUniswapChainId(args.chainId);
865
+ const tokens = normalizeTokenList(args.tokens);
866
+ if (tokens.length === 0) {
867
+ return {
868
+ results: [],
869
+ anyPermissioned: false,
870
+ allAllowlisted: true,
871
+ requiresUniversalRouterV22: false
872
+ };
873
+ }
874
+ const base = (args.baseUrl?.trim() || UNISWAP_TRADE_BASE_DEFAULT).replace(/\/$/, "");
875
+ const url = `${base}/permissions`;
876
+ const fetchFn = args.fetchImpl ?? globalThis.fetch;
877
+ const res = await fetchFn(url, {
878
+ method: "POST",
879
+ headers: {
880
+ "Content-Type": "application/json",
881
+ "User-Agent": "ctm-mpc-defi uniswapPermissions/1.0 (TS)",
882
+ "x-api-key": apiKey
883
+ },
884
+ body: JSON.stringify({
885
+ walletAddress,
886
+ tokens,
887
+ chainId
888
+ }),
889
+ signal: args.signal
890
+ });
891
+ const text = await res.text();
892
+ if (!res.ok) {
893
+ throw new Error(
894
+ `Uniswap POST /permissions failed: ${messageFromUniswapHttpResponseBody(text, res.status, res.statusText)}`
895
+ );
896
+ }
897
+ let parsed;
898
+ try {
899
+ parsed = JSON.parse(text.replace(/^\uFEFF/, ""));
900
+ } catch {
901
+ throw new Error(
902
+ `Uniswap POST /permissions: ${messageFromUniswapHttpResponseBody(text, res.status, res.statusText)}`
903
+ );
904
+ }
905
+ const rawResults = Array.isArray(parsed.results) ? parsed.results : [];
906
+ const results = [];
907
+ for (const row of rawResults) {
908
+ if (!row || typeof row !== "object" || Array.isArray(row)) continue;
909
+ const parsedRow = parsePermissionRow(row);
910
+ if (parsedRow) results.push(parsedRow);
911
+ }
912
+ for (const token of tokens) {
913
+ if (!results.some((r) => r.token === token)) {
914
+ results.push({ token, isPermissioned: false, isAllowlisted: true });
915
+ }
916
+ }
917
+ const anyPermissioned = results.some((r) => r.isPermissioned);
918
+ const allAllowlisted = results.every((r) => !r.isPermissioned || r.isAllowlisted);
919
+ return {
920
+ requestId: typeof parsed.requestId === "string" ? parsed.requestId : void 0,
921
+ results,
922
+ anyPermissioned,
923
+ allAllowlisted,
924
+ requiresUniversalRouterV22: anyPermissioned
925
+ };
926
+ }
927
+ var UniswapV4PermissionDeniedError = class extends Error {
928
+ code = "UNISWAP_V4_PERMISSION_DENIED";
929
+ permissions;
930
+ blocked;
931
+ constructor(permissions) {
932
+ const blocked = permissions.results.filter((r) => r.isPermissioned && !r.isAllowlisted);
933
+ const issuer = blocked.find((b) => b.issuer)?.issuer;
934
+ const kycUrl = blocked.find((b) => b.kycUrl)?.kycUrl;
935
+ const parts = [
936
+ "Wallet is not allowlisted for one or more permissioned Uniswap V4 tokens.",
937
+ issuer ? `Issuer: ${issuer}.` : null,
938
+ kycUrl ? `Complete KYC at: ${kycUrl}` : "Open the issuer KYC URL from check_permissions."
939
+ ].filter(Boolean);
940
+ super(parts.join(" "));
941
+ this.name = "UniswapV4PermissionDeniedError";
942
+ this.permissions = permissions;
943
+ this.blocked = blocked;
944
+ }
945
+ };
946
+ function assertUniswapV4PermissionsAllowlisted(permissions) {
947
+ if (!permissions.allAllowlisted) {
948
+ throw new UniswapV4PermissionDeniedError(permissions);
949
+ }
950
+ }
951
+
952
+ // src/protocols/evm/uniswap-v4/quote.ts
746
953
  var DEFAULT_TRADE_BASE = "https://trade-api.gateway.uniswap.org/v1";
747
954
  var UNISWAP_QUOTE_HEADERS_BASE = {
748
955
  "Content-Type": "application/json",
@@ -989,7 +1196,25 @@ async function uniswapTradeQuote(args) {
989
1196
  const base = args.baseUrl && args.baseUrl.trim() ? args.baseUrl.trim().replace(/\/$/, "") : DEFAULT_TRADE_BASE;
990
1197
  const quoteUrl = `${base}/quote`;
991
1198
  const chainIdForRouter = parseUniswapChainId(resolveInputChainId(args));
992
- const urv = args.universalRouterVersion && args.universalRouterVersion.trim() || getUniswapUniversalRouterVersion(chainIdForRouter);
1199
+ let permissions;
1200
+ const shouldCheckPermissions = args.forcePermissionedRouter !== true && args.skipPermissionCheck !== true && args.checkPermissions !== false;
1201
+ if (shouldCheckPermissions) {
1202
+ try {
1203
+ permissions = await uniswapV4CheckPermissions({
1204
+ walletAddress: swapper,
1205
+ tokens: [args.tokenIn, args.tokenOut],
1206
+ chainId: chainIdForRouter,
1207
+ uniswapApiKey: apiKey,
1208
+ baseUrl: base,
1209
+ signal: args.signal,
1210
+ fetchImpl: args.fetchImpl
1211
+ });
1212
+ } catch {
1213
+ permissions = void 0;
1214
+ }
1215
+ }
1216
+ const permissionedRouter = args.forcePermissionedRouter === true || Boolean(permissions?.requiresUniversalRouterV22);
1217
+ const urv = args.universalRouterVersion && args.universalRouterVersion.trim() || getUniswapUniversalRouterVersion(chainIdForRouter, { permissioned: permissionedRouter });
993
1218
  const body = buildUniswapQuoteRequestBody({ ...args, swapper });
994
1219
  const nativeIn = isUniswapNativeTokenAddress(args.tokenIn);
995
1220
  const nativeOut = isUniswapNativeTokenAddress(args.tokenOut);
@@ -1018,13 +1243,27 @@ async function uniswapTradeQuote(args) {
1018
1243
  }
1019
1244
  const forParse = text.replace(/^\uFEFF/, "");
1020
1245
  try {
1021
- return JSON.parse(forParse);
1246
+ const parsed = JSON.parse(forParse);
1247
+ if (permissionedRouter || urv === UNISWAP_UNIVERSAL_ROUTER_VERSION_PERMISSIONED) {
1248
+ parsed._continuumPermissioned = true;
1249
+ parsed._continuumUniversalRouterVersion = urv;
1250
+ }
1251
+ if (permissions) {
1252
+ parsed._continuumPermissions = permissions;
1253
+ }
1254
+ return parsed;
1022
1255
  } catch {
1023
1256
  throw new Error(
1024
1257
  `Trade API: ${messageFromUniswapHttpResponseBody(text, res.status, res.statusText)}`
1025
1258
  );
1026
1259
  }
1027
1260
  }
1261
+ function isUniswapStoredQuotePermissioned(stored) {
1262
+ if (!stored) return false;
1263
+ if (stored._continuumPermissioned === true) return true;
1264
+ const v = stored._continuumUniversalRouterVersion;
1265
+ return typeof v === "string" && v.trim() === UNISWAP_UNIVERSAL_ROUTER_VERSION_PERMISSIONED;
1266
+ }
1028
1267
  function parseUniswapQuoteClassicInOut(res) {
1029
1268
  const inner = res.quote;
1030
1269
  if (!inner || typeof inner !== "object" || Array.isArray(inner)) return null;
@@ -1121,7 +1360,9 @@ async function uniswapCreateSwap(args) {
1121
1360
  quote: classic,
1122
1361
  deadline
1123
1362
  };
1124
- const urv = (args.universalRouterVersion ?? "").trim() || getUniswapUniversalRouterVersion(resolveChainIdFromStoredUniswapQuote(args.fullQuoteFromPermit));
1363
+ const chainIdForRouter = resolveChainIdFromStoredUniswapQuote(args.fullQuoteFromPermit);
1364
+ const permissioned = isUniswapStoredQuotePermissioned(args.fullQuoteFromPermit);
1365
+ const urv = (args.universalRouterVersion ?? "").trim() || getUniswapUniversalRouterVersion(chainIdForRouter, { permissioned });
1125
1366
  const fn = args.fetchImpl ?? globalThis.fetch;
1126
1367
  const nativeIn = isUniswapFullQuoteResponseNativeIn(args.fullQuoteFromPermit);
1127
1368
  const nativeOut = isUniswapFullQuoteResponseNativeOut(args.fullQuoteFromPermit);
@@ -1249,8 +1490,8 @@ function applySlippagePercentToApproveWei(baseWei, slippagePercent) {
1249
1490
  if (bps <= 0) return baseWei;
1250
1491
  return (baseWei * BigInt(1e4 + bps) + 9999n) / 10000n;
1251
1492
  }
1252
- function permit2SpenderAndApproveWeiFromSwapCalldata(dataHex, tokenIn, quoteInputWei, chainId) {
1253
- const canonical = getUniswapUniversalRouterSpenderOrThrow(chainId);
1493
+ function permit2SpenderAndApproveWeiFromSwapCalldata(dataHex, tokenIn, quoteInputWei, chainId, permissioned = false) {
1494
+ const canonical = getUniswapUniversalRouterSpenderOrThrow(chainId, { permissioned });
1254
1495
  try {
1255
1496
  const d = viem.decodeFunctionData({ abi: uniswapDispatchExecuteAbi, data: dataHex });
1256
1497
  if (d.functionName !== "execute") {
@@ -1386,6 +1627,27 @@ async function buildEvmMultisignBodyUniswapV4SkipPermit2Batch(args) {
1386
1627
  "Could not read a positive input amount from the quote. Refresh the quote and ensure the Trade API returned classic quote.input.amount."
1387
1628
  );
1388
1629
  }
1630
+ const permissioned = isUniswapStoredQuotePermissioned(args.fullQuoteSnapshot);
1631
+ if (args.skipPermissionCheck !== true) {
1632
+ const cached = args.fullQuoteSnapshot._continuumPermissions;
1633
+ if (cached && typeof cached === "object" && cached.allAllowlisted === false) {
1634
+ assertUniswapV4PermissionsAllowlisted({
1635
+ results: cached.results ?? [],
1636
+ anyPermissioned: Boolean(cached.anyPermissioned),
1637
+ allAllowlisted: false,
1638
+ requiresUniversalRouterV22: Boolean(cached.requiresUniversalRouterV22)
1639
+ });
1640
+ } else if (args.uniswapApiKey?.trim() && (permissioned || args.tokenOut)) {
1641
+ const tokens = [args.tokenIn, args.tokenOut].filter(Boolean);
1642
+ const permissions = await uniswapV4CheckPermissions({
1643
+ walletAddress: args.executorAddress,
1644
+ tokens,
1645
+ chainId: args.chainId,
1646
+ uniswapApiKey: args.uniswapApiKey.trim()
1647
+ });
1648
+ assertUniswapV4PermissionsAllowlisted(permissions);
1649
+ }
1650
+ }
1389
1651
  if (tokenIn === viem.zeroAddress) {
1390
1652
  return buildEvmMultisignBodyUniswapV4NativeInOnly(args, quoteInputWei);
1391
1653
  }
@@ -1396,12 +1658,15 @@ async function buildEvmMultisignBodyUniswapV4SkipPermit2Batch(args) {
1396
1658
  const d = (args.swap.data ?? "0x").toString().trim();
1397
1659
  return d.startsWith("0x") ? d : `0x${d}`;
1398
1660
  })();
1399
- const canonicalUniversalRouter = getUniswapUniversalRouterSpenderOrThrow(args.chainId);
1661
+ const canonicalUniversalRouter = getUniswapUniversalRouterSpenderOrThrow(args.chainId, {
1662
+ permissioned
1663
+ });
1400
1664
  const { permit2Spender, approveWei: baseApproveWei, calldataAmountWei } = permit2SpenderAndApproveWeiFromSwapCalldata(
1401
1665
  dataHex,
1402
1666
  tokenIn,
1403
1667
  quoteInputWei,
1404
- args.chainId
1668
+ args.chainId,
1669
+ permissioned
1405
1670
  );
1406
1671
  const approveAmountWei = applySlippagePercentToApproveWei(baseApproveWei, args.slippagePercent);
1407
1672
  const usePermit2Triple = toRouter.toLowerCase() === permit2Spender.toLowerCase();
@@ -2069,6 +2334,9 @@ var erc20AllowanceAbi2 = viem.parseAbi([
2069
2334
  "function allowance(address owner, address spender) view returns (uint256)",
2070
2335
  "function decimals() view returns (uint8)"
2071
2336
  ]);
2337
+ var permit2ApproveAbi = viem.parseAbi([
2338
+ "function approve(address token, address spender, uint160 amount, uint48 expiration)"
2339
+ ]);
2072
2340
  function parseOptionalGasLimitString2(raw) {
2073
2341
  if (raw == null) return null;
2074
2342
  const s = String(raw).trim();
@@ -2156,7 +2424,9 @@ async function buildEvmMultisignBodyUniswapV4LiquidityBatchInternal(args) {
2156
2424
  const dataHex = dataHexFromTx(tx);
2157
2425
  const valueWei = parseTxValueWei(tx);
2158
2426
  const executor = viem.getAddress(args.executorAddress);
2159
- const spender = to;
2427
+ const positionManagerKind = args.positionManagerKind === "permissioned" || args.positionManagerKind === "standard" ? args.positionManagerKind : isUniswapV4PermissionedPositionManager(args.chainId, to) ? "permissioned" : "standard";
2428
+ const usePermit2 = args.usePermit2Approvals === true || args.usePermit2Approvals !== false && positionManagerKind === "permissioned";
2429
+ const erc20Spender = usePermit2 ? PERMIT2_ADDRESS : to;
2160
2430
  const chain = viem.defineChain({
2161
2431
  id: args.chainId,
2162
2432
  name: `uniswap-v4-lp-${args.chainId}`,
@@ -2164,15 +2434,21 @@ async function buildEvmMultisignBodyUniswapV4LiquidityBatchInternal(args) {
2164
2434
  rpcUrls: { default: { http: [args.rpcUrl] } }
2165
2435
  });
2166
2436
  const publicClient = viem.createPublicClient({ chain, transport: viem.http(args.rpcUrl) });
2167
- const approveTargets = await resolveApproveTargets({
2437
+ const needsTokenApproves = args.action === "mint" || args.action === "increase";
2438
+ const approveTargets = needsTokenApproves ? await resolveApproveTargets({
2168
2439
  publicClient,
2169
2440
  executor,
2170
- spender,
2441
+ spender: erc20Spender,
2171
2442
  token0: parsed.token0,
2172
2443
  token1: parsed.token1,
2173
2444
  nativeWrapped: args.nativeWrapped
2174
- });
2445
+ }) : [];
2175
2446
  const steps = [];
2447
+ let permit2Expiration48 = 0n;
2448
+ if (usePermit2 && needsTokenApproves) {
2449
+ permit2Expiration48 = BigInt(Math.floor(Date.now() / 1e3) + UNISWAP_LP_DEFAULT_DEADLINE_SEC_OFFSET);
2450
+ if (permit2Expiration48 > MAX_UINT48) permit2Expiration48 = MAX_UINT48;
2451
+ }
2176
2452
  for (const target of approveTargets) {
2177
2453
  if (target.kind === "weth_deposit") {
2178
2454
  steps.push({
@@ -2185,13 +2461,17 @@ async function buildEvmMultisignBodyUniswapV4LiquidityBatchInternal(args) {
2185
2461
  address: target.token,
2186
2462
  abi: erc20AllowanceAbi2,
2187
2463
  functionName: "allowance",
2188
- args: [executor, spender]
2464
+ args: [executor, erc20Spender]
2189
2465
  });
2190
2466
  if (wethAllowance < target.amount) {
2191
- if (wethAllowance > 0n) {
2467
+ if (wethAllowance > 0n && !usePermit2) {
2192
2468
  steps.push({
2193
2469
  to: target.token,
2194
- data: viem.encodeFunctionData({ abi: viem.erc20Abi, functionName: "approve", args: [spender, 0n] }),
2470
+ data: viem.encodeFunctionData({
2471
+ abi: viem.erc20Abi,
2472
+ functionName: "approve",
2473
+ args: [erc20Spender, 0n]
2474
+ }),
2195
2475
  value: 0n,
2196
2476
  fallbackGas: UNISWAP_V4_LP_ERC20_APPROVE_FALLBACK
2197
2477
  });
@@ -2201,7 +2481,7 @@ async function buildEvmMultisignBodyUniswapV4LiquidityBatchInternal(args) {
2201
2481
  data: viem.encodeFunctionData({
2202
2482
  abi: viem.erc20Abi,
2203
2483
  functionName: "approve",
2204
- args: [spender, target.amount]
2484
+ args: [erc20Spender, target.amount]
2205
2485
  }),
2206
2486
  value: 0n,
2207
2487
  fallbackGas: UNISWAP_V4_LP_ERC20_APPROVE_FALLBACK
@@ -2213,13 +2493,66 @@ async function buildEvmMultisignBodyUniswapV4LiquidityBatchInternal(args) {
2213
2493
  data: viem.encodeFunctionData({
2214
2494
  abi: viem.erc20Abi,
2215
2495
  functionName: "approve",
2216
- args: [spender, target.amount]
2496
+ args: [erc20Spender, target.amount]
2217
2497
  }),
2218
2498
  value: 0n,
2219
2499
  fallbackGas: UNISWAP_V4_LP_ERC20_APPROVE_FALLBACK
2220
2500
  });
2221
2501
  }
2222
2502
  }
2503
+ if (usePermit2 && needsTokenApproves) {
2504
+ const pairs = [parsed.token0, parsed.token1].filter(Boolean);
2505
+ for (const row of pairs) {
2506
+ const amt = BigInt(row.amount);
2507
+ if (amt <= 0n) continue;
2508
+ let token;
2509
+ if (isNativeUniswapLpTokenAddress(row.tokenAddress)) {
2510
+ if (!args.nativeWrapped) {
2511
+ throw new Error("nativeWrapped is required when LP uses native ETH (0x0) token.");
2512
+ }
2513
+ token = viem.getAddress(args.nativeWrapped);
2514
+ if (!approveTargets.some((a) => a.token === token && a.kind === "weth_deposit")) {
2515
+ steps.push({
2516
+ to: token,
2517
+ data: viem.encodeFunctionData({ abi: wethDepositAbi, functionName: "deposit" }),
2518
+ value: amt,
2519
+ fallbackGas: UNISWAP_V4_LP_WETH_DEPOSIT_FALLBACK
2520
+ });
2521
+ const wethAllowance = await publicClient.readContract({
2522
+ address: token,
2523
+ abi: erc20AllowanceAbi2,
2524
+ functionName: "allowance",
2525
+ args: [executor, PERMIT2_ADDRESS]
2526
+ });
2527
+ if (wethAllowance < amt) {
2528
+ steps.push({
2529
+ to: token,
2530
+ data: viem.encodeFunctionData({
2531
+ abi: viem.erc20Abi,
2532
+ functionName: "approve",
2533
+ args: [PERMIT2_ADDRESS, amt]
2534
+ }),
2535
+ value: 0n,
2536
+ fallbackGas: UNISWAP_V4_LP_ERC20_APPROVE_FALLBACK
2537
+ });
2538
+ }
2539
+ }
2540
+ } else {
2541
+ token = viem.getAddress(row.tokenAddress);
2542
+ }
2543
+ const amount160 = amt > MAX_UINT160 ? MAX_UINT160 : amt;
2544
+ steps.push({
2545
+ to: PERMIT2_ADDRESS,
2546
+ data: viem.encodeFunctionData({
2547
+ abi: permit2ApproveAbi,
2548
+ functionName: "approve",
2549
+ args: [token, to, amount160, Number(permit2Expiration48)]
2550
+ }),
2551
+ value: 0n,
2552
+ fallbackGas: UNISWAP_V4_LP_PERMIT2_APPROVE_FALLBACK
2553
+ });
2554
+ }
2555
+ }
2223
2556
  const lpFallbackGas = defaultGasForAction(args.action);
2224
2557
  const fromTradeApi = parseOptionalGasLimitString2(tx.gasLimit) ?? parseOptionalGasLimitString2(tx.gas);
2225
2558
  steps.push({
@@ -2231,6 +2564,7 @@ async function buildEvmMultisignBodyUniswapV4LiquidityBatchInternal(args) {
2231
2564
  const actionLabel = args.action === "mint" ? "mint liquidity" : args.action === "increase" ? "increase liquidity" : args.action === "decrease" ? "decrease liquidity" : "collect fees";
2232
2565
  const dataNo0x = dataHex.startsWith("0x") ? dataHex.slice(2) : dataHex;
2233
2566
  const lpIndex = steps.length - 1;
2567
+ const purposeSuffix = usePermit2 ? `Uniswap V4 permissioned: ${steps.length}-tx batch \u2014 ${actionLabel} (ERC-20\u2192Permit2, Permit2\u2192PosM, Position Manager).` : `Uniswap V4: ${steps.length}-tx batch \u2014 ${actionLabel} (classic ERC-20 approve + Position Manager).`;
2234
2568
  return buildEvmMultisignBatch({
2235
2569
  context: {
2236
2570
  chainCategory: "evm",
@@ -2244,7 +2578,7 @@ async function buildEvmMultisignBodyUniswapV4LiquidityBatchInternal(args) {
2244
2578
  customGasChainDetails: args.customGasChainDetails
2245
2579
  },
2246
2580
  steps,
2247
- purposeSuffix: `Uniswap V4: ${steps.length}-tx batch \u2014 ${actionLabel} (classic ERC-20 approve + Position Manager).`,
2581
+ purposeSuffix,
2248
2582
  firstMsgRawNo0x: dataNo0x,
2249
2583
  destinationAddress: to,
2250
2584
  buildBatchMeta: ({ index }) => {
@@ -2254,13 +2588,17 @@ async function buildEvmMultisignBodyUniswapV4LiquidityBatchInternal(args) {
2254
2588
  kind: "UniswapV4Liquidity",
2255
2589
  action: args.action,
2256
2590
  step: index,
2257
- lpTx: isLpStep
2591
+ lpTx: isLpStep,
2592
+ positionManagerKind,
2593
+ usePermit2
2258
2594
  }),
2259
2595
  ...isLpStep ? {
2260
2596
  evm: { type: "uniswap_v4_liquidity_tx", version: 1, chainId: String(args.chainId) },
2261
2597
  uniswapV4Liquidity: {
2262
2598
  action: args.action,
2263
- skipPermit2Batch: true,
2599
+ skipPermit2Batch: !usePermit2,
2600
+ usePermit2Approvals: usePermit2,
2601
+ positionManagerKind,
2264
2602
  nftTokenId: args.nftTokenId != null ? String(args.nftTokenId) : void 0,
2265
2603
  poolReference: args.poolReference,
2266
2604
  lpResponseSnapshot: args.lpResponse,
@@ -3118,8 +3456,10 @@ exports.getAaveGraphqlProxyUrl = getAaveGraphqlProxyUrl;
3118
3456
  exports.getActionsByChainCategory = getActionsByChainCategory;
3119
3457
  exports.getCoingeckoProxyUrl = getCoingeckoProxyUrl;
3120
3458
  exports.getEulerGraphqlProxyUrl = getEulerGraphqlProxyUrl;
3459
+ exports.getJsonViaOptionalProxy = getJsonViaOptionalProxy;
3121
3460
  exports.getMapleGraphqlProxyUrl = getMapleGraphqlProxyUrl;
3122
3461
  exports.getMorphoGraphqlProxyUrl = getMorphoGraphqlProxyUrl;
3462
+ exports.getMorphoMidnightRestProxyUrl = getMorphoMidnightRestProxyUrl;
3123
3463
  exports.getProtocolModule = getProtocolModule;
3124
3464
  exports.getProtocolModules = getProtocolModules;
3125
3465
  exports.isEvmNativeToken = isEvmNativeToken;
@@ -3139,6 +3479,7 @@ exports.setCoingeckoProxyUrl = setCoingeckoProxyUrl;
3139
3479
  exports.setEulerGraphqlProxyUrl = setEulerGraphqlProxyUrl;
3140
3480
  exports.setMapleGraphqlProxyUrl = setMapleGraphqlProxyUrl;
3141
3481
  exports.setMorphoGraphqlProxyUrl = setMorphoGraphqlProxyUrl;
3482
+ exports.setMorphoMidnightRestProxyUrl = setMorphoMidnightRestProxyUrl;
3142
3483
  exports.solanaChainCategoryModule = solanaChainCategoryModule;
3143
3484
  exports.uniswapV4 = uniswapV4;
3144
3485
  exports.uniswapV4ProtocolModule = uniswapV4ProtocolModule;