@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.
- package/dist/agent/catalog.cjs +681 -46
- package/dist/agent/catalog.cjs.map +1 -1
- package/dist/agent/catalog.d.ts +1209 -21
- package/dist/agent/catalog.js +668 -47
- package/dist/agent/catalog.js.map +1 -1
- package/dist/agent/skills/morpho/SKILL.md +38 -6
- package/dist/agent/skills/uniswap-v4/SKILL.md +15 -0
- package/dist/core/index.cjs +19 -0
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.ts +8 -1
- package/dist/core/index.js +17 -1
- package/dist/core/index.js.map +1 -1
- package/dist/index.cjs +363 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +361 -23
- package/dist/index.js.map +1 -1
- package/dist/protocols/evm/aave-v4/index.cjs.map +1 -1
- package/dist/protocols/evm/aave-v4/index.js.map +1 -1
- package/dist/protocols/evm/euler-v2/index.cjs.map +1 -1
- package/dist/protocols/evm/euler-v2/index.js.map +1 -1
- package/dist/protocols/evm/maple/index.cjs.map +1 -1
- package/dist/protocols/evm/maple/index.js.map +1 -1
- package/dist/protocols/evm/morpho/index.cjs +1153 -142
- package/dist/protocols/evm/morpho/index.cjs.map +1 -1
- package/dist/protocols/evm/morpho/index.d.ts +314 -8
- package/dist/protocols/evm/morpho/index.js +1131 -144
- package/dist/protocols/evm/morpho/index.js.map +1 -1
- package/dist/protocols/evm/permit2/index.cjs.map +1 -1
- package/dist/protocols/evm/permit2/index.js.map +1 -1
- package/dist/protocols/evm/sky/index.cjs.map +1 -1
- package/dist/protocols/evm/sky/index.js.map +1 -1
- package/dist/protocols/evm/uniswap-v4/index.cjs +863 -25
- package/dist/protocols/evm/uniswap-v4/index.cjs.map +1 -1
- package/dist/protocols/evm/uniswap-v4/index.d.ts +266 -9
- package/dist/protocols/evm/uniswap-v4/index.js +834 -27
- package/dist/protocols/evm/uniswap-v4/index.js.map +1 -1
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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({
|
|
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: [
|
|
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: [
|
|
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
|
|
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:
|
|
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;
|