@defisaver/positions-sdk 2.1.159 → 2.1.160-morpho-vault-dev
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/cjs/constants/index.d.ts +1 -0
- package/cjs/constants/index.js +4 -1
- package/cjs/helpers/morphoBlueHelpers/index.js +3 -4
- package/cjs/savings/index.js +5 -2
- package/cjs/savings/morphoVaults/index.d.ts +1 -1
- package/cjs/savings/morphoVaults/index.js +57 -19
- package/cjs/savings/morphoVaults/options.d.ts +1 -0
- package/cjs/savings/morphoVaults/options.js +50 -1
- package/cjs/types/savings/morphoVaults.d.ts +9 -2
- package/cjs/types/savings/morphoVaults.js +8 -1
- package/esm/constants/index.d.ts +1 -0
- package/esm/constants/index.js +3 -0
- package/esm/helpers/morphoBlueHelpers/index.js +4 -5
- package/esm/savings/index.js +5 -2
- package/esm/savings/morphoVaults/index.d.ts +1 -1
- package/esm/savings/morphoVaults/index.js +58 -20
- package/esm/savings/morphoVaults/options.d.ts +1 -0
- package/esm/savings/morphoVaults/options.js +50 -1
- package/esm/types/savings/morphoVaults.d.ts +9 -2
- package/esm/types/savings/morphoVaults.js +7 -0
- package/package.json +1 -1
- package/src/constants/index.ts +5 -1
- package/src/helpers/morphoBlueHelpers/index.ts +6 -5
- package/src/savings/index.ts +5 -2
- package/src/savings/morphoVaults/index.ts +74 -23
- package/src/savings/morphoVaults/options.ts +55 -1
- package/src/types/savings/morphoVaults.ts +10 -1
|
@@ -11,43 +11,51 @@ import Dec from 'decimal.js';
|
|
|
11
11
|
import { request as graphqlRequest } from 'graphql-request';
|
|
12
12
|
import { assetAmountInEth } from '@defisaver/tokens';
|
|
13
13
|
import * as morphoVaultsOptions from './options';
|
|
14
|
+
import { MorphoVaultVersion, } from '../../types';
|
|
14
15
|
import { getViemProvider } from '../../services/viem';
|
|
15
|
-
import { getMorphoVaultContractViem } from '../../contracts';
|
|
16
|
+
import { getErc4626ContractViem, getMorphoVaultContractViem } from '../../contracts';
|
|
17
|
+
import { MORPHO_API_URL } from '../../constants';
|
|
16
18
|
export { morphoVaultsOptions, };
|
|
17
|
-
const MORPHO_BLUE_API = 'https://api.morpho.org/graphql';
|
|
18
19
|
// Morpho API caps list page size at 100 items
|
|
19
20
|
const MORPHO_API_PAGE_SIZE = 100;
|
|
20
|
-
const
|
|
21
|
+
const buildLiquidityQuery = (listField) => `
|
|
21
22
|
query VaultsLiquidity($addresses: [String!], $chainIds: [Int!]) {
|
|
22
|
-
|
|
23
|
+
${listField}(first: ${MORPHO_API_PAGE_SIZE}, where: { address_in: $addresses, chainId_in: $chainIds }) {
|
|
23
24
|
items {
|
|
24
25
|
address
|
|
25
|
-
liquidity {
|
|
26
|
-
underlying
|
|
27
|
-
}
|
|
26
|
+
liquidity${listField === 'vaults' ? ' {\n underlying\n }' : ''}
|
|
28
27
|
}
|
|
29
28
|
}
|
|
30
29
|
}`;
|
|
30
|
+
const liquidityQueries = {
|
|
31
|
+
[MorphoVaultVersion.V1]: { field: 'vaults', query: buildLiquidityQuery('vaults') },
|
|
32
|
+
[MorphoVaultVersion.V2]: { field: 'vaultV2s', query: buildLiquidityQuery('vaultV2s') },
|
|
33
|
+
};
|
|
31
34
|
/**
|
|
32
|
-
* Fetches liquidity for all given vaults
|
|
35
|
+
* Fetches liquidity for all given vaults, one API request per vault version (chunked if over the page cap),
|
|
33
36
|
* returned as a map keyed by lowercased vault address. A vault missing from the API response is
|
|
34
37
|
* absent from the map.
|
|
35
38
|
*/
|
|
36
39
|
export const fetchMorphoVaultsLiquidity = (network, vaults) => __awaiter(void 0, void 0, void 0, function* () {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
const requests = [];
|
|
41
|
+
[MorphoVaultVersion.V1, MorphoVaultVersion.V2].forEach((version) => {
|
|
42
|
+
const versionVaults = vaults.filter((vault) => vault.version === version);
|
|
43
|
+
for (let i = 0; i < versionVaults.length; i += MORPHO_API_PAGE_SIZE)
|
|
44
|
+
requests.push({ version, chunk: versionVaults.slice(i, i + MORPHO_API_PAGE_SIZE) });
|
|
45
|
+
});
|
|
40
46
|
const liquidityByAddress = {};
|
|
41
|
-
yield Promise.all(
|
|
42
|
-
const
|
|
47
|
+
yield Promise.all(requests.map((_a) => __awaiter(void 0, [_a], void 0, function* ({ version, chunk }) {
|
|
48
|
+
const { field, query } = liquidityQueries[version];
|
|
49
|
+
const data = yield graphqlRequest(MORPHO_API_URL, query, {
|
|
43
50
|
addresses: chunk.map((vault) => vault.address),
|
|
44
51
|
chainIds: [network],
|
|
45
52
|
// the BigInt scalar serializes as a JSON number when it fits in Number.MAX_SAFE_INTEGER, a string otherwise
|
|
46
53
|
});
|
|
47
|
-
(data.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
(data[field].items || []).forEach((item) => {
|
|
55
|
+
// V1 exposes liquidity as { underlying }, V2 as a plain BigInt scalar
|
|
56
|
+
const underlying = (item === null || item === void 0 ? void 0 : item.liquidity) !== null && typeof (item === null || item === void 0 ? void 0 : item.liquidity) === 'object' ? item.liquidity.underlying : item === null || item === void 0 ? void 0 : item.liquidity;
|
|
57
|
+
if (underlying !== undefined && underlying !== null) {
|
|
58
|
+
liquidityByAddress[item.address.toLowerCase()] = String(underlying);
|
|
51
59
|
}
|
|
52
60
|
});
|
|
53
61
|
})));
|
|
@@ -60,7 +68,19 @@ const getBatchedViemProvider = (provider, network) => getViemProvider(provider,
|
|
|
60
68
|
},
|
|
61
69
|
},
|
|
62
70
|
});
|
|
63
|
-
|
|
71
|
+
// Vault V2 is ERC-4626 but has no decimals offset, so shares are converted with convertToAssets
|
|
72
|
+
const getMorphoVaultV2ChainData = (provider, morphoVault, accounts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
73
|
+
const vaultContract = getErc4626ContractViem(provider, morphoVault.address);
|
|
74
|
+
const [totalAssets, suppliedAssets] = yield Promise.all([
|
|
75
|
+
vaultContract.read.totalAssets(),
|
|
76
|
+
Promise.all(accounts.map((account) => __awaiter(void 0, void 0, void 0, function* () {
|
|
77
|
+
const share = yield vaultContract.read.balanceOf([account]);
|
|
78
|
+
return share === BigInt(0) ? BigInt(0) : vaultContract.read.convertToAssets([share]);
|
|
79
|
+
}))),
|
|
80
|
+
]);
|
|
81
|
+
return { totalAssets, suppliedAssets };
|
|
82
|
+
});
|
|
83
|
+
const getMorphoVaultV1ChainData = (provider, morphoVault, accounts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
64
84
|
const morphoVaultContract = getMorphoVaultContractViem(provider, morphoVault.address);
|
|
65
85
|
const shares = {};
|
|
66
86
|
const [totalAssets, totalSupply, decimals, decimalsOffset] = yield Promise.all([
|
|
@@ -77,11 +97,29 @@ const getMorphoVaultChainData = (provider, morphoVault, accounts) => __awaiter(v
|
|
|
77
97
|
totalAssets, totalSupply, decimals, decimalsOffset, shares,
|
|
78
98
|
};
|
|
79
99
|
});
|
|
100
|
+
const getMorphoVaultChainData = (provider, morphoVault, accounts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
101
|
+
return (morphoVault.version === MorphoVaultVersion.V2
|
|
102
|
+
? { v2: yield getMorphoVaultV2ChainData(provider, morphoVault, accounts) }
|
|
103
|
+
: { v1: yield getMorphoVaultV1ChainData(provider, morphoVault, accounts) });
|
|
104
|
+
});
|
|
80
105
|
const formatMorphoVaultData = (morphoVault, chainData, liquidityUnderlying, accounts) => {
|
|
81
|
-
const { totalAssets, totalSupply, decimals, decimalsOffset, shares, } = chainData;
|
|
82
|
-
const poolSize = assetAmountInEth(totalAssets.toString(), morphoVault.asset);
|
|
83
106
|
const liquidity = assetAmountInEth(liquidityUnderlying, morphoVault.asset);
|
|
84
107
|
const supplied = {};
|
|
108
|
+
if (chainData.v2) {
|
|
109
|
+
const { totalAssets, suppliedAssets } = chainData.v2;
|
|
110
|
+
accounts.forEach((account, i) => {
|
|
111
|
+
supplied[account.toLowerCase()] = assetAmountInEth(suppliedAssets[i].toString(), morphoVault.asset);
|
|
112
|
+
});
|
|
113
|
+
return {
|
|
114
|
+
poolSize: assetAmountInEth(totalAssets.toString(), morphoVault.asset),
|
|
115
|
+
supplied,
|
|
116
|
+
liquidity,
|
|
117
|
+
asset: morphoVault.asset,
|
|
118
|
+
optionType: morphoVault.type,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const { totalAssets, totalSupply, decimals, decimalsOffset, shares, } = chainData.v1;
|
|
122
|
+
const poolSize = assetAmountInEth(totalAssets.toString(), morphoVault.asset);
|
|
85
123
|
accounts.forEach((account) => {
|
|
86
124
|
const share = shares[account] || BigInt(0);
|
|
87
125
|
supplied[account.toLowerCase()] = new Dec(new Dec(share.toString()).mul(new Dec(totalAssets.toString()).add(1)).div(new Dec(totalSupply.toString()).add(Math.pow(10, decimalsOffset))).div(Math.pow(10, 18))
|
|
@@ -17,5 +17,6 @@ export declare const MORPHO_VAULT_STEAKHOUSE_PYUSD: MorphoVault;
|
|
|
17
17
|
export declare const MORPHO_VAULT_SMOKEHOUSE_USDT: MorphoVault;
|
|
18
18
|
export declare const MORPHO_VAULT_SMOKEHOUSE_USDC: MorphoVault;
|
|
19
19
|
export declare const MORPHO_VAULT_SMOKEHOUSE_DAI: MorphoVault;
|
|
20
|
+
export declare const MORPHO_VAULT_GAUNTLET_USDG_PREMIUM: MorphoVault;
|
|
20
21
|
export declare const MORPHO_VAULTS: Record<MorphoVaultType, MorphoVault>;
|
|
21
22
|
export declare const getMorphoVault: (type: MorphoVaultType) => MorphoVault;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { MorphoVaultType } from '../../types';
|
|
1
|
+
import { MorphoVaultType, MorphoVaultVersion, NetworkNumber, } from '../../types';
|
|
2
2
|
export const MORPHO_VAULT_FLAGSHIP_ETH = {
|
|
3
3
|
type: MorphoVaultType.MorphoVaultFlagshipEth,
|
|
4
4
|
name: 'Flagship ETH',
|
|
5
5
|
address: '0x38989BBA00BDF8181F4082995b3DEAe96163aC5D',
|
|
6
6
|
asset: 'WETH',
|
|
7
|
+
network: NetworkNumber.Eth,
|
|
8
|
+
version: MorphoVaultVersion.V1,
|
|
7
9
|
deploymentBlock: 18927491,
|
|
8
10
|
isLegacy: true,
|
|
9
11
|
};
|
|
@@ -12,6 +14,8 @@ export const MORPHO_VAULT_GAUNTLET_USDC_CORE = {
|
|
|
12
14
|
name: 'Gauntlet USDC Core',
|
|
13
15
|
address: '0x8eB67A509616cd6A7c1B3c8C21D48FF57df3d458',
|
|
14
16
|
asset: 'USDC',
|
|
17
|
+
network: NetworkNumber.Eth,
|
|
18
|
+
version: MorphoVaultVersion.V1,
|
|
15
19
|
deploymentBlock: 19777569,
|
|
16
20
|
isLegacy: true,
|
|
17
21
|
};
|
|
@@ -20,6 +24,8 @@ export const MORPHO_VAULT_GAUNTLET_USDC_PRIME = {
|
|
|
20
24
|
name: 'Gauntlet USDC Prime',
|
|
21
25
|
address: '0xdd0f28e19C1780eb6396170735D45153D261490d',
|
|
22
26
|
asset: 'USDC',
|
|
27
|
+
network: NetworkNumber.Eth,
|
|
28
|
+
version: MorphoVaultVersion.V1,
|
|
23
29
|
deploymentBlock: 19777569,
|
|
24
30
|
isLegacy: true,
|
|
25
31
|
};
|
|
@@ -28,6 +34,8 @@ export const MORPHO_VAULT_RE7_WETH = {
|
|
|
28
34
|
name: 'RE7 WETH',
|
|
29
35
|
address: '0x78Fc2c2eD1A4cDb5402365934aE5648aDAd094d0',
|
|
30
36
|
asset: 'WETH',
|
|
37
|
+
network: NetworkNumber.Eth,
|
|
38
|
+
version: MorphoVaultVersion.V1,
|
|
31
39
|
deploymentBlock: 19121118,
|
|
32
40
|
isLegacy: true,
|
|
33
41
|
};
|
|
@@ -36,6 +44,8 @@ export const MORPHO_VAULT_GAUNTLET_WETH_CORE = {
|
|
|
36
44
|
name: 'Gauntlet WETH Core',
|
|
37
45
|
address: '0x4881Ef0BF6d2365D3dd6499ccd7532bcdBCE0658',
|
|
38
46
|
asset: 'WETH',
|
|
47
|
+
network: NetworkNumber.Eth,
|
|
48
|
+
version: MorphoVaultVersion.V1,
|
|
39
49
|
deploymentBlock: 19541126,
|
|
40
50
|
isLegacy: true,
|
|
41
51
|
};
|
|
@@ -44,6 +54,8 @@ export const MORPHO_VAULT_GAUNTLET_WETH_PRIME = {
|
|
|
44
54
|
name: 'Gauntlet WETH Prime',
|
|
45
55
|
address: '0x2371e134e3455e0593363cBF89d3b6cf53740618',
|
|
46
56
|
asset: 'WETH',
|
|
57
|
+
network: NetworkNumber.Eth,
|
|
58
|
+
version: MorphoVaultVersion.V1,
|
|
47
59
|
deploymentBlock: 19372149,
|
|
48
60
|
isLegacy: true,
|
|
49
61
|
};
|
|
@@ -52,6 +64,8 @@ export const MORPHO_VAULT_BOOSTED_USDC = {
|
|
|
52
64
|
name: 'Boosted USDC',
|
|
53
65
|
address: '0xd63070114470f685b75B74D60EEc7c1113d33a3D',
|
|
54
66
|
asset: 'USDC',
|
|
67
|
+
network: NetworkNumber.Eth,
|
|
68
|
+
version: MorphoVaultVersion.V1,
|
|
55
69
|
deploymentBlock: 20377233,
|
|
56
70
|
isLegacy: true,
|
|
57
71
|
};
|
|
@@ -60,6 +74,8 @@ export const MORPHO_VAULT_FLAGSHIP_USDT = {
|
|
|
60
74
|
name: 'Flagship USDT',
|
|
61
75
|
address: '0x2C25f6C25770fFEC5959D34B94Bf898865e5D6b1',
|
|
62
76
|
asset: 'USDT',
|
|
77
|
+
network: NetworkNumber.Eth,
|
|
78
|
+
version: MorphoVaultVersion.V1,
|
|
63
79
|
deploymentBlock: 19127039,
|
|
64
80
|
isLegacy: true,
|
|
65
81
|
};
|
|
@@ -68,6 +84,8 @@ export const MORPHO_VAULT_GAUNTLET_USDA_CORE = {
|
|
|
68
84
|
name: 'Gauntlet USDA Core',
|
|
69
85
|
address: '0x125D41A6e5dbf455cD9Df8F80BCC6fd172D52Cc6',
|
|
70
86
|
asset: 'USDA',
|
|
87
|
+
network: NetworkNumber.Eth,
|
|
88
|
+
version: MorphoVaultVersion.V1,
|
|
71
89
|
deploymentBlock: 19633608,
|
|
72
90
|
isLegacy: true,
|
|
73
91
|
};
|
|
@@ -76,6 +94,8 @@ export const MORPHO_VAULT_GAUNTLET_USDT_PRIME = {
|
|
|
76
94
|
name: 'Gauntlet USDT Prime',
|
|
77
95
|
address: '0x8CB3649114051cA5119141a34C200D65dc0Faa73',
|
|
78
96
|
asset: 'USDT',
|
|
97
|
+
network: NetworkNumber.Eth,
|
|
98
|
+
version: MorphoVaultVersion.V1,
|
|
79
99
|
deploymentBlock: 19486541,
|
|
80
100
|
isLegacy: true,
|
|
81
101
|
};
|
|
@@ -84,6 +104,8 @@ export const MORPHO_VAULT_GAUNTLET_RESOLV_USDC = {
|
|
|
84
104
|
name: 'Gauntlet Resolv USDC',
|
|
85
105
|
address: '0x132E6C9C33A62D7727cd359b1f51e5B566E485Eb',
|
|
86
106
|
asset: 'USDC',
|
|
107
|
+
network: NetworkNumber.Eth,
|
|
108
|
+
version: MorphoVaultVersion.V1,
|
|
87
109
|
deploymentBlock: 21487882,
|
|
88
110
|
isLegacy: true,
|
|
89
111
|
};
|
|
@@ -93,6 +115,8 @@ export const MORPHO_VAULT_STEAKHOUSE_USDT = {
|
|
|
93
115
|
name: 'Steakhouse USDT',
|
|
94
116
|
address: '0xbEef047a543E45807105E51A8BBEFCc5950fcfBa',
|
|
95
117
|
asset: 'USDT',
|
|
118
|
+
network: NetworkNumber.Eth,
|
|
119
|
+
version: MorphoVaultVersion.V1,
|
|
96
120
|
deploymentBlock: 19043398,
|
|
97
121
|
isLegacy: false,
|
|
98
122
|
};
|
|
@@ -101,6 +125,8 @@ export const MORPHO_VAULT_STEAKHOUSE_USDC = {
|
|
|
101
125
|
name: 'Steakhouse USDC',
|
|
102
126
|
address: '0xBEEF01735c132Ada46AA9aA4c54623cAA92A64CB',
|
|
103
127
|
asset: 'USDC',
|
|
128
|
+
network: NetworkNumber.Eth,
|
|
129
|
+
version: MorphoVaultVersion.V1,
|
|
104
130
|
deploymentBlock: 18928285,
|
|
105
131
|
isLegacy: false,
|
|
106
132
|
};
|
|
@@ -109,6 +135,8 @@ export const MORPHO_VAULT_STEAKHOUSE_ETH = {
|
|
|
109
135
|
name: 'Steakhouse WETH',
|
|
110
136
|
address: '0xBEEf050ecd6a16c4e7bfFbB52Ebba7846C4b8cD4',
|
|
111
137
|
asset: 'WETH',
|
|
138
|
+
network: NetworkNumber.Eth,
|
|
139
|
+
version: MorphoVaultVersion.V1,
|
|
112
140
|
deploymentBlock: 19380443,
|
|
113
141
|
isLegacy: false,
|
|
114
142
|
};
|
|
@@ -117,6 +145,8 @@ export const MORPHO_VAULT_STEAKHOUSE_PYUSD = {
|
|
|
117
145
|
name: 'Steakhouse PYUSD',
|
|
118
146
|
address: '0xbEEF02e5E13584ab96848af90261f0C8Ee04722a',
|
|
119
147
|
asset: 'PYUSD',
|
|
148
|
+
network: NetworkNumber.Eth,
|
|
149
|
+
version: MorphoVaultVersion.V1,
|
|
120
150
|
deploymentBlock: 19043398,
|
|
121
151
|
isLegacy: false,
|
|
122
152
|
};
|
|
@@ -126,6 +156,8 @@ export const MORPHO_VAULT_SMOKEHOUSE_USDT = {
|
|
|
126
156
|
name: 'Smokehouse USDT',
|
|
127
157
|
address: '0xA0804346780b4c2e3bE118ac957D1DB82F9d7484',
|
|
128
158
|
asset: 'USDT',
|
|
159
|
+
network: NetworkNumber.Eth,
|
|
160
|
+
version: MorphoVaultVersion.V1,
|
|
129
161
|
deploymentBlock: 21522238,
|
|
130
162
|
isLegacy: false,
|
|
131
163
|
};
|
|
@@ -134,6 +166,8 @@ export const MORPHO_VAULT_SMOKEHOUSE_USDC = {
|
|
|
134
166
|
name: 'Smokehouse USDC',
|
|
135
167
|
address: '0xBEeFFF209270748ddd194831b3fa287a5386f5bC',
|
|
136
168
|
asset: 'USDC',
|
|
169
|
+
network: NetworkNumber.Eth,
|
|
170
|
+
version: MorphoVaultVersion.V1,
|
|
137
171
|
deploymentBlock: 21337719,
|
|
138
172
|
isLegacy: false,
|
|
139
173
|
};
|
|
@@ -142,9 +176,22 @@ export const MORPHO_VAULT_SMOKEHOUSE_DAI = {
|
|
|
142
176
|
name: 'Smokehouse DAI',
|
|
143
177
|
address: '0xbeeFfF68CC520D68f82641EFF84330C631E2490E',
|
|
144
178
|
asset: 'DAI',
|
|
179
|
+
network: NetworkNumber.Eth,
|
|
180
|
+
version: MorphoVaultVersion.V1,
|
|
145
181
|
deploymentBlock: 21343808,
|
|
146
182
|
isLegacy: false,
|
|
147
183
|
};
|
|
184
|
+
// Vault V2
|
|
185
|
+
export const MORPHO_VAULT_GAUNTLET_USDG_PREMIUM = {
|
|
186
|
+
type: MorphoVaultType.MorphoVaultGauntletUSDGPremium,
|
|
187
|
+
name: 'Gauntlet USDG Premium',
|
|
188
|
+
address: '0x390c1bb01F3F627144a40617e287d4cE3D5aBCfa',
|
|
189
|
+
asset: 'USDG',
|
|
190
|
+
network: NetworkNumber.Arb,
|
|
191
|
+
version: MorphoVaultVersion.V2,
|
|
192
|
+
deploymentBlock: 510799903,
|
|
193
|
+
isLegacy: false,
|
|
194
|
+
};
|
|
148
195
|
export const MORPHO_VAULTS = {
|
|
149
196
|
[MorphoVaultType.MorphoVaultFlagshipEth]: MORPHO_VAULT_FLAGSHIP_ETH,
|
|
150
197
|
[MorphoVaultType.MorphoVaultGauntletUSDCCore]: MORPHO_VAULT_GAUNTLET_USDC_CORE,
|
|
@@ -166,5 +213,7 @@ export const MORPHO_VAULTS = {
|
|
|
166
213
|
[MorphoVaultType.MorphoVaultSmokehouseUSDT]: MORPHO_VAULT_SMOKEHOUSE_USDT,
|
|
167
214
|
[MorphoVaultType.MorphoVaultSmokehouseUSDC]: MORPHO_VAULT_SMOKEHOUSE_USDC,
|
|
168
215
|
[MorphoVaultType.MorphoVaultSmokehouseDAI]: MORPHO_VAULT_SMOKEHOUSE_DAI,
|
|
216
|
+
// Vault V2
|
|
217
|
+
[MorphoVaultType.MorphoVaultGauntletUSDGPremium]: MORPHO_VAULT_GAUNTLET_USDG_PREMIUM,
|
|
169
218
|
};
|
|
170
219
|
export const getMorphoVault = (type) => MORPHO_VAULTS[type];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EthAddress } from '../common';
|
|
1
|
+
import { EthAddress, NetworkNumber } from '../common';
|
|
2
2
|
export declare enum MorphoVaultType {
|
|
3
3
|
MorphoVaultFlagshipEth = "morpho_vault_flagship_eth",
|
|
4
4
|
MorphoVaultGauntletUSDCCore = "morpho_vault_gauntlet_usdc_core",
|
|
@@ -17,13 +17,20 @@ export declare enum MorphoVaultType {
|
|
|
17
17
|
MorphoVaultSteakhousePYUSD = "morpho_vault_steakhouse_pyusd",
|
|
18
18
|
MorphoVaultSmokehouseUSDT = "morpho_vault_smokehouse_usdt",
|
|
19
19
|
MorphoVaultSmokehouseUSDC = "morpho_vault_smokehouse_usdc",
|
|
20
|
-
MorphoVaultSmokehouseDAI = "morpho_vault_smokehouse_dai"
|
|
20
|
+
MorphoVaultSmokehouseDAI = "morpho_vault_smokehouse_dai",
|
|
21
|
+
MorphoVaultGauntletUSDGPremium = "morpho_vault_gauntlet_usdg_premium"
|
|
22
|
+
}
|
|
23
|
+
export declare enum MorphoVaultVersion {
|
|
24
|
+
V1 = 1,
|
|
25
|
+
V2 = 2
|
|
21
26
|
}
|
|
22
27
|
export interface MorphoVault {
|
|
23
28
|
type: MorphoVaultType;
|
|
24
29
|
name: string;
|
|
25
30
|
address: EthAddress;
|
|
26
31
|
asset: string;
|
|
32
|
+
network: NetworkNumber;
|
|
33
|
+
version: MorphoVaultVersion;
|
|
27
34
|
deploymentBlock: number;
|
|
28
35
|
isLegacy: boolean;
|
|
29
36
|
}
|
|
@@ -20,4 +20,11 @@ export var MorphoVaultType;
|
|
|
20
20
|
MorphoVaultType["MorphoVaultSmokehouseUSDT"] = "morpho_vault_smokehouse_usdt";
|
|
21
21
|
MorphoVaultType["MorphoVaultSmokehouseUSDC"] = "morpho_vault_smokehouse_usdc";
|
|
22
22
|
MorphoVaultType["MorphoVaultSmokehouseDAI"] = "morpho_vault_smokehouse_dai";
|
|
23
|
+
// Vault V2
|
|
24
|
+
MorphoVaultType["MorphoVaultGauntletUSDGPremium"] = "morpho_vault_gauntlet_usdg_premium";
|
|
23
25
|
})(MorphoVaultType || (MorphoVaultType = {}));
|
|
26
|
+
export var MorphoVaultVersion;
|
|
27
|
+
(function (MorphoVaultVersion) {
|
|
28
|
+
MorphoVaultVersion[MorphoVaultVersion["V1"] = 1] = "V1";
|
|
29
|
+
MorphoVaultVersion[MorphoVaultVersion["V2"] = 2] = "V2";
|
|
30
|
+
})(MorphoVaultVersion || (MorphoVaultVersion = {}));
|
package/package.json
CHANGED
package/src/constants/index.ts
CHANGED
|
@@ -9,4 +9,8 @@ export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
|
9
9
|
export const ZERO_BYTES32 = '0x0000000000000000000000000000000000000000000000000000000000000000';
|
|
10
10
|
export const WAD = 1e18;
|
|
11
11
|
export const USD_QUOTE = '0x0000000000000000000000000000000000000348';
|
|
12
|
-
export const borrowOperations = ['borrow', 'payback'];
|
|
12
|
+
export const borrowOperations = ['borrow', 'payback'];
|
|
13
|
+
|
|
14
|
+
// Proxies Morpho's GraphQL API through the DFS backend, which attaches the private API key
|
|
15
|
+
// for better rate limits (DEV-13673)
|
|
16
|
+
export const MORPHO_API_URL = 'https://fe.defisaver.com/api/morpho/graphql';
|
|
@@ -12,7 +12,9 @@ import {
|
|
|
12
12
|
MorphoBluePublicAllocatorItem,
|
|
13
13
|
MorphoBlueRealloactionMarketData,
|
|
14
14
|
} from '../../types';
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
borrowOperations, MORPHO_API_URL, SECONDS_PER_YEAR, WAD,
|
|
17
|
+
} from '../../constants';
|
|
16
18
|
import { MorphoBlueViewContractViem } from '../../contracts';
|
|
17
19
|
import { compareAddresses, LONGER_TIMEOUT, wethToEth } from '../../services/utils';
|
|
18
20
|
import { getViemProvider } from '../../services/viem';
|
|
@@ -147,7 +149,6 @@ export const getApyAfterValuesEstimation = async (selectedMarket: MorphoBlueMark
|
|
|
147
149
|
return { borrowRate, supplyRate };
|
|
148
150
|
};
|
|
149
151
|
|
|
150
|
-
const API_URL = 'https://api.morpho.org/graphql';
|
|
151
152
|
// Morpho Blue ACRM (Adaptive Curve IRM) always targets 90% utilization — protocol constant
|
|
152
153
|
const ACRM_TARGET_UTILIZATION = '900000000000000000';
|
|
153
154
|
|
|
@@ -226,7 +227,7 @@ const REWARDS_QUERY = `
|
|
|
226
227
|
*/
|
|
227
228
|
export const getReallocatableLiquidity = async (marketId: string, network: NetworkNumber = NetworkNumber.Eth): Promise<{ reallocatableLiquidity: string, targetBorrowUtilization: string }> => {
|
|
228
229
|
try {
|
|
229
|
-
const response = await fetch(
|
|
230
|
+
const response = await fetch(MORPHO_API_URL, {
|
|
230
231
|
method: 'POST',
|
|
231
232
|
headers: { 'Content-Type': 'application/json' },
|
|
232
233
|
body: JSON.stringify({
|
|
@@ -291,7 +292,7 @@ export const getLiquidityToAllocate = (amountToBorrow: string, totalBorrow: stri
|
|
|
291
292
|
export const getReallocation = async (market: MorphoBlueMarketData, assetsData: MorphoBlueAssetsData, amountToBorrow: string, network: NetworkNumber = NetworkNumber.Eth): Promise<{ vaults: string[], withdrawals: (string | string[])[][][] }> => {
|
|
292
293
|
try {
|
|
293
294
|
const { marketId, loanToken } = market;
|
|
294
|
-
const response = await fetch(
|
|
295
|
+
const response = await fetch(MORPHO_API_URL, {
|
|
295
296
|
method: 'POST',
|
|
296
297
|
headers: { 'Content-Type': 'application/json' },
|
|
297
298
|
body: JSON.stringify({
|
|
@@ -382,7 +383,7 @@ export const getReallocation = async (market: MorphoBlueMarketData, assetsData:
|
|
|
382
383
|
};
|
|
383
384
|
|
|
384
385
|
export const getRewardsForMarket = async (marketId: string, network: NetworkNumber = NetworkNumber.Eth) => {
|
|
385
|
-
const response = await fetch(
|
|
386
|
+
const response = await fetch(MORPHO_API_URL, {
|
|
386
387
|
method: 'POST',
|
|
387
388
|
headers: { 'Content-Type': 'application/json' },
|
|
388
389
|
body: JSON.stringify({
|
package/src/savings/index.ts
CHANGED
|
@@ -32,7 +32,8 @@ export const getSavingsData = async (
|
|
|
32
32
|
network: NetworkNumber,
|
|
33
33
|
accounts: EthAddress[],
|
|
34
34
|
) => {
|
|
35
|
-
const morphoVaultsList = Object.keys(morphoVaults.morphoVaultsOptions.MORPHO_VAULTS) as MorphoVaultType[]
|
|
35
|
+
const morphoVaultsList = (Object.keys(morphoVaults.morphoVaultsOptions.MORPHO_VAULTS) as MorphoVaultType[])
|
|
36
|
+
.filter((key) => morphoVaults.morphoVaultsOptions.getMorphoVault(key).network === network);
|
|
36
37
|
const yearnVaultsList = Object.keys(yearnVaults.yearnVaultsOptions.YEARN_VAULTS) as YearnVaultType[];
|
|
37
38
|
const sparkSavingsVaultsList = Object.keys(sparkSavingsVaults.sparkSavingsVaultsOptions.SPARK_SAVINGS_VAULTS) as SparkSavingsVaultType[];
|
|
38
39
|
const yearnV3VaultsList = Object.keys(yearnV3Vaults.yearnV3VaultsOptions.YEARN_V3_VAULTS) as YearnV3VaultType[];
|
|
@@ -42,7 +43,7 @@ export const getSavingsData = async (
|
|
|
42
43
|
const savingsData: SavingsData = {};
|
|
43
44
|
|
|
44
45
|
await Promise.all([
|
|
45
|
-
...(
|
|
46
|
+
...(morphoVaultsList.length ? [
|
|
46
47
|
(async () => {
|
|
47
48
|
try {
|
|
48
49
|
const vaults = morphoVaultsList.map((vaultKey) => morphoVaults.morphoVaultsOptions.getMorphoVault(vaultKey));
|
|
@@ -52,6 +53,8 @@ export const getSavingsData = async (
|
|
|
52
53
|
console.error('[getSavingsData] Error fetching morpho vaults:', err);
|
|
53
54
|
}
|
|
54
55
|
})(),
|
|
56
|
+
] : []),
|
|
57
|
+
...(network === NetworkNumber.Eth ? [
|
|
55
58
|
...yearnVaultsList.map(async (vaultKey) => {
|
|
56
59
|
try {
|
|
57
60
|
const vault = yearnVaults.yearnVaultsOptions.getYearnVault(vaultKey);
|
|
@@ -4,52 +4,62 @@ import { request as graphqlRequest } from 'graphql-request';
|
|
|
4
4
|
import { assetAmountInEth } from '@defisaver/tokens';
|
|
5
5
|
import * as morphoVaultsOptions from './options';
|
|
6
6
|
import {
|
|
7
|
-
EthAddress, EthereumProvider, NetworkNumber, MorphoVault, MorphoVaultType, SavingsVaultData,
|
|
7
|
+
EthAddress, EthereumProvider, NetworkNumber, MorphoVault, MorphoVaultType, MorphoVaultVersion, SavingsVaultData,
|
|
8
8
|
} from '../../types';
|
|
9
9
|
import { getViemProvider } from '../../services/viem';
|
|
10
|
-
import { getMorphoVaultContractViem } from '../../contracts';
|
|
10
|
+
import { getErc4626ContractViem, getMorphoVaultContractViem } from '../../contracts';
|
|
11
|
+
import { MORPHO_API_URL } from '../../constants';
|
|
11
12
|
|
|
12
13
|
export {
|
|
13
14
|
morphoVaultsOptions,
|
|
14
15
|
};
|
|
15
16
|
|
|
16
|
-
const MORPHO_BLUE_API = 'https://api.morpho.org/graphql';
|
|
17
|
-
|
|
18
17
|
// Morpho API caps list page size at 100 items
|
|
19
18
|
const MORPHO_API_PAGE_SIZE = 100;
|
|
20
19
|
|
|
21
|
-
const
|
|
20
|
+
const buildLiquidityQuery = (listField: 'vaults' | 'vaultV2s') => `
|
|
22
21
|
query VaultsLiquidity($addresses: [String!], $chainIds: [Int!]) {
|
|
23
|
-
|
|
22
|
+
${listField}(first: ${MORPHO_API_PAGE_SIZE}, where: { address_in: $addresses, chainId_in: $chainIds }) {
|
|
24
23
|
items {
|
|
25
24
|
address
|
|
26
|
-
liquidity {
|
|
27
|
-
underlying
|
|
28
|
-
}
|
|
25
|
+
liquidity${listField === 'vaults' ? ' {\n underlying\n }' : ''}
|
|
29
26
|
}
|
|
30
27
|
}
|
|
31
28
|
}`;
|
|
32
29
|
|
|
30
|
+
const liquidityQueries = {
|
|
31
|
+
[MorphoVaultVersion.V1]: { field: 'vaults', query: buildLiquidityQuery('vaults') },
|
|
32
|
+
[MorphoVaultVersion.V2]: { field: 'vaultV2s', query: buildLiquidityQuery('vaultV2s') },
|
|
33
|
+
} as const;
|
|
34
|
+
|
|
35
|
+
type LiquidityItem = { address: string, liquidity: { underlying: string | number | null } | string | number | null };
|
|
36
|
+
|
|
33
37
|
/**
|
|
34
|
-
* Fetches liquidity for all given vaults
|
|
38
|
+
* Fetches liquidity for all given vaults, one API request per vault version (chunked if over the page cap),
|
|
35
39
|
* returned as a map keyed by lowercased vault address. A vault missing from the API response is
|
|
36
40
|
* absent from the map.
|
|
37
41
|
*/
|
|
38
42
|
export const fetchMorphoVaultsLiquidity = async (network: NetworkNumber, vaults: MorphoVault[]): Promise<Record<string, string>> => {
|
|
39
|
-
const
|
|
40
|
-
|
|
43
|
+
const requests: { version: MorphoVaultVersion, chunk: MorphoVault[] }[] = [];
|
|
44
|
+
[MorphoVaultVersion.V1, MorphoVaultVersion.V2].forEach((version) => {
|
|
45
|
+
const versionVaults = vaults.filter((vault) => vault.version === version);
|
|
46
|
+
for (let i = 0; i < versionVaults.length; i += MORPHO_API_PAGE_SIZE) requests.push({ version, chunk: versionVaults.slice(i, i + MORPHO_API_PAGE_SIZE) });
|
|
47
|
+
});
|
|
41
48
|
|
|
42
49
|
const liquidityByAddress: Record<string, string> = {};
|
|
43
|
-
await Promise.all(
|
|
44
|
-
const
|
|
50
|
+
await Promise.all(requests.map(async ({ version, chunk }) => {
|
|
51
|
+
const { field, query } = liquidityQueries[version];
|
|
52
|
+
const data = await graphqlRequest(MORPHO_API_URL, query, {
|
|
45
53
|
addresses: chunk.map((vault) => vault.address),
|
|
46
54
|
chainIds: [network],
|
|
47
55
|
// the BigInt scalar serializes as a JSON number when it fits in Number.MAX_SAFE_INTEGER, a string otherwise
|
|
48
|
-
}) as
|
|
56
|
+
}) as Record<string, { items: LiquidityItem[] | null }>;
|
|
49
57
|
|
|
50
|
-
(data.
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
(data[field].items || []).forEach((item) => {
|
|
59
|
+
// V1 exposes liquidity as { underlying }, V2 as a plain BigInt scalar
|
|
60
|
+
const underlying = item?.liquidity !== null && typeof item?.liquidity === 'object' ? item.liquidity.underlying : item?.liquidity;
|
|
61
|
+
if (underlying !== undefined && underlying !== null) {
|
|
62
|
+
liquidityByAddress[item.address.toLowerCase()] = String(underlying);
|
|
53
63
|
}
|
|
54
64
|
});
|
|
55
65
|
}));
|
|
@@ -64,7 +74,22 @@ const getBatchedViemProvider = (provider: EthereumProvider, network: NetworkNumb
|
|
|
64
74
|
},
|
|
65
75
|
});
|
|
66
76
|
|
|
67
|
-
|
|
77
|
+
// Vault V2 is ERC-4626 but has no decimals offset, so shares are converted with convertToAssets
|
|
78
|
+
const getMorphoVaultV2ChainData = async (provider: Client, morphoVault: MorphoVault, accounts: EthAddress[]) => {
|
|
79
|
+
const vaultContract = getErc4626ContractViem(provider, morphoVault.address);
|
|
80
|
+
|
|
81
|
+
const [totalAssets, suppliedAssets] = await Promise.all([
|
|
82
|
+
vaultContract.read.totalAssets(),
|
|
83
|
+
Promise.all(accounts.map(async (account) => {
|
|
84
|
+
const share = await vaultContract.read.balanceOf([account]);
|
|
85
|
+
return share === BigInt(0) ? BigInt(0) : vaultContract.read.convertToAssets([share]);
|
|
86
|
+
})),
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
return { totalAssets, suppliedAssets };
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const getMorphoVaultV1ChainData = async (provider: Client, morphoVault: MorphoVault, accounts: EthAddress[]) => {
|
|
68
93
|
const morphoVaultContract = getMorphoVaultContractViem(provider, morphoVault.address);
|
|
69
94
|
|
|
70
95
|
const shares: Record<EthAddress, bigint> = {};
|
|
@@ -85,20 +110,46 @@ const getMorphoVaultChainData = async (provider: Client, morphoVault: MorphoVaul
|
|
|
85
110
|
};
|
|
86
111
|
};
|
|
87
112
|
|
|
113
|
+
type MorphoVaultChainData = {
|
|
114
|
+
v1?: Awaited<ReturnType<typeof getMorphoVaultV1ChainData>>,
|
|
115
|
+
v2?: Awaited<ReturnType<typeof getMorphoVaultV2ChainData>>,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const getMorphoVaultChainData = async (provider: Client, morphoVault: MorphoVault, accounts: EthAddress[]): Promise<MorphoVaultChainData> => (
|
|
119
|
+
morphoVault.version === MorphoVaultVersion.V2
|
|
120
|
+
? { v2: await getMorphoVaultV2ChainData(provider, morphoVault, accounts) }
|
|
121
|
+
: { v1: await getMorphoVaultV1ChainData(provider, morphoVault, accounts) }
|
|
122
|
+
);
|
|
123
|
+
|
|
88
124
|
const formatMorphoVaultData = (
|
|
89
125
|
morphoVault: MorphoVault,
|
|
90
|
-
chainData:
|
|
126
|
+
chainData: MorphoVaultChainData,
|
|
91
127
|
liquidityUnderlying: string,
|
|
92
128
|
accounts: EthAddress[],
|
|
93
129
|
): SavingsVaultData => {
|
|
130
|
+
const liquidity = assetAmountInEth(liquidityUnderlying, morphoVault.asset);
|
|
131
|
+
const supplied: Record<EthAddress, string> = {};
|
|
132
|
+
|
|
133
|
+
if (chainData.v2) {
|
|
134
|
+
const { totalAssets, suppliedAssets } = chainData.v2;
|
|
135
|
+
accounts.forEach((account, i) => {
|
|
136
|
+
supplied[account.toLowerCase() as EthAddress] = assetAmountInEth(suppliedAssets[i].toString(), morphoVault.asset);
|
|
137
|
+
});
|
|
138
|
+
return {
|
|
139
|
+
poolSize: assetAmountInEth(totalAssets.toString(), morphoVault.asset),
|
|
140
|
+
supplied,
|
|
141
|
+
liquidity,
|
|
142
|
+
asset: morphoVault.asset,
|
|
143
|
+
optionType: morphoVault.type,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
94
147
|
const {
|
|
95
148
|
totalAssets, totalSupply, decimals, decimalsOffset, shares,
|
|
96
|
-
} = chainData
|
|
149
|
+
} = chainData.v1!;
|
|
97
150
|
|
|
98
151
|
const poolSize = assetAmountInEth(totalAssets.toString(), morphoVault.asset);
|
|
99
|
-
const liquidity = assetAmountInEth(liquidityUnderlying, morphoVault.asset);
|
|
100
152
|
|
|
101
|
-
const supplied: Record<EthAddress, string> = {};
|
|
102
153
|
accounts.forEach((account) => {
|
|
103
154
|
const share = shares[account] || BigInt(0);
|
|
104
155
|
supplied[account.toLowerCase() as EthAddress] = new Dec(new Dec(share.toString()).mul(new Dec(totalAssets.toString()).add(1)).div(new Dec(totalSupply.toString()).add(10 ** decimalsOffset)).div(10 ** 18)
|