@defisaver/positions-sdk 2.1.127-midnight-1-dev → 2.1.127-midnight-2-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/aaveV3/index.js +7 -1
- package/cjs/aaveV4/lend.js +3 -3
- package/cjs/claiming/index.d.ts +2 -1
- package/cjs/claiming/index.js +3 -1
- package/cjs/claiming/uniswap.d.ts +5 -0
- package/cjs/claiming/uniswap.js +75 -0
- package/cjs/config/contracts.d.ts +22 -0
- package/cjs/config/contracts.js +9 -1
- package/cjs/contracts.d.ts +35 -0
- package/cjs/contracts.js +2 -1
- package/cjs/curveUsd/index.js +1 -1
- package/cjs/helpers/morphoBlueHelpers/index.js +1 -0
- package/cjs/helpers/morphoMidnightHelpers/index.d.ts +33 -5
- package/cjs/helpers/morphoMidnightHelpers/index.js +66 -13
- package/cjs/llamaLend/index.js +1 -1
- package/cjs/markets/aaveV4/index.d.ts +2 -0
- package/cjs/markets/aaveV4/index.js +14 -1
- package/cjs/morphoBlue/index.d.ts +8 -6
- package/cjs/morphoBlue/index.js +69 -40
- package/cjs/portfolio/index.js +31 -2
- package/cjs/savings/summerVaults/options.js +10 -10
- package/cjs/staking/staking.js +3 -0
- package/cjs/types/aaveV4.d.ts +2 -1
- package/cjs/types/aaveV4.js +1 -0
- package/cjs/types/claiming.d.ts +10 -0
- package/cjs/types/claiming.js +2 -0
- package/cjs/types/morphoBlue.d.ts +9 -0
- package/esm/aaveV3/index.js +7 -1
- package/esm/aaveV4/lend.js +3 -3
- package/esm/claiming/index.d.ts +2 -1
- package/esm/claiming/index.js +2 -1
- package/esm/claiming/uniswap.d.ts +5 -0
- package/esm/claiming/uniswap.js +67 -0
- package/esm/config/contracts.d.ts +22 -0
- package/esm/config/contracts.js +8 -0
- package/esm/contracts.d.ts +35 -0
- package/esm/contracts.js +1 -0
- package/esm/curveUsd/index.js +1 -1
- package/esm/helpers/morphoBlueHelpers/index.js +1 -0
- package/esm/helpers/morphoMidnightHelpers/index.d.ts +33 -5
- package/esm/helpers/morphoMidnightHelpers/index.js +63 -12
- package/esm/llamaLend/index.js +1 -1
- package/esm/markets/aaveV4/index.d.ts +2 -0
- package/esm/markets/aaveV4/index.js +12 -0
- package/esm/morphoBlue/index.d.ts +8 -6
- package/esm/morphoBlue/index.js +62 -39
- package/esm/portfolio/index.js +32 -3
- package/esm/savings/summerVaults/options.js +10 -10
- package/esm/staking/staking.js +3 -0
- package/esm/types/aaveV4.d.ts +2 -1
- package/esm/types/aaveV4.js +1 -0
- package/esm/types/claiming.d.ts +10 -0
- package/esm/types/claiming.js +2 -0
- package/esm/types/morphoBlue.d.ts +9 -0
- package/package.json +2 -2
- package/src/aaveV3/index.ts +13 -1
- package/src/aaveV4/lend.ts +3 -3
- package/src/claiming/index.ts +2 -0
- package/src/claiming/uniswap.ts +70 -0
- package/src/config/contracts.ts +9 -0
- package/src/contracts.ts +2 -0
- package/src/curveUsd/index.ts +1 -1
- package/src/helpers/morphoBlueHelpers/index.ts +1 -0
- package/src/helpers/morphoMidnightHelpers/index.ts +87 -14
- package/src/llamaLend/index.ts +1 -1
- package/src/markets/aaveV4/index.ts +13 -0
- package/src/morphoBlue/index.ts +101 -44
- package/src/portfolio/index.ts +32 -3
- package/src/savings/summerVaults/options.ts +10 -10
- package/src/staking/staking.ts +2 -0
- package/src/types/aaveV4.ts +1 -0
- package/src/types/claiming.ts +11 -0
- package/src/types/morphoBlue.ts +11 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defisaver/positions-sdk",
|
|
3
|
-
"version": "2.1.127-midnight-
|
|
3
|
+
"version": "2.1.127-midnight-2-dev",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./cjs/index.js",
|
|
6
6
|
"module": "./esm/index.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"author": "",
|
|
22
22
|
"license": "ISC",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@defisaver/tokens": "^1.7.
|
|
24
|
+
"@defisaver/tokens": "^1.7.44",
|
|
25
25
|
"@types/lodash": "^4.17.15",
|
|
26
26
|
"@types/memoizee": "^0.4.12",
|
|
27
27
|
"decimal.js": "^10.6.0",
|
package/src/aaveV3/index.ts
CHANGED
|
@@ -84,9 +84,21 @@ export async function _getAaveV3MarketData(provider: Client, network: NetworkNum
|
|
|
84
84
|
const aaveIncentivesContract = AaveIncentiveDataProviderV3ContractViem(provider, network);
|
|
85
85
|
const marketAddress = market.providerAddress;
|
|
86
86
|
const networksWithIncentives = [NetworkNumber.Eth, NetworkNumber.Arb, NetworkNumber.Opt, NetworkNumber.Linea, NetworkNumber.Plasma];
|
|
87
|
+
// Limit each view call to 10 tokens, run chunks concurrently, then combine their results into loanInfo.
|
|
88
|
+
const addressesPerRequest = 10;
|
|
89
|
+
const addressChunks: EthAddress[][] = _addresses.length > addressesPerRequest
|
|
90
|
+
? Array.from(
|
|
91
|
+
{ length: Math.ceil(_addresses.length / addressesPerRequest) },
|
|
92
|
+
(_, index) => _addresses.slice(index * addressesPerRequest, (index + 1) * addressesPerRequest) as EthAddress[],
|
|
93
|
+
)
|
|
94
|
+
: [_addresses as EthAddress[]];
|
|
95
|
+
const loanInfoPromise = Promise.all(addressChunks.map((addresses) => loanInfoContract.read.getFullTokensInfo(
|
|
96
|
+
[marketAddress, addresses],
|
|
97
|
+
setViemBlockNumber(blockNumber),
|
|
98
|
+
))).then((loanInfoChunks) => loanInfoChunks.flat());
|
|
87
99
|
// eslint-disable-next-line prefer-const
|
|
88
100
|
let [loanInfo, eModesInfo, rewardInfo, merkleRewardsMap, meritRewardsMap] = await Promise.all([
|
|
89
|
-
|
|
101
|
+
loanInfoPromise,
|
|
90
102
|
loanInfoContract.read.getAllEmodes([marketAddress], setViemBlockNumber(blockNumber)),
|
|
91
103
|
networksWithIncentives.includes(network) ? aaveIncentivesContract.read.getReservesIncentivesData([marketAddress], setViemBlockNumber(blockNumber)) : null,
|
|
92
104
|
getMerkleCampaigns(network),
|
package/src/aaveV4/lend.ts
CHANGED
|
@@ -49,9 +49,9 @@ export const AAVE_V4_TOKENIZED_SPOKES: Record<string, EthAddress> = {
|
|
|
49
49
|
USDC_PRIME: '0x486415fb1F8b062c89ED548f871cf64304AACb31',
|
|
50
50
|
USDT_PRIME: '0x46c588DD8453aC259c1f6a54b4C9A93C2aC3762D',
|
|
51
51
|
|
|
52
|
-
USDC_PAXOS: '
|
|
53
|
-
USDT_PAXOS: '
|
|
54
|
-
PT_USDG_Sep_PAXOS: '
|
|
52
|
+
USDC_PAXOS: '0xaed7c529bD2878170B61C758DfAa215AC7a4FD07',
|
|
53
|
+
USDT_PAXOS: '0xa0e97e45C2f89003730E467Bd484fA3eEcE5B4Cf',
|
|
54
|
+
PT_USDG_Sep_PAXOS: '0x7Df10B4A01350D2A1d95cFbE7c9207d7210A2663',
|
|
55
55
|
};
|
|
56
56
|
|
|
57
57
|
export const AAVE_V4_TOKENIZED_SPOKE_ADDRESSES: Partial<Record<NetworkNumber, EthAddress[]>> = {
|
package/src/claiming/index.ts
CHANGED
|
@@ -2,10 +2,12 @@ import * as aaveV3Claim from './aaveV3';
|
|
|
2
2
|
import * as compV3Claim from './compV3';
|
|
3
3
|
import * as kingV3Claim from './king';
|
|
4
4
|
import * as sparkClaim from './spark';
|
|
5
|
+
import * as uniswapClaim from './uniswap';
|
|
5
6
|
|
|
6
7
|
export {
|
|
7
8
|
aaveV3Claim,
|
|
8
9
|
compV3Claim,
|
|
9
10
|
kingV3Claim,
|
|
10
11
|
sparkClaim,
|
|
12
|
+
uniswapClaim,
|
|
11
13
|
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import Dec from 'decimal.js';
|
|
2
|
+
import { Client } from 'viem';
|
|
3
|
+
import { assetAmountInEth } from '@defisaver/tokens';
|
|
4
|
+
import { EthAddress, NetworkNumber } from '../types';
|
|
5
|
+
import { UniswapTokenDistributorViem } from '../contracts';
|
|
6
|
+
import { ClaimType, UniswapAirdropClaimableToken } from '../types/claiming';
|
|
7
|
+
|
|
8
|
+
const EMPTY_DATA = (walletAddress: EthAddress) => ({
|
|
9
|
+
address: walletAddress, index: 0, amount: '0x0', proof: [],
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export const fetchUniswapRewardsData = async (walletAddress: EthAddress) => {
|
|
13
|
+
try {
|
|
14
|
+
const res = await fetch(`https://fe.defisaver.com/api/rewards/uniswap?user=${walletAddress}`,
|
|
15
|
+
{ signal: AbortSignal.timeout(5000) });
|
|
16
|
+
|
|
17
|
+
if (!res.ok) throw new Error(await res.text());
|
|
18
|
+
|
|
19
|
+
const data = await res.json();
|
|
20
|
+
if (data.data.error) return EMPTY_DATA(walletAddress);
|
|
21
|
+
|
|
22
|
+
return data.data;
|
|
23
|
+
} catch (err) {
|
|
24
|
+
return EMPTY_DATA(walletAddress);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const getUniswapRewards = async (provider: Client, network: NetworkNumber, walletAddresses: EthAddress[]): Promise<Record<string, UniswapAirdropClaimableToken[]>> => {
|
|
29
|
+
// Fetch all API data in parallel (these are external API calls, can't be batched with multicall)
|
|
30
|
+
const apiDataPromises = walletAddresses.map(address => fetchUniswapRewardsData(address));
|
|
31
|
+
const apiDataArray = await Promise.all(apiDataPromises);
|
|
32
|
+
|
|
33
|
+
// Batch all contract calls using multicall
|
|
34
|
+
const contract = UniswapTokenDistributorViem(provider, network);
|
|
35
|
+
const cumulativePromises = apiDataArray.map(data => (data.index ? contract.read.isClaimed([data.index]) : Promise.resolve(false)),
|
|
36
|
+
);
|
|
37
|
+
const cumulativeResults = await Promise.all(cumulativePromises);
|
|
38
|
+
|
|
39
|
+
// Process results
|
|
40
|
+
const results: Record<string, UniswapAirdropClaimableToken[]> = {};
|
|
41
|
+
|
|
42
|
+
for (let i = 0; i < walletAddresses.length; i++) {
|
|
43
|
+
const walletAddress = walletAddresses[i];
|
|
44
|
+
const data = apiDataArray[i];
|
|
45
|
+
const cumulative = cumulativeResults[i];
|
|
46
|
+
|
|
47
|
+
const amountToClaim = new Dec(data.amount);
|
|
48
|
+
|
|
49
|
+
if (amountToClaim.lessThanOrEqualTo('0') || cumulative) {
|
|
50
|
+
results[walletAddress.toLowerCase() as EthAddress] = [];
|
|
51
|
+
} else {
|
|
52
|
+
results[walletAddress.toLowerCase() as EthAddress] = [{
|
|
53
|
+
symbol: 'UNI',
|
|
54
|
+
underlyingSymbol: 'UNI',
|
|
55
|
+
label: 'Uniswap Airdrop',
|
|
56
|
+
tokenAddress: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984',
|
|
57
|
+
amount: assetAmountInEth(amountToClaim.toString(), 'UNI'),
|
|
58
|
+
walletAddress,
|
|
59
|
+
claimType: ClaimType.UNI_REWARDS,
|
|
60
|
+
additionalClaimFields: {
|
|
61
|
+
index: data.index,
|
|
62
|
+
isClaimed: cumulative,
|
|
63
|
+
proof: data.proof,
|
|
64
|
+
},
|
|
65
|
+
}];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return results;
|
|
70
|
+
};
|
package/src/config/contracts.ts
CHANGED
|
@@ -1360,3 +1360,12 @@ export const AaveV4View = {
|
|
|
1360
1360
|
}
|
|
1361
1361
|
}
|
|
1362
1362
|
} as const;
|
|
1363
|
+
|
|
1364
|
+
export const UniswapTokenDistributor = {
|
|
1365
|
+
"abi": [{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"isClaimed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}],
|
|
1366
|
+
"networks": {
|
|
1367
|
+
"1": {
|
|
1368
|
+
"address": "0x090D4613473dEE047c3f2706764f49E0821D256e",
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
} as const;
|
package/src/contracts.ts
CHANGED
|
@@ -182,3 +182,5 @@ export const MakerDsrContractViem = createViemContractFromConfigFunc('MakerDsr')
|
|
|
182
182
|
export const SkySavingsContractView = createViemContractFromConfigFunc('SkySavings');
|
|
183
183
|
|
|
184
184
|
export const AaveV4ViewContractViem = createViemContractFromConfigFunc('AaveV4View');
|
|
185
|
+
|
|
186
|
+
export const UniswapTokenDistributorViem = createViemContractFromConfigFunc('UniswapTokenDistributor');
|
package/src/curveUsd/index.ts
CHANGED
|
@@ -24,7 +24,7 @@ const getAndFormatBands = async (provider: Client, network: NetworkNumber, selec
|
|
|
24
24
|
// getBandsData uses a lot of gas to get all of the bands at once, so we use pagination and fetch 200 bands at a time
|
|
25
25
|
let i = minBand;
|
|
26
26
|
while (i < maxBand) {
|
|
27
|
-
i +=
|
|
27
|
+
i += 20;
|
|
28
28
|
if (i > maxBand) {
|
|
29
29
|
pivots.push(maxBand);
|
|
30
30
|
} else {
|
|
@@ -389,6 +389,7 @@ export const getRewardsForMarket = async (marketId: string, network: NetworkNumb
|
|
|
389
389
|
query: REWARDS_QUERY,
|
|
390
390
|
variables: { marketId, chainId: network },
|
|
391
391
|
}),
|
|
392
|
+
signal: AbortSignal.timeout(LONGER_TIMEOUT),
|
|
392
393
|
});
|
|
393
394
|
|
|
394
395
|
const data = await response.json();
|
|
@@ -98,6 +98,11 @@ export const getMorphoMidnightAggregatedPositionData = ({
|
|
|
98
98
|
const MIDNIGHT_API_BASE = 'https://api.morpho.org/v0/midnight';
|
|
99
99
|
const nowInSeconds = () => Math.floor(Date.now() / 1000);
|
|
100
100
|
|
|
101
|
+
// The quote endpoint's `slippage` query param is validated as a string: 0.1–100, at most one decimal
|
|
102
|
+
// place (`0.50` is rejected even though `0.5` passes). See `midnightSlippageParam`.
|
|
103
|
+
const MIDNIGHT_SLIPPAGE_MIN = 0.1;
|
|
104
|
+
const MIDNIGHT_SLIPPAGE_MAX = 100;
|
|
105
|
+
|
|
101
106
|
interface MidnightTransaction {
|
|
102
107
|
event_type: string,
|
|
103
108
|
market_id: string,
|
|
@@ -105,6 +110,20 @@ interface MidnightTransaction {
|
|
|
105
110
|
data: { seller_assets?: string, units?: string },
|
|
106
111
|
}
|
|
107
112
|
|
|
113
|
+
interface MidnightApiError {
|
|
114
|
+
code?: string,
|
|
115
|
+
message?: string,
|
|
116
|
+
details?: ({ field?: string, issue?: string })[] | null,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface MidnightQuoteResponse {
|
|
120
|
+
average_best_price?: string,
|
|
121
|
+
average_worst_price?: string,
|
|
122
|
+
available_assets?: string,
|
|
123
|
+
available_units?: string,
|
|
124
|
+
takeable_offers?: unknown[],
|
|
125
|
+
}
|
|
126
|
+
|
|
108
127
|
export interface MorphoMidnightBorrowInfo {
|
|
109
128
|
borrowRate: string, // weighted-average borrow APY as a percent
|
|
110
129
|
debtBase: string, // base borrowed (Σ seller_assets), loan-token units
|
|
@@ -116,9 +135,9 @@ export interface MorphoMidnightBorrowQuote {
|
|
|
116
135
|
bestPrice: string, // average_best_price, loan-per-unit
|
|
117
136
|
worstPrice: string, // average_worst_price, slippage-adjusted
|
|
118
137
|
estBorrowRate: string, // estimated borrow APY as a percent
|
|
119
|
-
maxRate: string, //
|
|
138
|
+
maxRate: string, // borrow APY the on-chain cap permits, i.e. `maxUnits` annualized (display only)
|
|
120
139
|
newUnits: string, // debt added at best price, raw loan-token base units
|
|
121
|
-
maxUnits: string, //
|
|
140
|
+
maxUnits: string, // capped debt (on-chain cap), raw loan-token base units
|
|
122
141
|
availableAssets: string,
|
|
123
142
|
availableUnits: string,
|
|
124
143
|
takeableOffers: any[], // opaque orderbook offers, forwarded verbatim to on-chain execution
|
|
@@ -138,6 +157,33 @@ export const midnightApyFromPrice = (price: Dec.Value, ttmDays: Dec.Value): stri
|
|
|
138
157
|
.toString();
|
|
139
158
|
};
|
|
140
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Inverse of `midnightApyFromPrice`: the loan-per-unit price a borrow APY implies,
|
|
162
|
+
* price = (1 + rate)^(−ttmDays / 365).
|
|
163
|
+
*
|
|
164
|
+
* This is what turns an absolute rate ceiling into an on-chain `maxUnits` cap (units = assets / price),
|
|
165
|
+
* and equally the principal a unit of borrow power is worth — Midnight debt is recorded at its maturity
|
|
166
|
+
* face value, so borrowing the full limit as principal would overshoot it by the interest.
|
|
167
|
+
*/
|
|
168
|
+
export const midnightPriceFromApy = (ratePercent: Dec.Value, ttmDays: Dec.Value): string => {
|
|
169
|
+
const rate = new Dec(ratePercent);
|
|
170
|
+
const ttm = new Dec(ttmDays);
|
|
171
|
+
if (rate.lte(0) || ttm.lte(0)) return '1';
|
|
172
|
+
return new Dec(1).div(new Dec(1).add(rate.div(100)).pow(ttm.div(365))).toString();
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Coerce a slippage into what the quote endpoint accepts: 0.1–100 with at most one decimal place. The
|
|
177
|
+
* validation is lexical, so a computed value (`4.15066671050631467`) is rejected outright — without this
|
|
178
|
+
* the request 400s and the quote looks unavailable.
|
|
179
|
+
*
|
|
180
|
+
* Rounded **down**, since a wider slippage is a looser cap than the caller asked for.
|
|
181
|
+
*/
|
|
182
|
+
export const midnightSlippageParam = (slippagePercent: Dec.Value): string => Dec.min(
|
|
183
|
+
Dec.max(new Dec(slippagePercent), MIDNIGHT_SLIPPAGE_MIN),
|
|
184
|
+
MIDNIGHT_SLIPPAGE_MAX,
|
|
185
|
+
).toDP(1, Dec.ROUND_DOWN).toString();
|
|
186
|
+
|
|
141
187
|
/**
|
|
142
188
|
* Current borrower rate + debt breakdown from the Midnight transactions API. On-chain we can only read the
|
|
143
189
|
* total debt at maturity (`units`); the base-vs-interest split and the effective borrow rate require the
|
|
@@ -179,31 +225,58 @@ export const getMorphoMidnightUserBorrowInfo = async (
|
|
|
179
225
|
};
|
|
180
226
|
};
|
|
181
227
|
|
|
228
|
+
// The API says why a quote failed — NOT_FOUND (market matured or not open yet), INSUFFICIENT_LIQUIDITY
|
|
229
|
+
// (book can't fill the size), VALIDATION_ERROR (bad param, with the offending field in `details`).
|
|
230
|
+
// Callers surface this to the user, so keep the reason rather than collapsing everything into one string.
|
|
231
|
+
const midnightQuoteError = (error?: MidnightApiError): string => {
|
|
232
|
+
const detail = (error?.details || []).map(({ issue }) => issue).filter(Boolean).join('; ');
|
|
233
|
+
const reason = detail || error?.message || error?.code;
|
|
234
|
+
return reason ? `Morpho Midnight quote unavailable: ${reason}` : 'Morpho Midnight quote unavailable';
|
|
235
|
+
};
|
|
236
|
+
|
|
182
237
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
238
|
+
* Quote a prospective borrow against the Midnight order book: the estimated rate, the debt units it adds,
|
|
239
|
+
* and the `maxUnits` cap sent on-chain to protect the user if better offers get filled first. `assetsRaw`
|
|
240
|
+
* (and the returned `newUnits`/`maxUnits`) are raw loan-token base units — callers convert to/from human
|
|
241
|
+
* amounts. Throws if the book can't fill the amount (caller handles).
|
|
242
|
+
*
|
|
243
|
+
* Two ways to set the cap:
|
|
244
|
+
* - `maxBorrowRate` — an absolute APY ceiling, honoured **exactly**: the cap price is derived locally via
|
|
245
|
+
* `midnightPriceFromApy`. Prefer this when a user pins a max rate.
|
|
246
|
+
* - otherwise `slippagePercent`, the API's own knob. Note it is a **price**-level slippage, not APY points:
|
|
247
|
+
* near maturity the annualisation factor (365 / ttmDays) multiplies it heavily, so on a 22-day market a
|
|
248
|
+
* slippage of 0.5 permitted an APY ~9pp above the estimate, not 0.5pp. It also saturates at the book's
|
|
249
|
+
* cheapest bid. `maxRate` therefore reports what the cap actually permits, derived from the cap price.
|
|
250
|
+
*
|
|
251
|
+
* A `maxBorrowRate` below `estBorrowRate` yields `maxUnits < newUnits` — the borrow would revert on-chain.
|
|
252
|
+
* Compare the two before submitting and tell the user their ceiling is under the market rate.
|
|
187
253
|
*/
|
|
188
254
|
export const getMorphoMidnightBorrowQuote = async (
|
|
189
255
|
marketId: string,
|
|
190
256
|
assetsRaw: string,
|
|
191
257
|
slippagePercent: Dec.Value,
|
|
192
258
|
maturity: number,
|
|
259
|
+
maxBorrowRate?: Dec.Value,
|
|
193
260
|
): Promise<MorphoMidnightBorrowQuote> => {
|
|
194
|
-
const url = `${MIDNIGHT_API_BASE}/books/${marketId}/bids/quote?assets=${assetsRaw}&slippage=${slippagePercent}`;
|
|
261
|
+
const url = `${MIDNIGHT_API_BASE}/books/${marketId}/bids/quote?assets=${assetsRaw}&slippage=${midnightSlippageParam(slippagePercent)}`;
|
|
195
262
|
const res = await fetch(url, { signal: AbortSignal.timeout(LONGER_TIMEOUT) });
|
|
196
|
-
const json: { data?:
|
|
263
|
+
const json: { data?: MidnightQuoteResponse, error?: MidnightApiError } = await res.json();
|
|
197
264
|
const d = json?.data;
|
|
198
|
-
if (!d?.average_best_price) throw new Error(
|
|
265
|
+
if (!d?.average_best_price) throw new Error(midnightQuoteError(json?.error));
|
|
199
266
|
|
|
200
267
|
const bestPrice = new Dec(d.average_best_price).div(WAD).toString();
|
|
201
|
-
const worstPrice = new Dec(d.average_worst_price).div(WAD).toString();
|
|
268
|
+
const worstPrice = new Dec(d.average_worst_price || 0).div(WAD).toString();
|
|
202
269
|
const ttmDays = midnightTimeToMaturityDays(maturity);
|
|
203
270
|
const estBorrowRate = midnightApyFromPrice(bestPrice, ttmDays);
|
|
204
|
-
|
|
271
|
+
|
|
272
|
+
// Price the cap sits at, and the rate that price represents — one derivation, so `maxRate` and
|
|
273
|
+
// `maxUnits` can never disagree about what the user is protected at.
|
|
274
|
+
const capPrice = maxBorrowRate !== undefined && new Dec(maxBorrowRate).gt(0)
|
|
275
|
+
? midnightPriceFromApy(maxBorrowRate, ttmDays)
|
|
276
|
+
: worstPrice;
|
|
277
|
+
const maxRate = midnightApyFromPrice(capPrice, ttmDays);
|
|
205
278
|
const newUnits = new Dec(bestPrice).lte(0) ? '0' : new Dec(assetsRaw).div(bestPrice).toFixed(0);
|
|
206
|
-
const maxUnits = new Dec(
|
|
279
|
+
const maxUnits = new Dec(capPrice).lte(0) ? '0' : new Dec(assetsRaw).div(capPrice).toFixed(0);
|
|
207
280
|
|
|
208
281
|
return {
|
|
209
282
|
bestPrice,
|
|
@@ -212,8 +285,8 @@ export const getMorphoMidnightBorrowQuote = async (
|
|
|
212
285
|
maxRate,
|
|
213
286
|
newUnits,
|
|
214
287
|
maxUnits,
|
|
215
|
-
availableAssets: d.available_assets,
|
|
216
|
-
availableUnits: d.available_units,
|
|
288
|
+
availableAssets: d.available_assets || '0',
|
|
289
|
+
availableUnits: d.available_units || '0',
|
|
217
290
|
takeableOffers: d.takeable_offers || [],
|
|
218
291
|
};
|
|
219
292
|
};
|
package/src/llamaLend/index.ts
CHANGED
|
@@ -24,7 +24,7 @@ const getAndFormatBands = async (provider: Client, network: NetworkNumber, selec
|
|
|
24
24
|
// getBandsData uses a lot of gas to get all of the bands at once, so we use pagination and fetch 200 bands at a time
|
|
25
25
|
let i = minBand;
|
|
26
26
|
while (i < maxBand) {
|
|
27
|
-
i +=
|
|
27
|
+
i += 20;
|
|
28
28
|
if (i > maxBand) {
|
|
29
29
|
pivots.push(maxBand);
|
|
30
30
|
} else {
|
|
@@ -193,6 +193,18 @@ export const AAVE_V4_USDG_PENDLE_SPOKE = (networkId: NetworkNumber): AaveV4Spoke
|
|
|
193
193
|
],
|
|
194
194
|
});
|
|
195
195
|
|
|
196
|
+
export const AAVE_V4_USDG_MAPLE_SPOKE = (networkId: NetworkNumber): AaveV4SpokeInfo => ({
|
|
197
|
+
chainIds: [NetworkNumber.Eth],
|
|
198
|
+
label: 'USDG Maple',
|
|
199
|
+
value: AaveV4SpokesType.AaveV4USDGMapleSpoke,
|
|
200
|
+
url: 'usdg-maple',
|
|
201
|
+
address: '0x774b9655413c34809c1f1b16b654465A89EBE989',
|
|
202
|
+
hubs: [
|
|
203
|
+
AAVE_V4_PAXOS_HUB(NetworkNumber.Eth).address,
|
|
204
|
+
AAVE_V4_CORE_HUB(NetworkNumber.Eth).address,
|
|
205
|
+
],
|
|
206
|
+
});
|
|
207
|
+
|
|
196
208
|
export const AaveV4Spokes = (networkId: NetworkNumber) => ({
|
|
197
209
|
[AaveV4SpokesType.AaveV4BluechipSpoke]: AAVE_V4_BLUECHIP_SPOKE(networkId),
|
|
198
210
|
[AaveV4SpokesType.AaveV4EthenaCorrelatedSpoke]: AAVE_V4_ETHENA_CORRELATED_SPOKE(networkId),
|
|
@@ -205,6 +217,7 @@ export const AaveV4Spokes = (networkId: NetworkNumber) => ({
|
|
|
205
217
|
[AaveV4SpokesType.AaveV4LombardBtcSpoke]: AAVE_V4_LOMBARD_BTC_SPOKE(networkId),
|
|
206
218
|
[AaveV4SpokesType.AaveV4MainSpoke]: AAVE_V4_MAIN_SPOKE(networkId),
|
|
207
219
|
[AaveV4SpokesType.AaveV4USDGPendleSpoke]: AAVE_V4_USDG_PENDLE_SPOKE(networkId),
|
|
220
|
+
[AaveV4SpokesType.AaveV4USDGMapleSpoke]: AAVE_V4_USDG_MAPLE_SPOKE(networkId),
|
|
208
221
|
}) as const;
|
|
209
222
|
|
|
210
223
|
export const getAaveV4SpokeTypeInfo = (type: AaveV4SpokesType, network?: NetworkNumber) => ({ ...AaveV4Spokes(network ?? NetworkNumber.Eth) }[type]);
|
package/src/morphoBlue/index.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
DFSFeedRegistryContractViem, FeedRegistryContractViem, MorphoBlueViewContractViem,
|
|
9
9
|
} from '../contracts';
|
|
10
10
|
import {
|
|
11
|
-
MorphoBlueAssetsData, MorphoBlueMarketData, MorphoBlueMarketInfo, MorphoBluePositionData,
|
|
11
|
+
MorphoBlueAssetsData, MorphoBlueEarnData, MorphoBlueMarketData, MorphoBlueMarketInfo, MorphoBlueMarketRewards, MorphoBluePositionData,
|
|
12
12
|
} from '../types';
|
|
13
13
|
import { USD_QUOTE, WAD } from '../constants';
|
|
14
14
|
import { calculateNetApy, getStakingApy, STAKING_ASSETS } from '../staking';
|
|
@@ -21,7 +21,39 @@ import { getViemProvider, setViemBlockNumber } from '../services/viem';
|
|
|
21
21
|
|
|
22
22
|
const HARDCODED_USD_STABLE_PRICE = '100000000'; // $1 with 8 decimals
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
const getMorphoRewardIncentives = (apy: string) => [{
|
|
25
|
+
token: 'MORPHO',
|
|
26
|
+
apy,
|
|
27
|
+
incentiveKind: IncentiveKind.Reward,
|
|
28
|
+
description: 'Eligible for protocol-level MORPHO incentives.',
|
|
29
|
+
}];
|
|
30
|
+
|
|
31
|
+
export const addMorphoBlueRewardsToMarketInfo = (
|
|
32
|
+
marketInfo: MorphoBlueMarketInfo,
|
|
33
|
+
rewards: MorphoBlueMarketRewards,
|
|
34
|
+
): MorphoBlueMarketInfo => ({
|
|
35
|
+
...marketInfo,
|
|
36
|
+
assetsData: {
|
|
37
|
+
...marketInfo.assetsData,
|
|
38
|
+
[marketInfo.loanToken]: {
|
|
39
|
+
...marketInfo.assetsData[marketInfo.loanToken],
|
|
40
|
+
supplyIncentives: [
|
|
41
|
+
...marketInfo.assetsData[marketInfo.loanToken].supplyIncentives.filter(({ token }) => token !== 'MORPHO'),
|
|
42
|
+
...getMorphoRewardIncentives(rewards.supplyApy),
|
|
43
|
+
],
|
|
44
|
+
borrowIncentives: [
|
|
45
|
+
...marketInfo.assetsData[marketInfo.loanToken].borrowIncentives.filter(({ token }) => token !== 'MORPHO'),
|
|
46
|
+
...getMorphoRewardIncentives(rewards.borrowApy),
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
async function getMorphoBlueMarketDataInternal(
|
|
53
|
+
provider: Client,
|
|
54
|
+
network: NetworkNumber,
|
|
55
|
+
selectedMarket: MorphoBlueMarketData,
|
|
56
|
+
): Promise<MorphoBlueMarketInfo> {
|
|
25
57
|
const {
|
|
26
58
|
loanToken, collateralToken, oracle, irm, lltv, oracleType,
|
|
27
59
|
} = selectedMarket;
|
|
@@ -59,16 +91,6 @@ export async function _getMorphoBlueMarketData(provider: Client, network: Networ
|
|
|
59
91
|
loanTokenPrice = loanTokenPriceRound[1].toString();
|
|
60
92
|
}
|
|
61
93
|
|
|
62
|
-
let morphoSupplyApy = '0';
|
|
63
|
-
let morphoBorrowApy = '0';
|
|
64
|
-
try {
|
|
65
|
-
const { supplyApy: _morphoSupplyApy, borrowApy: _morphoBorrowApy } = await getRewardsForMarket(selectedMarket.marketId, network);
|
|
66
|
-
morphoSupplyApy = _morphoSupplyApy;
|
|
67
|
-
morphoBorrowApy = _morphoBorrowApy;
|
|
68
|
-
} catch (e) {
|
|
69
|
-
console.error(e);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
94
|
const supplyRate = getSupplyRate(marketInfo.totalSupplyAssets.toString(), marketInfo.totalBorrowAssets.toString(), marketInfo.borrowRate.toString(), marketInfo.fee.toString());
|
|
73
95
|
const compoundedBorrowRate = getBorrowRate(marketInfo.borrowRate.toString(), marketInfo.totalBorrowShares.toString());
|
|
74
96
|
const utillization = new Dec(marketInfo.totalBorrowAssets.toString()).div(marketInfo.totalSupplyAssets.toString()).mul(100).toString();
|
|
@@ -90,18 +112,8 @@ export async function _getMorphoBlueMarketData(provider: Client, network: Networ
|
|
|
90
112
|
totalBorrow: new Dec(marketInfo.totalBorrowAssets.toString()).div(scale).toString(),
|
|
91
113
|
canBeSupplied: true,
|
|
92
114
|
canBeBorrowed: true,
|
|
93
|
-
supplyIncentives: [
|
|
94
|
-
|
|
95
|
-
apy: morphoSupplyApy,
|
|
96
|
-
incentiveKind: IncentiveKind.Reward,
|
|
97
|
-
description: 'Eligible for protocol-level MORPHO incentives.',
|
|
98
|
-
}],
|
|
99
|
-
borrowIncentives: [{
|
|
100
|
-
token: 'MORPHO',
|
|
101
|
-
apy: morphoBorrowApy,
|
|
102
|
-
incentiveKind: IncentiveKind.Reward,
|
|
103
|
-
description: 'Eligible for protocol-level MORPHO incentives.',
|
|
104
|
-
}],
|
|
115
|
+
supplyIncentives: [],
|
|
116
|
+
borrowIncentives: [],
|
|
105
117
|
};
|
|
106
118
|
|
|
107
119
|
assetsData[wethToEth(collateralTokenInfo.symbol)] = {
|
|
@@ -138,10 +150,70 @@ export async function _getMorphoBlueMarketData(provider: Client, network: Networ
|
|
|
138
150
|
};
|
|
139
151
|
}
|
|
140
152
|
|
|
153
|
+
export async function _getMorphoBlueMarketData(provider: Client, network: NetworkNumber, selectedMarket: MorphoBlueMarketData): Promise<MorphoBlueMarketInfo> {
|
|
154
|
+
const marketInfo = await getMorphoBlueMarketDataInternal(provider, network, selectedMarket);
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
const rewards = await getRewardsForMarket(selectedMarket.marketId, network);
|
|
158
|
+
return addMorphoBlueRewardsToMarketInfo(marketInfo, rewards);
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.error(error);
|
|
161
|
+
return addMorphoBlueRewardsToMarketInfo(marketInfo, { supplyApy: '0', borrowApy: '0' });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function _getMorphoBluePortfolioMarketData(provider: Client, network: NetworkNumber, selectedMarket: MorphoBlueMarketData): Promise<MorphoBlueMarketInfo> {
|
|
166
|
+
return getMorphoBlueMarketDataInternal(provider, network, selectedMarket);
|
|
167
|
+
}
|
|
168
|
+
|
|
141
169
|
export async function getMorphoBlueMarketData(provider: EthereumProvider, network: NetworkNumber, selectedMarket: MorphoBlueMarketData): Promise<MorphoBlueMarketInfo> {
|
|
142
170
|
return _getMorphoBlueMarketData(getViemProvider(provider, network), network, selectedMarket);
|
|
143
171
|
}
|
|
144
172
|
|
|
173
|
+
export function getMorphoBluePortfolioMarketData(provider: EthereumProvider, network: NetworkNumber, selectedMarket: MorphoBlueMarketData): Promise<MorphoBlueMarketInfo> {
|
|
174
|
+
return _getMorphoBluePortfolioMarketData(getViemProvider(provider, network), network, selectedMarket);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export const getMorphoBluePositionDataWithMarketInfo = (
|
|
178
|
+
data: MorphoBluePositionData,
|
|
179
|
+
marketInfo: MorphoBlueMarketInfo,
|
|
180
|
+
): MorphoBluePositionData => ({
|
|
181
|
+
...data,
|
|
182
|
+
...getMorphoBlueAggregatedPositionData({
|
|
183
|
+
usedAssets: data.usedAssets,
|
|
184
|
+
assetsData: marketInfo.assetsData,
|
|
185
|
+
marketInfo,
|
|
186
|
+
}),
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
export const getMorphoEarnDataWithMarketInfo = (data: MorphoBlueEarnData, marketInfo: MorphoBlueMarketInfo): MorphoBlueEarnData => {
|
|
190
|
+
const loanTokenInfo = marketInfo.assetsData[marketInfo.loanToken];
|
|
191
|
+
const usedAssets: MMUsedAssets = {
|
|
192
|
+
[marketInfo.loanToken]: {
|
|
193
|
+
symbol: loanTokenInfo.symbol,
|
|
194
|
+
supplied: data.amount,
|
|
195
|
+
borrowed: '0',
|
|
196
|
+
isSupplied: new Dec(data.amount).gt(0),
|
|
197
|
+
isBorrowed: false,
|
|
198
|
+
collateral: false,
|
|
199
|
+
suppliedUsd: data.amountUsd,
|
|
200
|
+
borrowedUsd: '0',
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
...data,
|
|
206
|
+
apy: calculateNetApy({ usedAssets, assetsData: marketInfo.assetsData as unknown as MMAssetsData }).netApy,
|
|
207
|
+
};
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
export function getMorphoBlueMarketRewards(
|
|
211
|
+
network: NetworkNumber,
|
|
212
|
+
selectedMarket: MorphoBlueMarketData,
|
|
213
|
+
): Promise<MorphoBlueMarketRewards> {
|
|
214
|
+
return getRewardsForMarket(selectedMarket.marketId, network);
|
|
215
|
+
}
|
|
216
|
+
|
|
145
217
|
export const _getMorphoBlueAccountBalances = async (provider: Client, network: NetworkNumber, block: Blockish, addressMapping: boolean, address: EthAddress, selectedMarket: MorphoBlueMarketData): Promise<PositionBalances> => {
|
|
146
218
|
let balances: PositionBalances = {
|
|
147
219
|
collateral: {},
|
|
@@ -238,7 +310,7 @@ export async function getMorphoBlueAccountData(provider: EthereumProvider, netwo
|
|
|
238
310
|
return _getMorphoBlueAccountData(getViemProvider(provider, network), network, account, selectedMarket, marketInfo);
|
|
239
311
|
}
|
|
240
312
|
|
|
241
|
-
export async function getMorphoEarn(provider: Client, network: NetworkNumber, account: EthAddress, selectedMarket: MorphoBlueMarketData, marketInfo: MorphoBlueMarketInfo): Promise<
|
|
313
|
+
export async function getMorphoEarn(provider: Client, network: NetworkNumber, account: EthAddress, selectedMarket: MorphoBlueMarketData, marketInfo: MorphoBlueMarketInfo): Promise<MorphoBlueEarnData> {
|
|
242
314
|
const {
|
|
243
315
|
loanToken, collateralToken, oracle, irm, lltv,
|
|
244
316
|
} = selectedMarket;
|
|
@@ -254,24 +326,9 @@ export async function getMorphoEarn(provider: Client, network: NetworkNumber, ac
|
|
|
254
326
|
const loanTokenInfo = marketInfo.assetsData[marketInfo.loanToken];
|
|
255
327
|
const loanTokenSupplied = assetAmountInEth(loanInfo.suppliedInAssets.toString(), marketInfo.loanToken);
|
|
256
328
|
const loanTokenSuppliedUsd = new Dec(loanTokenSupplied).mul(loanTokenInfo.price).toString();
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
symbol: loanTokenInfo.symbol,
|
|
260
|
-
supplied: loanTokenSupplied,
|
|
261
|
-
borrowed: '0',
|
|
262
|
-
isSupplied: new Dec(loanInfo.suppliedInAssets.toString()).gt(0),
|
|
263
|
-
isBorrowed: false,
|
|
264
|
-
collateral: false,
|
|
265
|
-
suppliedUsd: loanTokenSuppliedUsd,
|
|
266
|
-
borrowedUsd: '0',
|
|
267
|
-
},
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
const { netApy } = calculateNetApy({ usedAssets, assetsData: marketInfo.assetsData as unknown as MMAssetsData });
|
|
271
|
-
|
|
272
|
-
return {
|
|
273
|
-
apy: netApy,
|
|
329
|
+
return getMorphoEarnDataWithMarketInfo({
|
|
330
|
+
apy: '0',
|
|
274
331
|
amount: loanTokenSupplied,
|
|
275
332
|
amountUsd: loanTokenSuppliedUsd,
|
|
276
|
-
};
|
|
277
|
-
}
|
|
333
|
+
}, marketInfo);
|
|
334
|
+
}
|
package/src/portfolio/index.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
MorphoMidnightMarkets,
|
|
13
13
|
SparkMarkets,
|
|
14
14
|
} from '../markets';
|
|
15
|
-
import { _getMorphoBlueAccountData,
|
|
15
|
+
import { _getMorphoBlueAccountData, _getMorphoBluePortfolioMarketData, getMorphoEarn } from '../morphoBlue';
|
|
16
16
|
import { _getMorphoMidnightAccountData, _getMorphoMidnightMarketData, getMorphoMidnightEarn } from '../morphoMidnight';
|
|
17
17
|
import {
|
|
18
18
|
AaveV2MarketData,
|
|
@@ -54,6 +54,7 @@ import { fetchSparkAirdropRewards, fetchSparkRewards } from '../claiming/spark';
|
|
|
54
54
|
import { getKingRewards } from '../claiming/king';
|
|
55
55
|
import { fetchEthenaAirdropRewards } from '../claiming/ethena';
|
|
56
56
|
import { _getAaveV4AccountData, _getAaveV4SpokeData } from '../aaveV4';
|
|
57
|
+
import { getUniswapRewards } from '../claiming/uniswap';
|
|
57
58
|
|
|
58
59
|
export async function getPortfolioData(provider: EthereumProvider, network: NetworkNumber, defaultProvider: EthereumProvider, addresses: EthAddress[], isSim = false): Promise<{
|
|
59
60
|
positions: PortfolioPositionsData;
|
|
@@ -78,7 +79,8 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
78
79
|
const liquityV2MarketsStaking = [NetworkNumber.Eth].includes(network) ? Object.values(LiquityV2Markets(network)).filter(market => !market.isLegacy) : [];
|
|
79
80
|
const aaveV4Spokes = Object.values(AaveV4Spokes(network)).filter((market) => market.chainIds.includes(network));
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
|
|
83
|
+
const args: [NetworkNumber, any?] = [network, { batch: { multicall: { batchSize: isSim ? 2_000 : 2_500_000 } } }];
|
|
82
84
|
const client = getViemProvider(provider, ...args);
|
|
83
85
|
const defaultClient = getViemProvider(defaultProvider, ...args);
|
|
84
86
|
|
|
@@ -170,7 +172,7 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
170
172
|
await Promise.allSettled([
|
|
171
173
|
// === MARKET DATA (needs to be fetched first) ===
|
|
172
174
|
...morphoMarkets.map(async (market) => {
|
|
173
|
-
const marketData = await
|
|
175
|
+
const marketData = await _getMorphoBluePortfolioMarketData(client, network, market);
|
|
174
176
|
morphoMarketsData[market.value] = marketData;
|
|
175
177
|
}),
|
|
176
178
|
...morphoMidnightMarkets.map(async (market) => {
|
|
@@ -322,6 +324,33 @@ export async function getPortfolioData(provider: EthereumProvider, network: Netw
|
|
|
322
324
|
}
|
|
323
325
|
}
|
|
324
326
|
})(),
|
|
327
|
+
// Batch UNI rewards
|
|
328
|
+
(async () => {
|
|
329
|
+
try {
|
|
330
|
+
if (!isMainnet) {
|
|
331
|
+
for (const address of addresses) {
|
|
332
|
+
rewardsData[address.toLowerCase()].uniswap = { error: '', data: [] };
|
|
333
|
+
}
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const uniswapRewards = await getUniswapRewards(client, network, addresses);
|
|
337
|
+
for (const address of addresses) {
|
|
338
|
+
const lowerAddress = address.toLowerCase() as EthAddress;
|
|
339
|
+
rewardsData[lowerAddress].uniswap = {
|
|
340
|
+
error: '',
|
|
341
|
+
data: uniswapRewards[lowerAddress] || [],
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
} catch (error) {
|
|
345
|
+
console.error('Error fetching Uniswap rewards data in batch:', error);
|
|
346
|
+
for (const address of addresses) {
|
|
347
|
+
rewardsData[address.toLowerCase() as EthAddress].uniswap = {
|
|
348
|
+
error: 'Error fetching Uniswap rewards data in batch',
|
|
349
|
+
data: null,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
})(),
|
|
325
354
|
...sparkMarkets.map((market) => addresses.map(async address => {
|
|
326
355
|
try {
|
|
327
356
|
if (!isMainnet) {
|