@defisaver/positions-sdk 2.1.137-audit-dev → 2.1.138
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/aaveV4/index.js +26 -10
- package/cjs/contracts.d.ts +2129 -1886
- package/cjs/savings/morphoVaults/index.js +2 -19
- package/cjs/services/utils.d.ts +1 -0
- package/cjs/services/utils.js +3 -1
- package/cjs/services/viem.d.ts +2679 -918
- package/cjs/types/aaveV4.d.ts +5 -0
- package/esm/aaveV4/index.js +27 -11
- package/esm/contracts.d.ts +2129 -1886
- package/esm/savings/morphoVaults/index.js +2 -19
- package/esm/services/utils.d.ts +1 -0
- package/esm/services/utils.js +1 -0
- package/esm/services/viem.d.ts +2667 -916
- package/esm/types/aaveV4.d.ts +5 -0
- package/package.json +2 -9
- package/src/aaveV4/index.ts +29 -11
- package/src/contracts.ts +2 -2
- package/src/savings/morphoVaults/index.ts +2 -18
- package/src/services/utils.ts +2 -0
- package/src/types/aaveV4.ts +5 -0
package/esm/types/aaveV4.d.ts
CHANGED
|
@@ -44,6 +44,11 @@ export interface AaveV4SpokeInfo {
|
|
|
44
44
|
value: AaveV4SpokesType;
|
|
45
45
|
url: string;
|
|
46
46
|
address: EthAddress;
|
|
47
|
+
/**
|
|
48
|
+
* Known hub addresses, used only to prefetch hub data in parallel with the spoke data.
|
|
49
|
+
* The on-chain reserves are the source of truth — hubs found there but missing here are
|
|
50
|
+
* fetched dynamically, so this list going stale can't break spoke loading.
|
|
51
|
+
*/
|
|
47
52
|
hubs: EthAddress[];
|
|
48
53
|
}
|
|
49
54
|
export interface AaveV4SpokeData {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defisaver/positions-sdk",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.138",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./cjs/index.js",
|
|
6
6
|
"module": "./esm/index.js",
|
|
@@ -25,17 +25,11 @@
|
|
|
25
25
|
"@types/lodash": "^4.17.15",
|
|
26
26
|
"@types/memoizee": "^0.4.12",
|
|
27
27
|
"decimal.js": "^10.6.0",
|
|
28
|
+
"graphql-request": "^1.8.2",
|
|
28
29
|
"lodash": "^4.17.21",
|
|
29
30
|
"memoizee": "^0.4.17",
|
|
30
31
|
"viem": "^2.37.9"
|
|
31
32
|
},
|
|
32
|
-
"overrides": {
|
|
33
|
-
"serialize-javascript": "^7.1.0",
|
|
34
|
-
"uuid": "^11.1.1",
|
|
35
|
-
"@typescript-eslint/typescript-estree": {
|
|
36
|
-
"minimatch": "^9.0.9"
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
33
|
"devDependencies": {
|
|
40
34
|
"@defisaver/eslint-config": "^1.0.1",
|
|
41
35
|
"@metamask/eth-json-rpc-middleware": "^15.0.1",
|
|
@@ -46,7 +40,6 @@
|
|
|
46
40
|
"dotenv": "^16.3.1",
|
|
47
41
|
"eslint": "^8.49.0",
|
|
48
42
|
"eslint-plugin-import": "^2.31.0",
|
|
49
|
-
"eslint-webpack-plugin": "^4.0.1",
|
|
50
43
|
"mocha": "^10.2.0",
|
|
51
44
|
"nock": "^14.0.0",
|
|
52
45
|
"ts-node": "^10.9.2",
|
package/src/aaveV4/index.ts
CHANGED
|
@@ -21,7 +21,9 @@ import {
|
|
|
21
21
|
} from '../types';
|
|
22
22
|
import { AaveV4ViewContractViem } from '../contracts';
|
|
23
23
|
import { getStakingApy, STAKING_ASSETS } from '../staking';
|
|
24
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
isMaxUint, shortenAddress, wethToEth, wethToEthByAddress,
|
|
26
|
+
} from '../services/utils';
|
|
25
27
|
import { aaveV4GetAggregatedPositionData, calcUserRiskPremiumBps } from '../helpers/aaveV4Helpers';
|
|
26
28
|
import { findAaveV4SpokeByAddress, getAaveV4HubByAddress } from '../markets/aaveV4';
|
|
27
29
|
import { aprToApy } from '../moneymarket';
|
|
@@ -56,10 +58,10 @@ const formatReserveAsset = async (reserveAsset: AaveV4ReserveAssetOnChain, hubAs
|
|
|
56
58
|
// tokens package. Flag it so consumers can render it read-only instead of feeding NaN into amounts.
|
|
57
59
|
const isUnsupported = assetInfo.symbol === '?';
|
|
58
60
|
const symbol = wethToEth(assetInfo.symbol);
|
|
61
|
+
// The hub registry only provides display metadata — a hub missing from it (newly deployed by
|
|
62
|
+
// Aave, SDK not yet updated) must not prevent the reserve from loading, so fall back to a
|
|
63
|
+
// generated label instead of failing.
|
|
59
64
|
const hubInfo = getAaveV4HubByAddress(network, reserveAsset.hub);
|
|
60
|
-
if (!hubInfo) {
|
|
61
|
-
throw new Error(`Hub not found with address: ${reserveAsset.hub}`);
|
|
62
|
-
}
|
|
63
65
|
|
|
64
66
|
const isStakingAsset = STAKING_ASSETS.includes(symbol);
|
|
65
67
|
const supplyIncentives: IncentiveData[] = [];
|
|
@@ -124,8 +126,8 @@ const formatReserveAsset = async (reserveAsset: AaveV4ReserveAssetOnChain, hubAs
|
|
|
124
126
|
decimals: reserveAsset.decimals,
|
|
125
127
|
isUnsupported,
|
|
126
128
|
underlying: reserveAsset.underlying,
|
|
127
|
-
hub: hubInfo.
|
|
128
|
-
hubName: hubInfo?.label
|
|
129
|
+
hub: hubInfo?.address ?? reserveAsset.hub,
|
|
130
|
+
hubName: hubInfo?.label ?? `Hub ${shortenAddress(reserveAsset.hub)}`,
|
|
129
131
|
assetId: reserveAsset.assetId,
|
|
130
132
|
reserveId,
|
|
131
133
|
paused: reserveAsset.paused,
|
|
@@ -163,16 +165,32 @@ const formatReserveAsset = async (reserveAsset: AaveV4ReserveAssetOnChain, hubAs
|
|
|
163
165
|
export async function _getAaveV4SpokeData(provider: Client, network: NetworkNumber, market: AaveV4SpokeInfo, blockNumber: 'latest' | number = 'latest'): Promise<AaveV4SpokeData> {
|
|
164
166
|
const viewContract = AaveV4ViewContractViem(provider, network, blockNumber);
|
|
165
167
|
|
|
166
|
-
const hubsData: Record<
|
|
168
|
+
const hubsData: Record<string, AaveV4HubOnChainData> = {};
|
|
169
|
+
const loadHubData = async (hubAddress: EthAddress) => {
|
|
170
|
+
hubsData[hubAddress.toLowerCase()] = await fetchHubData(viewContract, hubAddress);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// market.hubs is only a prefetch hint (lets known hubs load in parallel with the spoke data), so
|
|
174
|
+
// failures are tolerated here — any hub the reserves actually reference is (re)fetched below.
|
|
167
175
|
const [spokeData, merklCampaigns] = await Promise.all([
|
|
168
176
|
viewContract.read.getSpokeDataFull([market.address]),
|
|
169
177
|
getAaveV4MerkleCampaigns(network),
|
|
170
|
-
...market.hubs.map(
|
|
171
|
-
hubsData[hubAddress] = await fetchHubData(viewContract, hubAddress);
|
|
172
|
-
}),
|
|
178
|
+
...market.hubs.map((hubAddress) => loadHubData(hubAddress).catch(() => {})),
|
|
173
179
|
]);
|
|
174
180
|
|
|
175
|
-
|
|
181
|
+
// The on-chain reserves are the source of truth for which hubs the spoke uses — fetch any hub
|
|
182
|
+
// the prefetch didn't cover, so an asset listed from a hub unknown to the SDK can't break the spoke.
|
|
183
|
+
const missingHubs = [...new Set(spokeData[1].map((reserveAsset: AaveV4ReserveAssetOnChain) => reserveAsset.hub.toLowerCase() as EthAddress))]
|
|
184
|
+
.filter((hubAddress) => !hubsData[hubAddress]);
|
|
185
|
+
await Promise.all(missingHubs.map(loadHubData));
|
|
186
|
+
|
|
187
|
+
const reserveAssetsArray = (await Promise.all(spokeData[1].map(async (reserveAssetOnChain: AaveV4ReserveAssetOnChain, index: number) => {
|
|
188
|
+
const hubAsset = hubsData[reserveAssetOnChain.hub.toLowerCase()]?.assets[reserveAssetOnChain.assetId];
|
|
189
|
+
// A reserve whose hub-side asset data can't be resolved is skipped instead of failing the
|
|
190
|
+
// whole spoke (position math degrades for that one asset only).
|
|
191
|
+
if (!hubAsset) return null;
|
|
192
|
+
return formatReserveAsset(reserveAssetOnChain, hubAsset, index, +spokeData[0].oracleDecimals.toString(), network);
|
|
193
|
+
}))).filter((asset): asset is AaveV4ReserveAssetData => asset !== null);
|
|
176
194
|
|
|
177
195
|
const enrichedAssets = reserveAssetsArray.map((asset) => attachAaveV4MerklIncentives(asset, market.address, merklCampaigns));
|
|
178
196
|
|
package/src/contracts.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getContract, Client
|
|
1
|
+
import { getContract, Client } from 'viem';
|
|
2
2
|
import * as configRaw from './config/contracts';
|
|
3
3
|
import {
|
|
4
4
|
Blockish, EthAddress, HexString, NetworkNumber,
|
|
@@ -57,7 +57,7 @@ export const getConfigContractAbi = <TKey extends ConfigKey>(name: TKey, network
|
|
|
57
57
|
return latestAbi as unknown as typeof configRaw[TKey]['abi'];
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
-
export const createViemContractFromConfigFunc = <TKey extends ConfigKey>(name: TKey, _address?: HexString) => (client: Client, network: NetworkNumber, block?: Blockish)
|
|
60
|
+
export const createViemContractFromConfigFunc = <TKey extends ConfigKey>(name: TKey, _address?: HexString) => (client: Client, network: NetworkNumber, block?: Blockish) => {
|
|
61
61
|
const address = (_address || getConfigContractAddress(name, network, block));
|
|
62
62
|
const abi = getConfigContractAbi(name, network, block) as typeof configRaw[TKey]['abi'];
|
|
63
63
|
return getContract({
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Client } from 'viem';
|
|
2
2
|
import Dec from 'decimal.js';
|
|
3
|
+
import { request as graphqlRequest } from 'graphql-request';
|
|
3
4
|
import { assetAmountInEth } from '@defisaver/tokens';
|
|
4
5
|
import * as morphoVaultsOptions from './options';
|
|
5
6
|
import { EthAddress, EthereumProvider, NetworkNumber } from '../../types/common';
|
|
6
|
-
import { LONGER_TIMEOUT } from '../../services/utils';
|
|
7
7
|
import { getViemProvider } from '../../services/viem';
|
|
8
8
|
import { getMorphoVaultContractViem } from '../../contracts';
|
|
9
9
|
import { MorphoVault, SavingsVaultData } from '../../types';
|
|
@@ -30,22 +30,6 @@ const vaultDataQuery = `
|
|
|
30
30
|
|
|
31
31
|
const MORPHO_BLUE_API = 'https://api.morpho.org/graphql';
|
|
32
32
|
|
|
33
|
-
const fetchVaultData = async (address: EthAddress, chainId: NetworkNumber) => {
|
|
34
|
-
const res = await fetch(MORPHO_BLUE_API, {
|
|
35
|
-
method: 'POST',
|
|
36
|
-
headers: { 'Content-Type': 'application/json' },
|
|
37
|
-
body: JSON.stringify({
|
|
38
|
-
query: vaultDataQuery,
|
|
39
|
-
variables: { address, chainId },
|
|
40
|
-
}),
|
|
41
|
-
signal: AbortSignal.timeout(LONGER_TIMEOUT),
|
|
42
|
-
});
|
|
43
|
-
if (!res.ok) throw new Error(`Morpho vault request failed: ${res.status}`);
|
|
44
|
-
const body = await res.json();
|
|
45
|
-
if (!body?.data) throw new Error('Morpho vault response missing data');
|
|
46
|
-
return body.data;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
33
|
export const _getMorphoVaultData = async (provider: Client, network: NetworkNumber, morphoVault: MorphoVault, accounts: EthAddress[]): Promise<SavingsVaultData> => {
|
|
50
34
|
const morphoVaultContract = getMorphoVaultContractViem(provider, morphoVault.address);
|
|
51
35
|
|
|
@@ -56,7 +40,7 @@ export const _getMorphoVaultData = async (provider: Client, network: NetworkNumb
|
|
|
56
40
|
morphoVaultContract.read.totalSupply(),
|
|
57
41
|
morphoVaultContract.read.decimals(),
|
|
58
42
|
morphoVaultContract.read.DECIMALS_OFFSET(),
|
|
59
|
-
|
|
43
|
+
graphqlRequest(MORPHO_BLUE_API, vaultDataQuery, { address: morphoVault.address, chainId: network }),
|
|
60
44
|
...accounts.map(async (account) => {
|
|
61
45
|
const share = await morphoVaultContract.read.balanceOf([account]);
|
|
62
46
|
shares[account] = share;
|
package/src/services/utils.ts
CHANGED
|
@@ -20,6 +20,8 @@ export const isAddress = (address: string) => typeof address === 'string' && (ne
|
|
|
20
20
|
|
|
21
21
|
export const compareAddresses = (addr1 = '', addr2 = '') => addr1.toLowerCase() === addr2.toLowerCase();
|
|
22
22
|
|
|
23
|
+
export const shortenAddress = (address = '', start = 8, end = 4) => `${address.substring(0, start)}...${address.slice(-end)}`;
|
|
24
|
+
|
|
23
25
|
export const getWeiAmountForDecimals = (amount: string | number, decimals: number) => new Dec(amount).mul(10 ** decimals).floor().toString();
|
|
24
26
|
|
|
25
27
|
export const getEthAmountForDecimals = (amount: string | number, decimals: string | number) => new Dec(amount).div(10 ** +decimals).toString();
|
package/src/types/aaveV4.ts
CHANGED
|
@@ -52,6 +52,11 @@ export interface AaveV4SpokeInfo {
|
|
|
52
52
|
value: AaveV4SpokesType,
|
|
53
53
|
url: string,
|
|
54
54
|
address: EthAddress,
|
|
55
|
+
/**
|
|
56
|
+
* Known hub addresses, used only to prefetch hub data in parallel with the spoke data.
|
|
57
|
+
* The on-chain reserves are the source of truth — hubs found there but missing here are
|
|
58
|
+
* fetched dynamically, so this list going stale can't break spoke loading.
|
|
59
|
+
*/
|
|
55
60
|
hubs: EthAddress[],
|
|
56
61
|
}
|
|
57
62
|
|