@1delta/data-sdk 0.0.46 → 0.0.48
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/index.d.ts +1 -0
- package/dist/index.js +222 -14
- package/dist/index.mjs +202 -15
- package/dist/lending/index.d.ts +1 -0
- package/dist/lending/morpho.d.ts +7 -0
- package/dist/lending/registry.d.ts +3 -0
- package/dist/lending/twyne.d.ts +210 -0
- package/dist/remoteCache.d.ts +68 -0
- package/dist/tokens.d.ts +12 -5
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -95,6 +95,8 @@ var LENDER_DATA_DEFAULTS = () => ({
|
|
|
95
95
|
llamaLendMarkets: {},
|
|
96
96
|
resupplyConfig: {},
|
|
97
97
|
curvanceConfig: {},
|
|
98
|
+
twyneConfig: {},
|
|
99
|
+
twyneMarkets: {},
|
|
98
100
|
fraxlendConfig: {},
|
|
99
101
|
eulerConfigs: {},
|
|
100
102
|
eulerVaults: {},
|
|
@@ -745,6 +747,60 @@ var curvancePositionManager = (lender, chainId, marketManager, kind) => {
|
|
|
745
747
|
return pms.simplePositionManager ?? pms.vaultPositionManager ?? pms.nativeVaultPositionManager ?? pms.pendlePtPositionManager;
|
|
746
748
|
};
|
|
747
749
|
|
|
750
|
+
// src/lending/twyne.ts
|
|
751
|
+
var twyneConfig = () => getGlobalData2()?.twyneConfig;
|
|
752
|
+
var twyneConfigFor = (lender, chainId) => getGlobalData2()?.twyneConfig?.[twyneBaseKey(lender)]?.[String(chainId)];
|
|
753
|
+
var twyneMarkets = () => getGlobalData2()?.twyneMarkets;
|
|
754
|
+
var twyneChainData = (lender, chainId) => getGlobalData2()?.twyneMarkets?.[twyneBaseKey(lender)]?.[String(chainId)];
|
|
755
|
+
var twyneBaseKey = (lender) => lender?.startsWith("TWYNE") ? "TWYNE" : lender;
|
|
756
|
+
var twyneMarketKey = (chainId, intermediateVault, targetAsset) => `TWYNE_${chainId}_${intermediateVault.replace(/^0x/i, "").toUpperCase()}_${targetAsset.replace(/^0x/i, "").toUpperCase()}`;
|
|
757
|
+
var assertTwyneKeysUnique = (markets, chainId) => {
|
|
758
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
759
|
+
for (const m of markets) {
|
|
760
|
+
const key = twyneMarketKey(chainId, m.intermediateVault, m.targetAsset);
|
|
761
|
+
const triple = `${m.intermediateVault}|${m.targetVault}|${m.targetAsset}`.toLowerCase();
|
|
762
|
+
const seen = byKey.get(key);
|
|
763
|
+
if (seen && seen !== triple) {
|
|
764
|
+
throw new Error(
|
|
765
|
+
`assertTwyneKeysUnique: key ${key} maps to two triples (${seen} and ${triple}). The target vault must be added back into the key.`
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
byKey.set(key, triple);
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
var twyneMarketByKey = (lender, chainId) => {
|
|
772
|
+
const upper = lender?.toUpperCase();
|
|
773
|
+
return twyneChainData(lender, chainId)?.markets?.find((m) => m.key.toUpperCase() === upper);
|
|
774
|
+
};
|
|
775
|
+
var twyneMarketFor = (lender, chainId, intermediateVault, targetAsset) => {
|
|
776
|
+
const iv = intermediateVault?.toLowerCase();
|
|
777
|
+
const asset = targetAsset?.toLowerCase();
|
|
778
|
+
return twyneChainData(lender, chainId)?.markets?.find(
|
|
779
|
+
(m) => m.intermediateVault.toLowerCase() === iv && m.targetAsset.toLowerCase() === asset
|
|
780
|
+
);
|
|
781
|
+
};
|
|
782
|
+
var twyneMarketsForIntermediateVault = (lender, chainId, intermediateVault) => {
|
|
783
|
+
const iv = intermediateVault?.toLowerCase();
|
|
784
|
+
return (twyneChainData(lender, chainId)?.markets ?? []).filter((m) => m.intermediateVault.toLowerCase() === iv);
|
|
785
|
+
};
|
|
786
|
+
var twyneLendersByChain = (chainId) => {
|
|
787
|
+
const cfg = getGlobalData2()?.twyneConfig ?? {};
|
|
788
|
+
const chain = String(chainId);
|
|
789
|
+
return Object.keys(cfg).filter((lender) => !!cfg[lender]?.[chain]?.collateralVaultFactory);
|
|
790
|
+
};
|
|
791
|
+
var twyneOperator = (lender, chainId, vaultType, kind) => twyneConfigFor(lender, chainId)?.operators?.[vaultType]?.[kind];
|
|
792
|
+
var twyneBeaconFor = (lender, chainId, targetVault) => {
|
|
793
|
+
const map = twyneConfigFor(lender, chainId)?.beacons;
|
|
794
|
+
if (!map || !targetVault) return void 0;
|
|
795
|
+
const direct = map[targetVault];
|
|
796
|
+
if (direct) return direct;
|
|
797
|
+
const target = targetVault.toLowerCase();
|
|
798
|
+
for (const key of Object.keys(map)) {
|
|
799
|
+
if (key.toLowerCase() === target) return map[key];
|
|
800
|
+
}
|
|
801
|
+
return void 0;
|
|
802
|
+
};
|
|
803
|
+
|
|
748
804
|
// src/lending/fraxlend.ts
|
|
749
805
|
var FRAXLEND_ETHEREUM_CONFIG = {
|
|
750
806
|
deployer: "0xD6E9D27C75Afd88ad24Cd5EdccdC76fd2fc3A751",
|
|
@@ -862,12 +918,143 @@ var parseFraxlendPairKey = (lender) => {
|
|
|
862
918
|
return { chainId: m[1], pair: m[2].toLowerCase() };
|
|
863
919
|
};
|
|
864
920
|
|
|
921
|
+
// src/remoteCache.ts
|
|
922
|
+
var FRESH_TTL_MS = 5 * 60 * 1e3;
|
|
923
|
+
var REMOTE_CACHE_STORE_TTL_S = 7 * 24 * 60 * 60;
|
|
924
|
+
var STATE_KEY = "__1delta_remote_cache__";
|
|
925
|
+
function getState() {
|
|
926
|
+
const g = globalThis;
|
|
927
|
+
if (!g[STATE_KEY]) g[STATE_KEY] = { memo: /* @__PURE__ */ new Map(), inflight: /* @__PURE__ */ new Map() };
|
|
928
|
+
return g[STATE_KEY];
|
|
929
|
+
}
|
|
930
|
+
function setRemoteCacheStore(next) {
|
|
931
|
+
getState().store = next;
|
|
932
|
+
}
|
|
933
|
+
function setRemoteCacheGithubToken(token) {
|
|
934
|
+
getState().githubToken = token;
|
|
935
|
+
}
|
|
936
|
+
function clearRemoteCache() {
|
|
937
|
+
const state = getState();
|
|
938
|
+
state.memo.clear();
|
|
939
|
+
state.inflight.clear();
|
|
940
|
+
}
|
|
941
|
+
var RAW_HOST = "https://raw.githubusercontent.com/";
|
|
942
|
+
function githubApiMirror(url) {
|
|
943
|
+
if (!url.startsWith(RAW_HOST)) return void 0;
|
|
944
|
+
const [owner, repo, ref, ...path] = url.slice(RAW_HOST.length).split("/");
|
|
945
|
+
if (!owner || !repo || !ref || path.length === 0) return void 0;
|
|
946
|
+
return `https://api.github.com/repos/${owner}/${repo}/contents/${path.join("/")}?ref=${ref}`;
|
|
947
|
+
}
|
|
948
|
+
var RemoteFetchError = class extends Error {
|
|
949
|
+
constructor(message, status) {
|
|
950
|
+
super(message);
|
|
951
|
+
this.status = status;
|
|
952
|
+
this.name = "RemoteFetchError";
|
|
953
|
+
}
|
|
954
|
+
/** 404/410 — re-fetching cannot change the answer. */
|
|
955
|
+
get isMissing() {
|
|
956
|
+
return this.status === 404 || this.status === 410;
|
|
957
|
+
}
|
|
958
|
+
};
|
|
959
|
+
async function fetchRemoteJson(url, label) {
|
|
960
|
+
const res = await fetch(url);
|
|
961
|
+
if (res.ok) return await res.json();
|
|
962
|
+
const mirror = githubApiMirror(url);
|
|
963
|
+
const worthRetrying = res.status === 429 || res.status >= 500;
|
|
964
|
+
if (!mirror || !worthRetrying) {
|
|
965
|
+
throw new RemoteFetchError(`${label} fetch failed: HTTP ${res.status} from ${new URL(url).host}`, res.status);
|
|
966
|
+
}
|
|
967
|
+
const headers = {
|
|
968
|
+
Accept: "application/vnd.github.raw",
|
|
969
|
+
// The GitHub API rejects a request that sends no User-Agent.
|
|
970
|
+
"User-Agent": "1delta-data-sdk"
|
|
971
|
+
};
|
|
972
|
+
const token = getState().githubToken;
|
|
973
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
974
|
+
const viaApi = await fetch(mirror, { headers });
|
|
975
|
+
if (!viaApi.ok) {
|
|
976
|
+
throw new RemoteFetchError(
|
|
977
|
+
`${label} fetch failed: HTTP ${res.status} from raw.githubusercontent.com, HTTP ${viaApi.status} from api.github.com`,
|
|
978
|
+
res.status
|
|
979
|
+
);
|
|
980
|
+
}
|
|
981
|
+
return await viaApi.json();
|
|
982
|
+
}
|
|
983
|
+
var storeKey = (key) => `remote-cache:v1:${key}`;
|
|
984
|
+
async function readStore(key, isValid) {
|
|
985
|
+
const { store } = getState();
|
|
986
|
+
if (!store) return void 0;
|
|
987
|
+
try {
|
|
988
|
+
const raw = await store.get(storeKey(key));
|
|
989
|
+
if (!raw) return void 0;
|
|
990
|
+
const parsed = JSON.parse(raw);
|
|
991
|
+
if (typeof parsed?.at !== "number" || !isValid(parsed.value)) return void 0;
|
|
992
|
+
return parsed;
|
|
993
|
+
} catch {
|
|
994
|
+
return void 0;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
async function writeStore(key, entry) {
|
|
998
|
+
const { store } = getState();
|
|
999
|
+
if (!store) return;
|
|
1000
|
+
try {
|
|
1001
|
+
await store.put(storeKey(key), JSON.stringify(entry), REMOTE_CACHE_STORE_TTL_S);
|
|
1002
|
+
} catch {
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
async function loadCached(key, load, isValid) {
|
|
1006
|
+
const { memo, inflight } = getState();
|
|
1007
|
+
const cached = memo.get(key);
|
|
1008
|
+
if (cached && Date.now() - cached.at < FRESH_TTL_MS) return cached.value;
|
|
1009
|
+
const pending = inflight.get(key);
|
|
1010
|
+
if (pending) return pending;
|
|
1011
|
+
const task = (async () => {
|
|
1012
|
+
const stored = await readStore(key, isValid);
|
|
1013
|
+
if (stored && Date.now() - stored.at < FRESH_TTL_MS) {
|
|
1014
|
+
memo.set(key, stored);
|
|
1015
|
+
return stored.value;
|
|
1016
|
+
}
|
|
1017
|
+
try {
|
|
1018
|
+
const value = await load();
|
|
1019
|
+
const entry = { at: Date.now(), value };
|
|
1020
|
+
memo.set(key, entry);
|
|
1021
|
+
await writeStore(key, entry);
|
|
1022
|
+
return value;
|
|
1023
|
+
} catch (error) {
|
|
1024
|
+
const stale = memo.get(key) ?? stored;
|
|
1025
|
+
if (!stale) throw error;
|
|
1026
|
+
memo.set(key, stale);
|
|
1027
|
+
return stale.value;
|
|
1028
|
+
}
|
|
1029
|
+
})().finally(() => {
|
|
1030
|
+
inflight.delete(key);
|
|
1031
|
+
});
|
|
1032
|
+
inflight.set(key, task);
|
|
1033
|
+
return task;
|
|
1034
|
+
}
|
|
1035
|
+
async function loadCachedJson(key, url, isValid, label = key) {
|
|
1036
|
+
return loadCached(key, () => fetchRemoteJson(url, label), isValid);
|
|
1037
|
+
}
|
|
1038
|
+
|
|
865
1039
|
// src/tokens.ts
|
|
866
1040
|
var getListUrl = (chainId) => `https://raw.githubusercontent.com/1delta-DAO/token-lists/main/${chainId}.json`;
|
|
1041
|
+
var MAIN_TOKENS_URL = "https://raw.githubusercontent.com/1delta-DAO/token-lists/main/main-tokens.json";
|
|
1042
|
+
var isTokenListFile = (value) => !!value?.list && typeof value.list === "object";
|
|
1043
|
+
async function loadTokenListFile(chainId) {
|
|
1044
|
+
return loadCached(
|
|
1045
|
+
`token-list:${chainId}`,
|
|
1046
|
+
async () => {
|
|
1047
|
+
const json = await fetchRemoteJson(getListUrl(chainId), `Token list for chain ${chainId}`);
|
|
1048
|
+
return {
|
|
1049
|
+
list: json.list,
|
|
1050
|
+
mainTokens: json.mainTokens ?? []
|
|
1051
|
+
};
|
|
1052
|
+
},
|
|
1053
|
+
isTokenListFile
|
|
1054
|
+
);
|
|
1055
|
+
}
|
|
867
1056
|
async function fetchTokenList(chainId) {
|
|
868
|
-
|
|
869
|
-
if (!data.ok) throw new Error(`HTTP error! status: ${data.status}`);
|
|
870
|
-
return (await data.json()).list;
|
|
1057
|
+
return (await loadTokenListFile(chainId)).list;
|
|
871
1058
|
}
|
|
872
1059
|
async function fetchTokenLists(chainIds) {
|
|
873
1060
|
const promises = chainIds.map(async (chainId) => {
|
|
@@ -878,25 +1065,27 @@ async function fetchTokenLists(chainIds) {
|
|
|
878
1065
|
return Object.fromEntries(results);
|
|
879
1066
|
}
|
|
880
1067
|
async function fetchTokenListFile(chainId) {
|
|
881
|
-
const
|
|
882
|
-
|
|
883
|
-
const json = await data.json();
|
|
884
|
-
return {
|
|
885
|
-
list: json.list,
|
|
886
|
-
mainTokens: json.mainTokens ?? []
|
|
887
|
-
};
|
|
1068
|
+
const file = await loadTokenListFile(chainId);
|
|
1069
|
+
return { list: file.list, mainTokens: file.mainTokens };
|
|
888
1070
|
}
|
|
889
|
-
var MAIN_TOKENS_URL = "https://raw.githubusercontent.com/1delta-DAO/token-lists/main/main-tokens.json";
|
|
890
1071
|
async function fetchMainTokensMap() {
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
1072
|
+
try {
|
|
1073
|
+
return await loadCached(
|
|
1074
|
+
"main-tokens",
|
|
1075
|
+
() => fetchRemoteJson(MAIN_TOKENS_URL, "main-tokens"),
|
|
1076
|
+
(value) => !!value && typeof value === "object"
|
|
1077
|
+
);
|
|
1078
|
+
} catch {
|
|
1079
|
+
return void 0;
|
|
1080
|
+
}
|
|
894
1081
|
}
|
|
895
1082
|
|
|
896
1083
|
exports.CURVANCE_MONAD_CONFIG = CURVANCE_MONAD_CONFIG;
|
|
897
1084
|
exports.FRAXLEND_ETHEREUM_CONFIG = FRAXLEND_ETHEREUM_CONFIG;
|
|
898
1085
|
exports.GEARBOX_ACCOUNT_COMPRESSOR_V310 = GEARBOX_ACCOUNT_COMPRESSOR_V310;
|
|
1086
|
+
exports.REMOTE_CACHE_STORE_TTL_S = REMOTE_CACHE_STORE_TTL_S;
|
|
899
1087
|
exports.RESUPPLY_ETHEREUM_CONFIG = RESUPPLY_ETHEREUM_CONFIG;
|
|
1088
|
+
exports.RemoteFetchError = RemoteFetchError;
|
|
900
1089
|
exports.aaveOracles = aaveOracles;
|
|
901
1090
|
exports.aaveOraclesConfig = aaveOraclesConfig;
|
|
902
1091
|
exports.aavePools = aavePools;
|
|
@@ -907,8 +1096,10 @@ exports.aaveV4Peripherals = aaveV4Peripherals;
|
|
|
907
1096
|
exports.aaveV4SpokeLenderKey = aaveV4SpokeLenderKey;
|
|
908
1097
|
exports.aaveV4Spokes = aaveV4Spokes;
|
|
909
1098
|
exports.aaveWethGateway = aaveWethGateway;
|
|
1099
|
+
exports.assertTwyneKeysUnique = assertTwyneKeysUnique;
|
|
910
1100
|
exports.chainLogoURI = chainLogoURI;
|
|
911
1101
|
exports.chains = chains;
|
|
1102
|
+
exports.clearRemoteCache = clearRemoteCache;
|
|
912
1103
|
exports.compoundV2Oracles = compoundV2Oracles;
|
|
913
1104
|
exports.compoundV2OraclesConfig = compoundV2OraclesConfig;
|
|
914
1105
|
exports.compoundV2Pools = compoundV2Pools;
|
|
@@ -942,6 +1133,7 @@ exports.exactlyMarketByAsset = exactlyMarketByAsset;
|
|
|
942
1133
|
exports.exactlyMarkets = exactlyMarkets;
|
|
943
1134
|
exports.exactlyMarketsByChain = exactlyMarketsByChain;
|
|
944
1135
|
exports.fetchMainTokensMap = fetchMainTokensMap;
|
|
1136
|
+
exports.fetchRemoteJson = fetchRemoteJson;
|
|
945
1137
|
exports.fetchTokenList = fetchTokenList;
|
|
946
1138
|
exports.fetchTokenListFile = fetchTokenListFile;
|
|
947
1139
|
exports.fetchTokenLists = fetchTokenLists;
|
|
@@ -1011,6 +1203,8 @@ exports.llamaLendMarketByVault = llamaLendMarketByVault;
|
|
|
1011
1203
|
exports.llamaLendMarkets = llamaLendMarkets;
|
|
1012
1204
|
exports.llamaLendSupportsDelegation = llamaLendSupportsDelegation;
|
|
1013
1205
|
exports.llamaLendSupportsLeverage = llamaLendSupportsLeverage;
|
|
1206
|
+
exports.loadCached = loadCached;
|
|
1207
|
+
exports.loadCachedJson = loadCachedJson;
|
|
1014
1208
|
exports.midnightApiBaseOverride = midnightApiBaseOverride;
|
|
1015
1209
|
exports.midnightConfig = midnightConfig;
|
|
1016
1210
|
exports.midnightMarketById = midnightMarketById;
|
|
@@ -1041,6 +1235,8 @@ exports.riverLendersByChain = riverLendersByChain;
|
|
|
1041
1235
|
exports.riverMarketByIndex = riverMarketByIndex;
|
|
1042
1236
|
exports.riverMarkets = riverMarkets;
|
|
1043
1237
|
exports.setMidnightApiBase = setMidnightApiBase;
|
|
1238
|
+
exports.setRemoteCacheGithubToken = setRemoteCacheGithubToken;
|
|
1239
|
+
exports.setRemoteCacheStore = setRemoteCacheStore;
|
|
1044
1240
|
exports.setTermApiBase = setTermApiBase;
|
|
1045
1241
|
exports.siloMarkets = siloMarkets;
|
|
1046
1242
|
exports.siloMarketsV3 = siloMarketsV3;
|
|
@@ -1068,6 +1264,18 @@ exports.termMaxChainIds = termMaxChainIds;
|
|
|
1068
1264
|
exports.termMaxConfig = termMaxConfig;
|
|
1069
1265
|
exports.termMaxConfigByChain = termMaxConfigByChain;
|
|
1070
1266
|
exports.termMaxLenderKey = termMaxLenderKey;
|
|
1267
|
+
exports.twyneBaseKey = twyneBaseKey;
|
|
1268
|
+
exports.twyneBeaconFor = twyneBeaconFor;
|
|
1269
|
+
exports.twyneChainData = twyneChainData;
|
|
1270
|
+
exports.twyneConfig = twyneConfig;
|
|
1271
|
+
exports.twyneConfigFor = twyneConfigFor;
|
|
1272
|
+
exports.twyneLendersByChain = twyneLendersByChain;
|
|
1273
|
+
exports.twyneMarketByKey = twyneMarketByKey;
|
|
1274
|
+
exports.twyneMarketFor = twyneMarketFor;
|
|
1275
|
+
exports.twyneMarketKey = twyneMarketKey;
|
|
1276
|
+
exports.twyneMarkets = twyneMarkets;
|
|
1277
|
+
exports.twyneMarketsForIntermediateVault = twyneMarketsForIntermediateVault;
|
|
1278
|
+
exports.twyneOperator = twyneOperator;
|
|
1071
1279
|
exports.usddChainData = usddChainData;
|
|
1072
1280
|
exports.usddConfig = usddConfig;
|
|
1073
1281
|
exports.usddConfigFor = usddConfigFor;
|
package/dist/index.mjs
CHANGED
|
@@ -93,6 +93,8 @@ var LENDER_DATA_DEFAULTS = () => ({
|
|
|
93
93
|
llamaLendMarkets: {},
|
|
94
94
|
resupplyConfig: {},
|
|
95
95
|
curvanceConfig: {},
|
|
96
|
+
twyneConfig: {},
|
|
97
|
+
twyneMarkets: {},
|
|
96
98
|
fraxlendConfig: {},
|
|
97
99
|
eulerConfigs: {},
|
|
98
100
|
eulerVaults: {},
|
|
@@ -743,6 +745,60 @@ var curvancePositionManager = (lender, chainId, marketManager, kind) => {
|
|
|
743
745
|
return pms.simplePositionManager ?? pms.vaultPositionManager ?? pms.nativeVaultPositionManager ?? pms.pendlePtPositionManager;
|
|
744
746
|
};
|
|
745
747
|
|
|
748
|
+
// src/lending/twyne.ts
|
|
749
|
+
var twyneConfig = () => getGlobalData2()?.twyneConfig;
|
|
750
|
+
var twyneConfigFor = (lender, chainId) => getGlobalData2()?.twyneConfig?.[twyneBaseKey(lender)]?.[String(chainId)];
|
|
751
|
+
var twyneMarkets = () => getGlobalData2()?.twyneMarkets;
|
|
752
|
+
var twyneChainData = (lender, chainId) => getGlobalData2()?.twyneMarkets?.[twyneBaseKey(lender)]?.[String(chainId)];
|
|
753
|
+
var twyneBaseKey = (lender) => lender?.startsWith("TWYNE") ? "TWYNE" : lender;
|
|
754
|
+
var twyneMarketKey = (chainId, intermediateVault, targetAsset) => `TWYNE_${chainId}_${intermediateVault.replace(/^0x/i, "").toUpperCase()}_${targetAsset.replace(/^0x/i, "").toUpperCase()}`;
|
|
755
|
+
var assertTwyneKeysUnique = (markets, chainId) => {
|
|
756
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
757
|
+
for (const m of markets) {
|
|
758
|
+
const key = twyneMarketKey(chainId, m.intermediateVault, m.targetAsset);
|
|
759
|
+
const triple = `${m.intermediateVault}|${m.targetVault}|${m.targetAsset}`.toLowerCase();
|
|
760
|
+
const seen = byKey.get(key);
|
|
761
|
+
if (seen && seen !== triple) {
|
|
762
|
+
throw new Error(
|
|
763
|
+
`assertTwyneKeysUnique: key ${key} maps to two triples (${seen} and ${triple}). The target vault must be added back into the key.`
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
byKey.set(key, triple);
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
var twyneMarketByKey = (lender, chainId) => {
|
|
770
|
+
const upper = lender?.toUpperCase();
|
|
771
|
+
return twyneChainData(lender, chainId)?.markets?.find((m) => m.key.toUpperCase() === upper);
|
|
772
|
+
};
|
|
773
|
+
var twyneMarketFor = (lender, chainId, intermediateVault, targetAsset) => {
|
|
774
|
+
const iv = intermediateVault?.toLowerCase();
|
|
775
|
+
const asset = targetAsset?.toLowerCase();
|
|
776
|
+
return twyneChainData(lender, chainId)?.markets?.find(
|
|
777
|
+
(m) => m.intermediateVault.toLowerCase() === iv && m.targetAsset.toLowerCase() === asset
|
|
778
|
+
);
|
|
779
|
+
};
|
|
780
|
+
var twyneMarketsForIntermediateVault = (lender, chainId, intermediateVault) => {
|
|
781
|
+
const iv = intermediateVault?.toLowerCase();
|
|
782
|
+
return (twyneChainData(lender, chainId)?.markets ?? []).filter((m) => m.intermediateVault.toLowerCase() === iv);
|
|
783
|
+
};
|
|
784
|
+
var twyneLendersByChain = (chainId) => {
|
|
785
|
+
const cfg = getGlobalData2()?.twyneConfig ?? {};
|
|
786
|
+
const chain = String(chainId);
|
|
787
|
+
return Object.keys(cfg).filter((lender) => !!cfg[lender]?.[chain]?.collateralVaultFactory);
|
|
788
|
+
};
|
|
789
|
+
var twyneOperator = (lender, chainId, vaultType, kind) => twyneConfigFor(lender, chainId)?.operators?.[vaultType]?.[kind];
|
|
790
|
+
var twyneBeaconFor = (lender, chainId, targetVault) => {
|
|
791
|
+
const map = twyneConfigFor(lender, chainId)?.beacons;
|
|
792
|
+
if (!map || !targetVault) return void 0;
|
|
793
|
+
const direct = map[targetVault];
|
|
794
|
+
if (direct) return direct;
|
|
795
|
+
const target = targetVault.toLowerCase();
|
|
796
|
+
for (const key of Object.keys(map)) {
|
|
797
|
+
if (key.toLowerCase() === target) return map[key];
|
|
798
|
+
}
|
|
799
|
+
return void 0;
|
|
800
|
+
};
|
|
801
|
+
|
|
746
802
|
// src/lending/fraxlend.ts
|
|
747
803
|
var FRAXLEND_ETHEREUM_CONFIG = {
|
|
748
804
|
deployer: "0xD6E9D27C75Afd88ad24Cd5EdccdC76fd2fc3A751",
|
|
@@ -860,12 +916,143 @@ var parseFraxlendPairKey = (lender) => {
|
|
|
860
916
|
return { chainId: m[1], pair: m[2].toLowerCase() };
|
|
861
917
|
};
|
|
862
918
|
|
|
919
|
+
// src/remoteCache.ts
|
|
920
|
+
var FRESH_TTL_MS = 5 * 60 * 1e3;
|
|
921
|
+
var REMOTE_CACHE_STORE_TTL_S = 7 * 24 * 60 * 60;
|
|
922
|
+
var STATE_KEY = "__1delta_remote_cache__";
|
|
923
|
+
function getState() {
|
|
924
|
+
const g = globalThis;
|
|
925
|
+
if (!g[STATE_KEY]) g[STATE_KEY] = { memo: /* @__PURE__ */ new Map(), inflight: /* @__PURE__ */ new Map() };
|
|
926
|
+
return g[STATE_KEY];
|
|
927
|
+
}
|
|
928
|
+
function setRemoteCacheStore(next) {
|
|
929
|
+
getState().store = next;
|
|
930
|
+
}
|
|
931
|
+
function setRemoteCacheGithubToken(token) {
|
|
932
|
+
getState().githubToken = token;
|
|
933
|
+
}
|
|
934
|
+
function clearRemoteCache() {
|
|
935
|
+
const state = getState();
|
|
936
|
+
state.memo.clear();
|
|
937
|
+
state.inflight.clear();
|
|
938
|
+
}
|
|
939
|
+
var RAW_HOST = "https://raw.githubusercontent.com/";
|
|
940
|
+
function githubApiMirror(url) {
|
|
941
|
+
if (!url.startsWith(RAW_HOST)) return void 0;
|
|
942
|
+
const [owner, repo, ref, ...path] = url.slice(RAW_HOST.length).split("/");
|
|
943
|
+
if (!owner || !repo || !ref || path.length === 0) return void 0;
|
|
944
|
+
return `https://api.github.com/repos/${owner}/${repo}/contents/${path.join("/")}?ref=${ref}`;
|
|
945
|
+
}
|
|
946
|
+
var RemoteFetchError = class extends Error {
|
|
947
|
+
constructor(message, status) {
|
|
948
|
+
super(message);
|
|
949
|
+
this.status = status;
|
|
950
|
+
this.name = "RemoteFetchError";
|
|
951
|
+
}
|
|
952
|
+
/** 404/410 — re-fetching cannot change the answer. */
|
|
953
|
+
get isMissing() {
|
|
954
|
+
return this.status === 404 || this.status === 410;
|
|
955
|
+
}
|
|
956
|
+
};
|
|
957
|
+
async function fetchRemoteJson(url, label) {
|
|
958
|
+
const res = await fetch(url);
|
|
959
|
+
if (res.ok) return await res.json();
|
|
960
|
+
const mirror = githubApiMirror(url);
|
|
961
|
+
const worthRetrying = res.status === 429 || res.status >= 500;
|
|
962
|
+
if (!mirror || !worthRetrying) {
|
|
963
|
+
throw new RemoteFetchError(`${label} fetch failed: HTTP ${res.status} from ${new URL(url).host}`, res.status);
|
|
964
|
+
}
|
|
965
|
+
const headers = {
|
|
966
|
+
Accept: "application/vnd.github.raw",
|
|
967
|
+
// The GitHub API rejects a request that sends no User-Agent.
|
|
968
|
+
"User-Agent": "1delta-data-sdk"
|
|
969
|
+
};
|
|
970
|
+
const token = getState().githubToken;
|
|
971
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
972
|
+
const viaApi = await fetch(mirror, { headers });
|
|
973
|
+
if (!viaApi.ok) {
|
|
974
|
+
throw new RemoteFetchError(
|
|
975
|
+
`${label} fetch failed: HTTP ${res.status} from raw.githubusercontent.com, HTTP ${viaApi.status} from api.github.com`,
|
|
976
|
+
res.status
|
|
977
|
+
);
|
|
978
|
+
}
|
|
979
|
+
return await viaApi.json();
|
|
980
|
+
}
|
|
981
|
+
var storeKey = (key) => `remote-cache:v1:${key}`;
|
|
982
|
+
async function readStore(key, isValid) {
|
|
983
|
+
const { store } = getState();
|
|
984
|
+
if (!store) return void 0;
|
|
985
|
+
try {
|
|
986
|
+
const raw = await store.get(storeKey(key));
|
|
987
|
+
if (!raw) return void 0;
|
|
988
|
+
const parsed = JSON.parse(raw);
|
|
989
|
+
if (typeof parsed?.at !== "number" || !isValid(parsed.value)) return void 0;
|
|
990
|
+
return parsed;
|
|
991
|
+
} catch {
|
|
992
|
+
return void 0;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
async function writeStore(key, entry) {
|
|
996
|
+
const { store } = getState();
|
|
997
|
+
if (!store) return;
|
|
998
|
+
try {
|
|
999
|
+
await store.put(storeKey(key), JSON.stringify(entry), REMOTE_CACHE_STORE_TTL_S);
|
|
1000
|
+
} catch {
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
async function loadCached(key, load, isValid) {
|
|
1004
|
+
const { memo, inflight } = getState();
|
|
1005
|
+
const cached = memo.get(key);
|
|
1006
|
+
if (cached && Date.now() - cached.at < FRESH_TTL_MS) return cached.value;
|
|
1007
|
+
const pending = inflight.get(key);
|
|
1008
|
+
if (pending) return pending;
|
|
1009
|
+
const task = (async () => {
|
|
1010
|
+
const stored = await readStore(key, isValid);
|
|
1011
|
+
if (stored && Date.now() - stored.at < FRESH_TTL_MS) {
|
|
1012
|
+
memo.set(key, stored);
|
|
1013
|
+
return stored.value;
|
|
1014
|
+
}
|
|
1015
|
+
try {
|
|
1016
|
+
const value = await load();
|
|
1017
|
+
const entry = { at: Date.now(), value };
|
|
1018
|
+
memo.set(key, entry);
|
|
1019
|
+
await writeStore(key, entry);
|
|
1020
|
+
return value;
|
|
1021
|
+
} catch (error) {
|
|
1022
|
+
const stale = memo.get(key) ?? stored;
|
|
1023
|
+
if (!stale) throw error;
|
|
1024
|
+
memo.set(key, stale);
|
|
1025
|
+
return stale.value;
|
|
1026
|
+
}
|
|
1027
|
+
})().finally(() => {
|
|
1028
|
+
inflight.delete(key);
|
|
1029
|
+
});
|
|
1030
|
+
inflight.set(key, task);
|
|
1031
|
+
return task;
|
|
1032
|
+
}
|
|
1033
|
+
async function loadCachedJson(key, url, isValid, label = key) {
|
|
1034
|
+
return loadCached(key, () => fetchRemoteJson(url, label), isValid);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
863
1037
|
// src/tokens.ts
|
|
864
1038
|
var getListUrl = (chainId) => `https://raw.githubusercontent.com/1delta-DAO/token-lists/main/${chainId}.json`;
|
|
1039
|
+
var MAIN_TOKENS_URL = "https://raw.githubusercontent.com/1delta-DAO/token-lists/main/main-tokens.json";
|
|
1040
|
+
var isTokenListFile = (value) => !!value?.list && typeof value.list === "object";
|
|
1041
|
+
async function loadTokenListFile(chainId) {
|
|
1042
|
+
return loadCached(
|
|
1043
|
+
`token-list:${chainId}`,
|
|
1044
|
+
async () => {
|
|
1045
|
+
const json = await fetchRemoteJson(getListUrl(chainId), `Token list for chain ${chainId}`);
|
|
1046
|
+
return {
|
|
1047
|
+
list: json.list,
|
|
1048
|
+
mainTokens: json.mainTokens ?? []
|
|
1049
|
+
};
|
|
1050
|
+
},
|
|
1051
|
+
isTokenListFile
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
865
1054
|
async function fetchTokenList(chainId) {
|
|
866
|
-
|
|
867
|
-
if (!data.ok) throw new Error(`HTTP error! status: ${data.status}`);
|
|
868
|
-
return (await data.json()).list;
|
|
1055
|
+
return (await loadTokenListFile(chainId)).list;
|
|
869
1056
|
}
|
|
870
1057
|
async function fetchTokenLists(chainIds) {
|
|
871
1058
|
const promises = chainIds.map(async (chainId) => {
|
|
@@ -876,19 +1063,19 @@ async function fetchTokenLists(chainIds) {
|
|
|
876
1063
|
return Object.fromEntries(results);
|
|
877
1064
|
}
|
|
878
1065
|
async function fetchTokenListFile(chainId) {
|
|
879
|
-
const
|
|
880
|
-
|
|
881
|
-
const json = await data.json();
|
|
882
|
-
return {
|
|
883
|
-
list: json.list,
|
|
884
|
-
mainTokens: json.mainTokens ?? []
|
|
885
|
-
};
|
|
1066
|
+
const file = await loadTokenListFile(chainId);
|
|
1067
|
+
return { list: file.list, mainTokens: file.mainTokens };
|
|
886
1068
|
}
|
|
887
|
-
var MAIN_TOKENS_URL = "https://raw.githubusercontent.com/1delta-DAO/token-lists/main/main-tokens.json";
|
|
888
1069
|
async function fetchMainTokensMap() {
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1070
|
+
try {
|
|
1071
|
+
return await loadCached(
|
|
1072
|
+
"main-tokens",
|
|
1073
|
+
() => fetchRemoteJson(MAIN_TOKENS_URL, "main-tokens"),
|
|
1074
|
+
(value) => !!value && typeof value === "object"
|
|
1075
|
+
);
|
|
1076
|
+
} catch {
|
|
1077
|
+
return void 0;
|
|
1078
|
+
}
|
|
892
1079
|
}
|
|
893
1080
|
|
|
894
|
-
export { CURVANCE_MONAD_CONFIG, FRAXLEND_ETHEREUM_CONFIG, GEARBOX_ACCOUNT_COMPRESSOR_V310, RESUPPLY_ETHEREUM_CONFIG, aaveOracles, aaveOraclesConfig, aavePools, aaveReserves, aaveTokens, aaveV4Oracles, aaveV4Peripherals, aaveV4SpokeLenderKey, aaveV4Spokes, aaveWethGateway, chainLogoURI, chains, compoundV2Oracles, compoundV2OraclesConfig, compoundV2Pools, compoundV2Reserves, compoundV2TokenArray, compoundV2Tokens, compoundV3BaseData, compoundV3Bulker, compoundV3OraclesData, compoundV3Pools, compoundV3Reserves, curvanceConfig, curvanceConfigFor, curvanceLendersByChain, curvancePositionManager, curvancePositionManagersFor, dolomiteChains, dolomiteConfigs, dolomiteEmode, dolomiteMarketIdByToken, dssChainData, dssConfigFor, dssDebtJoin, dssDebtToken, dssMarketByIlk, eulerConfigs, eulerVaults, exactlyConfig, exactlyMarketByAddress, exactlyMarketByAsset, exactlyMarkets, exactlyMarketsByChain, fetchMainTokensMap, fetchTokenList, fetchTokenListFile, fetchTokenLists, findAaveV4PositionManager, fluidChains, fluidResolvers, fluidVaultEntries, fluidVaults, frankencoinChainData, frankencoinConfig, frankencoinConfigFor, frankencoinLendersByChain, frankencoinMarketByPosition, frankencoinMarkets, fraxlendConfig, fraxlendConfigFor, fraxlendLendersByChain, fraxlendPairKey, fraxlendPairsFor, gearboxAccountCompressor, gearboxAddressProviderV310, gearboxBotListV3, gearboxChains, gearboxMarketCompressor, gearboxMarketConfigurators, gearboxResolvers, gearboxV3LenderKey, getAaveV4GatewayAddresses, getAaveV4PositionManagers, getAaveV4SpokeAccess, getAaveV4SpokeEntry, getAaveV4SpokePeripherals, getChainEntries, getSiloV2MarketEntry, getSiloV3MarketEntry, initConfig, initializeChainData, initializeLenderData, inverseChainData, inverseConfig, inverseConfigFor, inverseLendersByChain, inverseMarketByAddress, inverseMarkets, isAaveV4OperationRestricted, isListaSmartLpAsset, liquityBranchByIndex, liquityBranchByTroveManager, liquityBranchesByChain, liquityConfig, liquityConfigFor, liquityLendersByChain, liquityMarkets, listaCollateralProvider, listaCollateralProviders, listaNativeProvider, listaSmartLpByCollateral, llamaLendApiBaseUrl, llamaLendChainData, llamaLendConfig, llamaLendConfigFor, llamaLendLenderKey, llamaLendLendersByChain, llamaLendLeverageZap, llamaLendMarketByController, llamaLendMarketByVault, llamaLendMarkets, llamaLendSupportsDelegation, llamaLendSupportsLeverage, midnightApiBaseOverride, midnightConfig, midnightMarketById, midnightMarkets, midnightMarketsByChain, morphoBundler3, morphoOracles, morphoPools, morphoTypeMarkets, morphoTypeOracles, morphoTypeVaults, parseAaveV4SpokeLenderKey, parseFraxlendPairKey, parseGearboxV3LenderKey, parseLlamaLendLenderKey, parseSiloV2LenderKey, parseSiloV3LenderKey, parseTermMaxLenderKey, resolveMidnightApiBase, resolveTermApiBase, resupplyConfig, resupplyConfigFor, resupplyLendersByChain, riverChainData, riverConfig, riverConfigFor, riverLendersByChain, riverMarketByIndex, riverMarkets, setMidnightApiBase, setTermApiBase, siloMarkets, siloMarketsV3, siloPeripherals, siloPeripheralsV3, siloV2LenderKey, siloV3LenderKey, skyChainData, skyConfig, skyConfigFor, skyLendersByChain, skyMarkets, tellerConfig, tellerConfigByChain, tellerPoolByAddress, tellerPools, tellerPoolsByChain, termApiBaseOverride, termConfig, termMarketById, termMarkets, termMarketsByChain, termMaxApiBaseUrl, termMaxChainIds, termMaxConfig, termMaxConfigByChain, termMaxLenderKey, usddChainData, usddConfig, usddConfigFor, usddLendersByChain, usddMarketByIlk, usddMarkets };
|
|
1081
|
+
export { CURVANCE_MONAD_CONFIG, FRAXLEND_ETHEREUM_CONFIG, GEARBOX_ACCOUNT_COMPRESSOR_V310, REMOTE_CACHE_STORE_TTL_S, RESUPPLY_ETHEREUM_CONFIG, RemoteFetchError, aaveOracles, aaveOraclesConfig, aavePools, aaveReserves, aaveTokens, aaveV4Oracles, aaveV4Peripherals, aaveV4SpokeLenderKey, aaveV4Spokes, aaveWethGateway, assertTwyneKeysUnique, chainLogoURI, chains, clearRemoteCache, compoundV2Oracles, compoundV2OraclesConfig, compoundV2Pools, compoundV2Reserves, compoundV2TokenArray, compoundV2Tokens, compoundV3BaseData, compoundV3Bulker, compoundV3OraclesData, compoundV3Pools, compoundV3Reserves, curvanceConfig, curvanceConfigFor, curvanceLendersByChain, curvancePositionManager, curvancePositionManagersFor, dolomiteChains, dolomiteConfigs, dolomiteEmode, dolomiteMarketIdByToken, dssChainData, dssConfigFor, dssDebtJoin, dssDebtToken, dssMarketByIlk, eulerConfigs, eulerVaults, exactlyConfig, exactlyMarketByAddress, exactlyMarketByAsset, exactlyMarkets, exactlyMarketsByChain, fetchMainTokensMap, fetchRemoteJson, fetchTokenList, fetchTokenListFile, fetchTokenLists, findAaveV4PositionManager, fluidChains, fluidResolvers, fluidVaultEntries, fluidVaults, frankencoinChainData, frankencoinConfig, frankencoinConfigFor, frankencoinLendersByChain, frankencoinMarketByPosition, frankencoinMarkets, fraxlendConfig, fraxlendConfigFor, fraxlendLendersByChain, fraxlendPairKey, fraxlendPairsFor, gearboxAccountCompressor, gearboxAddressProviderV310, gearboxBotListV3, gearboxChains, gearboxMarketCompressor, gearboxMarketConfigurators, gearboxResolvers, gearboxV3LenderKey, getAaveV4GatewayAddresses, getAaveV4PositionManagers, getAaveV4SpokeAccess, getAaveV4SpokeEntry, getAaveV4SpokePeripherals, getChainEntries, getSiloV2MarketEntry, getSiloV3MarketEntry, initConfig, initializeChainData, initializeLenderData, inverseChainData, inverseConfig, inverseConfigFor, inverseLendersByChain, inverseMarketByAddress, inverseMarkets, isAaveV4OperationRestricted, isListaSmartLpAsset, liquityBranchByIndex, liquityBranchByTroveManager, liquityBranchesByChain, liquityConfig, liquityConfigFor, liquityLendersByChain, liquityMarkets, listaCollateralProvider, listaCollateralProviders, listaNativeProvider, listaSmartLpByCollateral, llamaLendApiBaseUrl, llamaLendChainData, llamaLendConfig, llamaLendConfigFor, llamaLendLenderKey, llamaLendLendersByChain, llamaLendLeverageZap, llamaLendMarketByController, llamaLendMarketByVault, llamaLendMarkets, llamaLendSupportsDelegation, llamaLendSupportsLeverage, loadCached, loadCachedJson, midnightApiBaseOverride, midnightConfig, midnightMarketById, midnightMarkets, midnightMarketsByChain, morphoBundler3, morphoOracles, morphoPools, morphoTypeMarkets, morphoTypeOracles, morphoTypeVaults, parseAaveV4SpokeLenderKey, parseFraxlendPairKey, parseGearboxV3LenderKey, parseLlamaLendLenderKey, parseSiloV2LenderKey, parseSiloV3LenderKey, parseTermMaxLenderKey, resolveMidnightApiBase, resolveTermApiBase, resupplyConfig, resupplyConfigFor, resupplyLendersByChain, riverChainData, riverConfig, riverConfigFor, riverLendersByChain, riverMarketByIndex, riverMarkets, setMidnightApiBase, setRemoteCacheGithubToken, setRemoteCacheStore, setTermApiBase, siloMarkets, siloMarketsV3, siloPeripherals, siloPeripheralsV3, siloV2LenderKey, siloV3LenderKey, skyChainData, skyConfig, skyConfigFor, skyLendersByChain, skyMarkets, tellerConfig, tellerConfigByChain, tellerPoolByAddress, tellerPools, tellerPoolsByChain, termApiBaseOverride, termConfig, termMarketById, termMarkets, termMarketsByChain, termMaxApiBaseUrl, termMaxChainIds, termMaxConfig, termMaxConfigByChain, termMaxLenderKey, twyneBaseKey, twyneBeaconFor, twyneChainData, twyneConfig, twyneConfigFor, twyneLendersByChain, twyneMarketByKey, twyneMarketFor, twyneMarketKey, twyneMarkets, twyneMarketsForIntermediateVault, twyneOperator, usddChainData, usddConfig, usddConfigFor, usddLendersByChain, usddMarketByIlk, usddMarkets };
|
package/dist/lending/index.d.ts
CHANGED
package/dist/lending/morpho.d.ts
CHANGED
|
@@ -53,6 +53,13 @@ export type MorphoTypeVaultEntry = {
|
|
|
53
53
|
underlying: string;
|
|
54
54
|
/** Curated display name (e.g. `Lista USD1 Vault`). Optional. */
|
|
55
55
|
name?: string;
|
|
56
|
+
/** Human curator/brand name (e.g. `Steakhouse Financial`). Filled by
|
|
57
|
+
* lender-metadata's `update:vault-curators` job (Morpho curator roster
|
|
58
|
+
* joined on the on-chain `curator()`/`owner()` address, with a
|
|
59
|
+
* conservative name-derived fallback) — the chains in this registry have
|
|
60
|
+
* no Morpho-API coverage, so the on-chain fetcher cannot learn the
|
|
61
|
+
* curator any other way. */
|
|
62
|
+
curatorName?: string;
|
|
56
63
|
/** Vault interface version. `v1` = MetaMorpho (withdraw-queue → markets);
|
|
57
64
|
* `v2` = Vaults V2 (adapter-based). Drives the on-chain fetcher's
|
|
58
65
|
* allocation walk. Absent ⇒ the fetcher auto-detects on-chain
|
|
@@ -24,6 +24,7 @@ import type { GeneralReservesMap, OracleMap } from './common';
|
|
|
24
24
|
import type { CompoundV2OraclesConfigType, CompoundV2PoolsType, CompoundV2TokenArrayType, CompoundV2TokensType } from './compoundV2';
|
|
25
25
|
import type { CompoundV3BaseDataType, CompoundV3BulkerType, CompoundV3OracleDataMap, CompoundV3PoolsType } from './compoundV3';
|
|
26
26
|
import type { CurvanceConfigType } from './curvance';
|
|
27
|
+
import type { TwyneConfigType, TwyneMarketsType } from './twyne';
|
|
27
28
|
import type { DolomiteConfigsType, DolomiteEmodeType } from './dolomite';
|
|
28
29
|
import type { UsddConfigType, UsddMarketsType } from './dss';
|
|
29
30
|
import type { EulerConfigsType, EulerVaultsType } from './euler';
|
|
@@ -105,6 +106,8 @@ export interface GlobalLenderDataRegistry {
|
|
|
105
106
|
llamaLendMarkets: LlamaLendMarketsType;
|
|
106
107
|
resupplyConfig: ResupplyConfigType;
|
|
107
108
|
curvanceConfig: CurvanceConfigType;
|
|
109
|
+
twyneConfig: TwyneConfigType;
|
|
110
|
+
twyneMarkets: TwyneMarketsType;
|
|
108
111
|
fraxlendConfig: FraxlendConfigType;
|
|
109
112
|
eulerConfigs: EulerConfigsType;
|
|
110
113
|
eulerVaults: EulerVaultsType;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/** The two external protocols Twyne integrates. Mirrors the on-chain enum. */
|
|
2
|
+
export type TwyneVaultType = 'EULER_V2' | 'AAVE_V3';
|
|
3
|
+
/**
|
|
4
|
+
* The 1-click operators for one `VaultType`. Each is EVC-authenticated
|
|
5
|
+
* (`_msgSender() == collateralVault.borrower()`) and runs a Morpho Blue flash
|
|
6
|
+
* loan plus, where it swaps, Euler's `Swapper` — whose GENERIC handler executes
|
|
7
|
+
* arbitrary calldata on an arbitrary target, so any aggregator we support works
|
|
8
|
+
* without an allowlist.
|
|
9
|
+
*/
|
|
10
|
+
export type TwyneOperators = {
|
|
11
|
+
/** `executeLeverage(collateralVault, underlyingAmt, collateralAmt, flashAmt, minOut, deadline, swapData)` */
|
|
12
|
+
leverage?: string;
|
|
13
|
+
/** `executeDeleverage(collateralVault, flashAmt, maxDebt, withdrawAmt, swapData)` */
|
|
14
|
+
deleverage?: string;
|
|
15
|
+
/**
|
|
16
|
+
* `executeTeleport(collateralVault, aTokenAmount, debtAmount)` — migrate an
|
|
17
|
+
* existing external position IN.
|
|
18
|
+
*
|
|
19
|
+
* AAVE_V3 ONLY, and it carries NO swapper (its constructor takes
|
|
20
|
+
* `evc, morpho, factory, permit2, aavePool` and nothing else), so it handles
|
|
21
|
+
* the IDENTITY CASE ONLY: the user's aToken must be this market's aToken and
|
|
22
|
+
* their VARIABLE debt asset must be this market's `targetAsset`. Anything
|
|
23
|
+
* cross-asset has to fall back to the generic migrate. There is no
|
|
24
|
+
* teleport-OUT.
|
|
25
|
+
*/
|
|
26
|
+
teleport?: string;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* The BeaconProxy beacon for one target vault, and OPTIONALLY a cached
|
|
30
|
+
* init-code hash for the CREATE2 address predictor.
|
|
31
|
+
*
|
|
32
|
+
* `CollateralVaultFactory.createCollateralVault` deploys
|
|
33
|
+
* `new BeaconProxy{salt: keccak256(abi.encodePacked(borrower, nonce))}(beacon, "")`,
|
|
34
|
+
* so a position's address IS predictable — but the init-code hash is a property
|
|
35
|
+
* of the FACTORY IMPLEMENTATION, not of the protocol. That factory is a UUPS
|
|
36
|
+
* proxy which has already been upgraded four times, and the embedded BeaconProxy
|
|
37
|
+
* creation code is not identical across those implementations.
|
|
38
|
+
*
|
|
39
|
+
* So `evc.batchSimulation` is the source of truth for a new vault's address, and
|
|
40
|
+
* a cached hash here is a FAST PATH ONLY. It is valid exactly while
|
|
41
|
+
* `factoryImplementation` still matches the live ERC-1967 implementation slot
|
|
42
|
+
* (`0x360894…382bbc`) — one `eth_getStorageAt` to check. On a mismatch, fall
|
|
43
|
+
* back; never guess, because a stale hash yields a plausible EMPTY address and
|
|
44
|
+
* the leverage batch would then `skim`/`borrow` on nothing.
|
|
45
|
+
*
|
|
46
|
+
* See TWYNE_BUILD_PLAN.md D3.
|
|
47
|
+
*/
|
|
48
|
+
export type TwyneBeacon = {
|
|
49
|
+
beacon: string;
|
|
50
|
+
/** `keccak256(BeaconProxy.creationCode ++ abi.encode(beacon, "0x"))`. */
|
|
51
|
+
initCodeHash?: string;
|
|
52
|
+
/** The factory implementation `initCodeHash` was derived from. A cached hash
|
|
53
|
+
* is only usable while this still matches the live implementation slot. */
|
|
54
|
+
factoryImplementation?: string;
|
|
55
|
+
};
|
|
56
|
+
/** Per-chain, protocol-level (evergreen) Twyne addresses. */
|
|
57
|
+
export type TwyneConfigChain = {
|
|
58
|
+
/**
|
|
59
|
+
* Twyne's OWN EVC — `0xef39D649…` on Ethereum, NOT Euler's canonical
|
|
60
|
+
* `0x0C9a3dd6…`. Every collateral-vault write is authenticated through it
|
|
61
|
+
* (`EVCUtil._msgSender()`), which is also what lets an EVC account operator
|
|
62
|
+
* drive a position. Reusing the Euler address here is a silent failure.
|
|
63
|
+
*/
|
|
64
|
+
evc: string;
|
|
65
|
+
/** `CollateralVaultFactory` — `createCollateralVault`, `getCollateralVaults`,
|
|
66
|
+
* `isCollateralVault`, `nonce`, `collateralVaultBeacon`, `paused`. */
|
|
67
|
+
collateralVaultFactory: string;
|
|
68
|
+
/** `VaultManager` — the whitelists and the LTV bounds
|
|
69
|
+
* (`maxTwyneLTVs`, `externalLiqBuffers`, `isAllowedTargetAssets`). */
|
|
70
|
+
vaultManager: string;
|
|
71
|
+
/** Euler-style oracle router used to price collateral vaults. */
|
|
72
|
+
oracleRouter?: string;
|
|
73
|
+
/** Morpho Blue — the (free) flash source every operator uses. */
|
|
74
|
+
morpho?: string;
|
|
75
|
+
/** Euler periphery `Swapper` (GENERIC handler = no target allowlist). */
|
|
76
|
+
swapper?: string;
|
|
77
|
+
/** Euler periphery `SwapVerifier` — used by the Euler leverage operator. */
|
|
78
|
+
swapVerifier?: string;
|
|
79
|
+
/** Permit2, as the operators pull user funds through `SafeERC20Lib`. */
|
|
80
|
+
permit2?: string;
|
|
81
|
+
/** 1-click operators per external protocol. */
|
|
82
|
+
operators?: Partial<Record<TwyneVaultType, TwyneOperators>>;
|
|
83
|
+
/**
|
|
84
|
+
* Collateral-vault beacons keyed by TARGET VAULT (the factory keys its
|
|
85
|
+
* beacon map that way), for the CREATE2 address predictor.
|
|
86
|
+
*/
|
|
87
|
+
beacons?: {
|
|
88
|
+
[targetVault: string]: TwyneBeacon;
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
export type TwyneConfigType = {
|
|
92
|
+
[lender: string]: {
|
|
93
|
+
[chainId: string]: TwyneConfigChain;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* One whitelisted Twyne market.
|
|
98
|
+
*
|
|
99
|
+
* Numeric risk fields are strings/numbers in the protocol's own scale (1e4 for
|
|
100
|
+
* LTVs and the buffer) because they are sourced from JSON metadata; live values
|
|
101
|
+
* are re-read on every refresh, since the external liquidation threshold moves
|
|
102
|
+
* — Aave's PT eMode thresholds ramp down as a PT ages, and Twyne's LOWER bound
|
|
103
|
+
* is derived from it.
|
|
104
|
+
*/
|
|
105
|
+
export type TwyneMarketConfig = {
|
|
106
|
+
/** `TWYNE_<chainId>_<INTERMEDIATE_VAULT>_<TARGET_ASSET>`, upper-cased hex. */
|
|
107
|
+
key: string;
|
|
108
|
+
vaultType: TwyneVaultType;
|
|
109
|
+
/** The EVK credit vault. Also the market's identity for LTV bounds. */
|
|
110
|
+
intermediateVault: string;
|
|
111
|
+
/** Euler: the debt eVault. Aave: the Pool. */
|
|
112
|
+
targetVault: string;
|
|
113
|
+
/** The borrowed asset (e.g. USDe, WETH). */
|
|
114
|
+
targetAsset: string;
|
|
115
|
+
/** The intermediate vault's asset — an `eToken` or a Twyne aToken wrapper.
|
|
116
|
+
* This is what the collateral vault actually holds. */
|
|
117
|
+
collateralAsset: string;
|
|
118
|
+
/** What the user thinks they deposited (WETH / wstETH / a Pendle PT).
|
|
119
|
+
* `depositUnderlying` / `redeemUnderlying` take this. */
|
|
120
|
+
underlyingAsset: string;
|
|
121
|
+
/** Aave only: the aToken the wrapper wraps. */
|
|
122
|
+
aToken?: string;
|
|
123
|
+
/** Aave only: the eMode category the collateral vault is initialized with.
|
|
124
|
+
* The external liquidation threshold must be read from THIS category when
|
|
125
|
+
* the reserve is in its collateral bitmap — never the plain reserve LT. */
|
|
126
|
+
categoryId?: number;
|
|
127
|
+
/** Governance ceiling on a position's chosen liq LTV, 1e4. */
|
|
128
|
+
maxTwyneLiqLTV?: number;
|
|
129
|
+
/** Safety margin below the external threshold, 1e4 — the LOWER bound is
|
|
130
|
+
* `extLiqLTV * externalLiqBuffer / 1e4`. */
|
|
131
|
+
externalLiqBuffer?: number;
|
|
132
|
+
/** Maturity of the collateral, unix seconds, when the collateral is a
|
|
133
|
+
* fixed-term asset (every live PT market). Absent for wstETH/WETH. */
|
|
134
|
+
collateralMaturity?: number;
|
|
135
|
+
/** The credit vault's `IRMTwyneCurve` — the join key into the `irm` repo's
|
|
136
|
+
* `twyne.json`, where the fitted `gamma` lives. */
|
|
137
|
+
creditIrm?: string;
|
|
138
|
+
/** The collateral vault BeaconProxy beacon for this market's target vault.
|
|
139
|
+
* Recorded for the CREATE2 predictor's fast path; the init-code hash is
|
|
140
|
+
* deliberately NOT pinned (see TWYNE_BUILD_PLAN.md D3). */
|
|
141
|
+
beacon?: string;
|
|
142
|
+
/** Display only. */
|
|
143
|
+
symbol?: string;
|
|
144
|
+
name?: string;
|
|
145
|
+
};
|
|
146
|
+
export type TwyneChainData = {
|
|
147
|
+
markets: TwyneMarketConfig[];
|
|
148
|
+
};
|
|
149
|
+
export type TwyneMarketsType = {
|
|
150
|
+
[lender: string]: {
|
|
151
|
+
[chainId: string]: TwyneChainData;
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
export declare const twyneConfig: () => TwyneConfigType;
|
|
155
|
+
/** Deployment seed for one Twyne lender on a chain (undefined if unloaded). */
|
|
156
|
+
export declare const twyneConfigFor: (lender: string, chainId: string | number) => TwyneConfigChain | undefined;
|
|
157
|
+
export declare const twyneMarkets: () => TwyneMarketsType;
|
|
158
|
+
/** Generated chain data (curated market roster; undefined if unloaded). */
|
|
159
|
+
export declare const twyneChainData: (lender: string, chainId: string | number) => TwyneChainData | undefined;
|
|
160
|
+
/**
|
|
161
|
+
* The bare lender key a per-market key belongs to. Per-market keys are
|
|
162
|
+
* `TWYNE_<chainId>_<iv>_<targetAsset>`, so the base is simply the first
|
|
163
|
+
* segment — kept as a function because callers pass both forms
|
|
164
|
+
* interchangeably.
|
|
165
|
+
*/
|
|
166
|
+
export declare const twyneBaseKey: (lender: string) => string;
|
|
167
|
+
/**
|
|
168
|
+
* Build the per-market lender key for a triple.
|
|
169
|
+
*
|
|
170
|
+
* The TARGET VAULT is deliberately NOT in the key: it is redundant on every
|
|
171
|
+
* live market (implied by the asset on Euler, always the Pool on Aave), and
|
|
172
|
+
* every other fan-out lender in this registry uses a one-or-two-identifier key.
|
|
173
|
+
* `assertTwyneKeysUnique` guards the day that assumption breaks.
|
|
174
|
+
*/
|
|
175
|
+
export declare const twyneMarketKey: (chainId: string | number, intermediateVault: string, targetAsset: string) => string;
|
|
176
|
+
/**
|
|
177
|
+
* Throw if two distinct `(intermediateVault, targetVault, targetAsset)` triples
|
|
178
|
+
* collapse onto one key.
|
|
179
|
+
*
|
|
180
|
+
* This cannot happen with the eight live markets, but it is exactly the failure
|
|
181
|
+
* that would silently MERGE two markets — the same lender key resolving to two
|
|
182
|
+
* different debt venues — so the roster is validated at load rather than
|
|
183
|
+
* trusted. Called by the generator and by the margin-fetcher on ingest.
|
|
184
|
+
*/
|
|
185
|
+
export declare const assertTwyneKeysUnique: (markets: TwyneMarketConfig[], chainId: string | number) => void;
|
|
186
|
+
/** Find one Twyne market by its per-market lender key. */
|
|
187
|
+
export declare const twyneMarketByKey: (lender: string, chainId: string | number) => TwyneMarketConfig | undefined;
|
|
188
|
+
/** Find one Twyne market by its (intermediate vault, target asset) pair. */
|
|
189
|
+
export declare const twyneMarketFor: (lender: string, chainId: string | number, intermediateVault: string, targetAsset: string) => TwyneMarketConfig | undefined;
|
|
190
|
+
/** Every market that reserves credit from one intermediate (credit) vault. */
|
|
191
|
+
export declare const twyneMarketsForIntermediateVault: (lender: string, chainId: string | number, intermediateVault: string) => TwyneMarketConfig[];
|
|
192
|
+
/**
|
|
193
|
+
* All Twyne lender keys with config on a chain (drives getLendersForChain).
|
|
194
|
+
* Gated on the collateral vault FACTORY — the one address every flow needs, and
|
|
195
|
+
* the bootstrap for position discovery.
|
|
196
|
+
*/
|
|
197
|
+
export declare const twyneLendersByChain: (chainId: string | number) => string[];
|
|
198
|
+
/**
|
|
199
|
+
* The operator to drive for a market's `VaultType`, or `undefined` when Twyne
|
|
200
|
+
* has not deployed one — a real state, not a gap: there is no Euler-side
|
|
201
|
+
* teleport at all, and the docs' address table omits deployed operators, so
|
|
202
|
+
* absence here must be reported rather than defaulted.
|
|
203
|
+
*/
|
|
204
|
+
export declare const twyneOperator: (lender: string, chainId: string | number, vaultType: TwyneVaultType, kind: keyof TwyneOperators) => string | undefined;
|
|
205
|
+
/**
|
|
206
|
+
* Beacon + init-code hash for a target vault, for the CREATE2 predictor.
|
|
207
|
+
* Lookup is case-insensitive because target-vault addresses arrive from config
|
|
208
|
+
* and from chain in mixed checksum forms.
|
|
209
|
+
*/
|
|
210
|
+
export declare const twyneBeaconFor: (lender: string, chainId: string | number, targetVault: string) => TwyneBeacon | undefined;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache for the static JSON this SDK reads from GitHub (token lists, the chain
|
|
3
|
+
* registry).
|
|
4
|
+
*
|
|
5
|
+
* raw.githubusercontent.com rate-limits per EGRESS IP, and Cloudflare Workers
|
|
6
|
+
* share theirs — so an uncached fetch per request 429s in bursts. Before this
|
|
7
|
+
* existed that surfaced as a hard `INTERNAL_ERROR` 500 on every route needing a
|
|
8
|
+
* token list, and as a silently EMPTY chain registry (every chain rendered as
|
|
9
|
+
* "Chain 8453") that a 10-minute init cache then pinned in place.
|
|
10
|
+
*
|
|
11
|
+
* These files change on the order of days, so serving a stale copy is strictly
|
|
12
|
+
* better than failing — hence the stale-on-error fallback, which is the part
|
|
13
|
+
* that actually survives an outage.
|
|
14
|
+
*/
|
|
15
|
+
/** How long a persisted copy is kept around purely as the stale fallback. */
|
|
16
|
+
export declare const REMOTE_CACHE_STORE_TTL_S: number;
|
|
17
|
+
/**
|
|
18
|
+
* Durable backing store — a Cloudflare KV namespace in the workers, absent
|
|
19
|
+
* everywhere else. Without one the cache is per-process, which still collapses
|
|
20
|
+
* N requests into one upstream fetch but cannot help a cold start; with one, a
|
|
21
|
+
* fresh isolate never touches GitHub at all.
|
|
22
|
+
*/
|
|
23
|
+
export interface RemoteCacheStore {
|
|
24
|
+
get(key: string): Promise<string | null>;
|
|
25
|
+
put(key: string, value: string, ttlSeconds: number): Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
/** Installs (or with `undefined`, removes) the durable store. */
|
|
28
|
+
export declare function setRemoteCacheStore(next: RemoteCacheStore | undefined): void;
|
|
29
|
+
/**
|
|
30
|
+
* Optional GitHub token for the API mirror below. Unauthenticated it allows 60
|
|
31
|
+
* requests/hour per IP, which is thin for a shared Worker egress; a token
|
|
32
|
+
* raises that to 5,000. Only ever used on the fallback path.
|
|
33
|
+
*/
|
|
34
|
+
export declare function setRemoteCacheGithubToken(token: string | undefined): void;
|
|
35
|
+
/** Drops every cached value. Intended for tests. */
|
|
36
|
+
export declare function clearRemoteCache(): void;
|
|
37
|
+
/**
|
|
38
|
+
* A non-2xx from the remote. Carries the ORIGIN status so a caller can tell a
|
|
39
|
+
* settled answer (404/410 — the file is not published) from a transient one
|
|
40
|
+
* (429/5xx — the same URL may work a second later). Callers that collapse the
|
|
41
|
+
* two re-fetch known-missing files forever, or treat a rate limit as proof that
|
|
42
|
+
* a lender has no config.
|
|
43
|
+
*/
|
|
44
|
+
export declare class RemoteFetchError extends Error {
|
|
45
|
+
/** Status from the ORIGIN host, not from the mirror. */
|
|
46
|
+
readonly status: number;
|
|
47
|
+
constructor(message: string,
|
|
48
|
+
/** Status from the ORIGIN host, not from the mirror. */
|
|
49
|
+
status: number);
|
|
50
|
+
/** 404/410 — re-fetching cannot change the answer. */
|
|
51
|
+
get isMissing(): boolean;
|
|
52
|
+
}
|
|
53
|
+
/** Fetches JSON from `url`, retrying via the GitHub API when raw rate-limits. */
|
|
54
|
+
export declare function fetchRemoteJson<T>(url: string, label: string): Promise<T>;
|
|
55
|
+
/**
|
|
56
|
+
* Serves `key` from memory, then the durable store, then `load()` — and on a
|
|
57
|
+
* failed `load()` falls back to whatever stale copy either layer holds, only
|
|
58
|
+
* rethrowing when there is nothing to fall back to.
|
|
59
|
+
*
|
|
60
|
+
* Concurrent callers for the same key share one in-flight load.
|
|
61
|
+
*
|
|
62
|
+
* `isValid` guards the durable layer: a truncated or shape-changed entry is
|
|
63
|
+
* treated as absent rather than served, since a bad token list silently
|
|
64
|
+
* mis-decimals every balance built from it.
|
|
65
|
+
*/
|
|
66
|
+
export declare function loadCached<T>(key: string, load: () => Promise<T>, isValid: (value: any) => boolean): Promise<T>;
|
|
67
|
+
/** `loadCached` over a JSON URL, failing on any non-2xx so stale can take over. */
|
|
68
|
+
export declare function loadCachedJson<T>(key: string, url: string, isValid: (value: any) => boolean, label?: string): Promise<T>;
|
package/dist/tokens.d.ts
CHANGED
|
@@ -13,10 +13,6 @@ export interface TokenListEntry {
|
|
|
13
13
|
type TokenList = {
|
|
14
14
|
[addr: string]: TokenListEntry;
|
|
15
15
|
};
|
|
16
|
-
export declare function fetchTokenList(chainId: string): Promise<TokenList>;
|
|
17
|
-
export declare function fetchTokenLists(chainIds: string[]): Promise<{
|
|
18
|
-
[chainId: string]: TokenList;
|
|
19
|
-
}>;
|
|
20
16
|
export interface TokenListFileData {
|
|
21
17
|
list: TokenList;
|
|
22
18
|
/**
|
|
@@ -26,7 +22,18 @@ export interface TokenListFileData {
|
|
|
26
22
|
*/
|
|
27
23
|
mainTokens: string[];
|
|
28
24
|
}
|
|
29
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* NOTE: cached — the returned `list` is a SHARED object. Treat it as read-only;
|
|
27
|
+
* mutating it corrupts the copy every other caller sees.
|
|
28
|
+
*/
|
|
29
|
+
export declare function fetchTokenList(chainId: string): Promise<TokenList>;
|
|
30
|
+
export declare function fetchTokenLists(chainIds: string[]): Promise<{
|
|
31
|
+
[chainId: string]: TokenList;
|
|
32
|
+
}>;
|
|
33
|
+
/**
|
|
34
|
+
* Like {@link fetchTokenList} but keeps the `mainTokens` curation. Same
|
|
35
|
+
* read-only caveat on `list`.
|
|
36
|
+
*/
|
|
30
37
|
export declare function fetchTokenListFile(chainId: string): Promise<TokenListFileData>;
|
|
31
38
|
/**
|
|
32
39
|
* Aggregated chainId → mainTokens map (one small file for all chains, so
|