@gearbox-protocol/sdk 17.2.0-next.1 → 17.2.0-next.3
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/cjs/model/charts.js +5 -0
- package/dist/cjs/model/opportunities.schema.js +2 -1
- package/dist/cjs/onchain/accounts/intents/leverage-band.js +1 -1
- package/dist/cjs/onchain/accounts/intents/maxBorrow.js +8 -6
- package/dist/cjs/onchain/accounts/intents/testing/sdk-mock.js +5 -3
- package/dist/cjs/onchain/chain/chains.js +1 -2
- package/dist/cjs/onchain/index.js +2 -0
- package/dist/cjs/onchain/market/MarketSuite.js +11 -4
- package/dist/cjs/onchain/market/credit/CreditSuite.js +24 -177
- package/dist/cjs/onchain/market/credit/CreditSuiteStrategy.js +179 -0
- package/dist/cjs/onchain/market/credit/index.js +2 -0
- package/dist/cjs/onchain/market/index.js +2 -0
- package/dist/cjs/onchain/opportunities/OpportunitiesService.js +7 -9
- package/dist/cjs/sdk/prepare/PrepareApi.js +1 -1
- package/dist/esm/model/charts.js +5 -0
- package/dist/esm/model/opportunities.schema.js +2 -1
- package/dist/esm/onchain/accounts/intents/leverage-band.js +1 -1
- package/dist/esm/onchain/accounts/intents/maxBorrow.js +8 -6
- package/dist/esm/onchain/accounts/intents/testing/sdk-mock.js +5 -3
- package/dist/esm/onchain/chain/chains.js +1 -2
- package/dist/esm/onchain/index.js +2 -1
- package/dist/esm/onchain/market/MarketSuite.js +11 -4
- package/dist/esm/onchain/market/credit/CreditSuite.js +26 -179
- package/dist/esm/onchain/market/credit/CreditSuiteStrategy.js +178 -0
- package/dist/esm/onchain/market/credit/index.js +2 -1
- package/dist/esm/onchain/market/index.js +2 -1
- package/dist/esm/onchain/opportunities/OpportunitiesService.js +7 -9
- package/dist/esm/sdk/prepare/PrepareApi.js +1 -1
- package/dist/types/model/charts.d.ts +5 -2
- package/dist/types/model/charts.schema.d.ts +8 -0
- package/dist/types/model/opportunities.d.ts +4 -0
- package/dist/types/model/opportunities.schema.d.ts +19 -0
- package/dist/types/model/positions.schema.d.ts +9 -0
- package/dist/types/onchain/accounts/intents/maxBorrow.d.ts +5 -5
- package/dist/types/onchain/index.d.ts +2 -1
- package/dist/types/onchain/market/MarketSuite.d.ts +6 -0
- package/dist/types/onchain/market/credit/CreditSuite.d.ts +17 -70
- package/dist/types/onchain/market/credit/CreditSuiteStrategy.d.ts +78 -0
- package/dist/types/onchain/market/credit/index.d.ts +2 -1
- package/dist/types/onchain/market/credit/types.d.ts +3 -1
- package/dist/types/onchain/market/index.d.ts +2 -1
- package/package.json +1 -1
package/dist/cjs/model/charts.js
CHANGED
|
@@ -26,10 +26,13 @@ const CHART_RANGES = [
|
|
|
26
26
|
];
|
|
27
27
|
/**
|
|
28
28
|
* Every metric a pool opportunity can chart.
|
|
29
|
+
*
|
|
30
|
+
* `rewardsApyAvg7d` is the trailing seven-day average yield from rewards.
|
|
29
31
|
**/
|
|
30
32
|
const POOL_OPPORTUNITY_CHART_METRICS = [
|
|
31
33
|
"depositApy",
|
|
32
34
|
"depositApyAvg7d",
|
|
35
|
+
"rewardsApyAvg7d",
|
|
33
36
|
"dieselRate",
|
|
34
37
|
"supplied",
|
|
35
38
|
"borrowed",
|
|
@@ -44,6 +47,7 @@ const POOL_OPPORTUNITY_CHART_METRICS = [
|
|
|
44
47
|
const STRATEGY_OPPORTUNITY_CHART_METRICS = [
|
|
45
48
|
"borrowApy",
|
|
46
49
|
"borrowApyAvg7d",
|
|
50
|
+
"rewardsApyAvg7d",
|
|
47
51
|
"quotaRate",
|
|
48
52
|
"liquidationThreshold",
|
|
49
53
|
"collateralApy",
|
|
@@ -106,6 +110,7 @@ const PROTOCOL_CHART_METRICS = ["tvlUsd"];
|
|
|
106
110
|
const CHART_METRIC_UNITS = {
|
|
107
111
|
depositApy: "bps",
|
|
108
112
|
depositApyAvg7d: "bps",
|
|
113
|
+
rewardsApyAvg7d: "bps",
|
|
109
114
|
borrowApy: "bps",
|
|
110
115
|
borrowApyAvg7d: "bps",
|
|
111
116
|
quotaRate: "bps",
|
|
@@ -31,7 +31,8 @@ const tokenRewardsSchema = zod_v4.z.object({
|
|
|
31
31
|
kind: zod_v4.z.literal("token"),
|
|
32
32
|
token: require_model_primitives_schema.tokenSchema,
|
|
33
33
|
supplyApr: require_model_primitives_schema.bpsSchema.optional(),
|
|
34
|
-
borrowApr: require_model_primitives_schema.bpsSchema.optional()
|
|
34
|
+
borrowApr: require_model_primitives_schema.bpsSchema.optional(),
|
|
35
|
+
links: zod_v4.z.array(zod_v4.z.string()).optional()
|
|
35
36
|
});
|
|
36
37
|
/**
|
|
37
38
|
* {@link PointRewards}
|
|
@@ -34,7 +34,7 @@ function calcLeverageBand({ sdk, creditManager, collateral, targetHF }) {
|
|
|
34
34
|
const found = require_onchain_accounts_intents_utils_common.resolveCreditManager(sdk, creditManager);
|
|
35
35
|
if (!found) return;
|
|
36
36
|
const { suite, market } = found;
|
|
37
|
-
const target = suite.
|
|
37
|
+
const target = suite.strategy?.targetCollateral;
|
|
38
38
|
if (!target) return;
|
|
39
39
|
const ceiling = suite.creditManager.maxLeverage(target, targetHF);
|
|
40
40
|
const underlying = market.pool.underlying;
|
|
@@ -20,9 +20,9 @@ const require_onchain_accounts_intents_collateral_valuation = require("./collate
|
|
|
20
20
|
* Collateral is valued the way the transaction will be judged — at safe
|
|
21
21
|
* prices, under its liquidation threshold, capped by the quota the borrow
|
|
22
22
|
* buys for it, all of which is {@link collateralValuation}'s business. The ceiling
|
|
23
|
-
* is then held to what the market will actually lend
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
* is then held to what the market will actually lend,
|
|
24
|
+
* {@link CreditSuite.maxBorrowAmount}: the pool's free liquidity, the
|
|
25
|
+
* manager's own allowance and the facade's `maxDebt`, whichever binds first.
|
|
26
26
|
*
|
|
27
27
|
* The facade's `minDebt` is not applied to the collateral's own ceiling. It
|
|
28
28
|
* is a floor, and a ceiling answered as `0n` because the collateral is too
|
|
@@ -31,8 +31,8 @@ const require_onchain_accounts_intents_collateral_valuation = require("./collate
|
|
|
31
31
|
* that carries something therefore answers with it, whether or not the market
|
|
32
32
|
* would lend that little; a loan under the floor is refused by `borrow`
|
|
33
33
|
* itself, with `debtOutOfRange` naming both ends. A market whose own capacity
|
|
34
|
-
* is under `minDebt` is different:
|
|
35
|
-
*
|
|
34
|
+
* is under `minDebt` is different: the answer is `0n`, because no loan of any
|
|
35
|
+
* size exists there.
|
|
36
36
|
*
|
|
37
37
|
* Nothing is fetched or simulated — the account does not exist yet and every
|
|
38
38
|
* input is loaded market state, so a form can call this on each keystroke.
|
|
@@ -83,7 +83,9 @@ function maxBorrow(props) {
|
|
|
83
83
|
const weighted = valuation.checkedUsd(holding) * valuation.lt(collateralToken);
|
|
84
84
|
const backed = quotas.some((q) => require_onchain_accounts_intents_utils_common.eq(q.token, collateralToken)) ? require_onchain_utils_bigint_math.BigIntMath.min(valuation.quotaValue(holding), weighted) : weighted;
|
|
85
85
|
if (backed <= 0n) return 0n;
|
|
86
|
-
const
|
|
86
|
+
const lends = suite.maxBorrowAmount().amount.value;
|
|
87
|
+
if (lends < suite.creditFacade.minDebt) return 0n;
|
|
88
|
+
const ceiling = require_onchain_utils_bigint_math.BigIntMath.min(priceOracle.safeConvertFromUSD(underlying, backed / targetHF).value, lends);
|
|
87
89
|
const unwrapsPayout = !!rwaAsset && require_onchain_accounts_intents_utils_common.eq(borrowToken, rwaAsset);
|
|
88
90
|
return require_onchain_accounts_intents_utils_common.eq(borrowToken, underlying) ? ceiling : unwrapsPayout ? require_onchain_accounts_intents_utils_common.toTargetDecimals(ceiling, underlying, borrowToken, sdk) : priceOracle.safeConvert(underlying, borrowToken, ceiling).value;
|
|
89
91
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require("../../../constants/math.js");
|
|
2
2
|
require("../../../constants/index.js");
|
|
3
3
|
const require_onchain_market_math = require("../../../market/math.js");
|
|
4
|
+
const require_onchain_market_credit_CreditSuiteStrategy = require("../../../market/credit/CreditSuiteStrategy.js");
|
|
4
5
|
const require_onchain_market_credit_CreditSuite = require("../../../market/credit/CreditSuite.js");
|
|
5
6
|
const require_onchain_positions_PositionsService = require("../../../positions/PositionsService.js");
|
|
6
7
|
const require_TestPriceOracle_mock = require("../../../market/oracle/TestPriceOracle.mock.js");
|
|
@@ -189,8 +190,10 @@ function buildMockSdk(args) {
|
|
|
189
190
|
const strategyName = strategyTargetCollateral ? `${tokenOf(strategyTargetCollateral).symbol} / ${underlyingToken.symbol}` : void 0;
|
|
190
191
|
const creditManagerSuite = {
|
|
191
192
|
name: "TestCreditManager",
|
|
192
|
-
strategyName,
|
|
193
193
|
underlyingToken,
|
|
194
|
+
get strategy() {
|
|
195
|
+
return strategyTargetCollateral ? new require_onchain_market_credit_CreditSuiteStrategy.CreditSuiteStrategy(creditManagerSuite, strategyTargetCollateral) : void 0;
|
|
196
|
+
},
|
|
194
197
|
accountTargetCollateral: () => strategyTargetCollateral ? tokenOf(strategyTargetCollateral) : null,
|
|
195
198
|
accountStrategyName: () => strategyName ?? underlyingToken.symbol,
|
|
196
199
|
liquidationFees: () => MOCK_LIQUIDATION_FEES,
|
|
@@ -198,7 +201,6 @@ function buildMockSdk(args) {
|
|
|
198
201
|
creditOperationMarket: require_onchain_market_credit_CreditSuite.CreditSuite.prototype.creditOperationMarket,
|
|
199
202
|
isForbidden: require_onchain_market_credit_CreditSuite.CreditSuite.prototype.isForbidden,
|
|
200
203
|
maxBorrowAmount: require_onchain_market_credit_CreditSuite.CreditSuite.prototype.maxBorrowAmount,
|
|
201
|
-
maxStrategyBorrowAmount: require_onchain_market_credit_CreditSuite.CreditSuite.prototype.maxStrategyBorrowAmount,
|
|
202
204
|
creditManager: {
|
|
203
205
|
address: args.creditManager,
|
|
204
206
|
liquidationThresholds,
|
|
@@ -220,7 +222,6 @@ function buildMockSdk(args) {
|
|
|
220
222
|
market,
|
|
221
223
|
isPaused: facadePaused || poolPaused,
|
|
222
224
|
forbiddenTokens: [...forbidden],
|
|
223
|
-
strategyTargetCollateral,
|
|
224
225
|
isExpired: expirationDate > 0 && expirationDate < (args.timestamp ?? 0)
|
|
225
226
|
};
|
|
226
227
|
const routeCalls = (tokenIn, tokenOut) => {
|
|
@@ -357,6 +358,7 @@ function buildMockSdk(args) {
|
|
|
357
358
|
}
|
|
358
359
|
};
|
|
359
360
|
Object.assign(sdk, { positions: new require_onchain_positions_PositionsService.PositionsService(sdk) });
|
|
361
|
+
Object.assign(creditManagerSuite, { sdk });
|
|
360
362
|
return sdk;
|
|
361
363
|
}
|
|
362
364
|
/** A full account payload carrying exactly what the slice builder reads back. */
|
|
@@ -96,8 +96,7 @@ const chains = {
|
|
|
96
96
|
"0x89014edc549ffa5c5b6e859b1496731bd035c247": "0x31454faa1daa04cacf59a6bd37681da9160d092a",
|
|
97
97
|
"0x3b7ab1f4fee570933b24b202de90ffda82f6cae0": "0x31454faa1daa04cacf59a6bd37681da9160d092a",
|
|
98
98
|
"0x721798d8ccf31ae75c12db82fa72b3806759cbc9": "0x31454faa1daa04cacf59a6bd37681da9160d092a",
|
|
99
|
-
"0xd7273d9594ac88f993eda9773041e621633acea0": "0x1a711a5bc48b5c1352c1882fa65dc14b5b9e829d"
|
|
100
|
-
"0x34442ca47435e90b80d835aab9737166e76d9962": "0x403cc0d2694ec2639101f32b146b90d766461ce9"
|
|
99
|
+
"0xd7273d9594ac88f993eda9773041e621633acea0": "0x1a711a5bc48b5c1352c1882fa65dc14b5b9e829d"
|
|
101
100
|
}),
|
|
102
101
|
sunsetPools: new require_onchain_utils_AddressSet.AddressSet([
|
|
103
102
|
"0xF791Ecc5F2472637eac9DFe3f7894C0B32C32bDf",
|
|
@@ -140,6 +140,7 @@ const require_onchain_market_rwa_midas_constants = require("./market/rwa/midas/c
|
|
|
140
140
|
const require_onchain_market_rwa_midas_MidasDegenNFT = require("./market/rwa/midas/MidasDegenNFT.js");
|
|
141
141
|
const require_onchain_market_rwa_securitize_constants = require("./market/rwa/securitize/constants.js");
|
|
142
142
|
const require_onchain_market_strategyName = require("./market/strategyName.js");
|
|
143
|
+
const require_onchain_market_credit_CreditSuiteStrategy = require("./market/credit/CreditSuiteStrategy.js");
|
|
143
144
|
const require_onchain_market_credit_collateralUtils = require("./market/credit/collateralUtils.js");
|
|
144
145
|
const require_onchain_market_credit_CreditSuite = require("./market/credit/CreditSuite.js");
|
|
145
146
|
const require_onchain_market_credit_expectedBalanceDeltas = require("./market/credit/expectedBalanceDeltas.js");
|
|
@@ -381,6 +382,7 @@ exports.CreditFacadeV310BaseContract = require_onchain_market_credit_CreditFacad
|
|
|
381
382
|
exports.CreditFacadeV310Contract = require_onchain_market_credit_CreditFacadeV310Contract.CreditFacadeV310Contract;
|
|
382
383
|
exports.CreditManagerV310Contract = require_onchain_market_credit_CreditManagerV310Contract.CreditManagerV310Contract;
|
|
383
384
|
exports.CreditSuite = require_onchain_market_credit_CreditSuite.CreditSuite;
|
|
385
|
+
exports.CreditSuiteStrategy = require_onchain_market_credit_CreditSuiteStrategy.CreditSuiteStrategy;
|
|
384
386
|
exports.Curve2AssetsAdapterContract = require_onchain_market_adapters_contracts_Curve2AssetsAdapterContract.Curve2AssetsAdapterContract;
|
|
385
387
|
exports.Curve3AssetsAdapterContract = require_onchain_market_adapters_contracts_Curve3AssetsAdapterContract.Curve3AssetsAdapterContract;
|
|
386
388
|
exports.Curve4AssetsAdapterContract = require_onchain_market_adapters_contracts_Curve4AssetsAdapterContract.Curve4AssetsAdapterContract;
|
|
@@ -237,13 +237,20 @@ var MarketSuite = class extends require_onchain_base_SDKConstruct.SDKConstruct {
|
|
|
237
237
|
const rows = [];
|
|
238
238
|
const kind = filter?.kind;
|
|
239
239
|
if (!require_model_filters.isFilterSet(kind) || kind === "pool") rows.push(this.poolOpportunity());
|
|
240
|
-
if (!require_model_filters.isFilterSet(kind) || kind === "strategy")
|
|
241
|
-
const opportunity = suite.strategyOpportunity();
|
|
242
|
-
if (opportunity) rows.push(opportunity);
|
|
243
|
-
}
|
|
240
|
+
if (!require_model_filters.isFilterSet(kind) || kind === "strategy") rows.push(...this.strategies().map((s) => s.opportunity()));
|
|
244
241
|
return rows.filter((row) => require_model_opportunities.matchesOpportunityFilter(row, filter));
|
|
245
242
|
}
|
|
246
243
|
/**
|
|
244
|
+
* Strategies this market lists as opportunities; see
|
|
245
|
+
* {@link CreditSuiteStrategy.isListed}.
|
|
246
|
+
*/
|
|
247
|
+
strategies() {
|
|
248
|
+
return this.creditManagers.flatMap((suite) => {
|
|
249
|
+
const strategy = suite.strategy;
|
|
250
|
+
return strategy?.isListed ? [strategy] : [];
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
247
254
|
* Passive lending into this market's pool, as the shared read model
|
|
248
255
|
* describes it.
|
|
249
256
|
*/
|
|
@@ -10,6 +10,7 @@ require("../../base/index.js");
|
|
|
10
10
|
const require_onchain_market_math = require("../math.js");
|
|
11
11
|
const require_onchain_market_rwa_createDegenNFT = require("../rwa/createDegenNFT.js");
|
|
12
12
|
const require_onchain_market_strategyName = require("../strategyName.js");
|
|
13
|
+
const require_onchain_market_credit_CreditSuiteStrategy = require("./CreditSuiteStrategy.js");
|
|
13
14
|
const require_onchain_market_credit_collateralUtils = require("./collateralUtils.js");
|
|
14
15
|
const require_onchain_market_credit_createCreditConfigurator = require("./createCreditConfigurator.js");
|
|
15
16
|
const require_onchain_market_credit_createCreditFacade = require("./createCreditFacade.js");
|
|
@@ -17,13 +18,6 @@ const require_onchain_market_credit_createCreditManager = require("./createCredi
|
|
|
17
18
|
let viem = require("viem");
|
|
18
19
|
//#region src/onchain/market/credit/CreditSuite.ts
|
|
19
20
|
/**
|
|
20
|
-
* Amount of underlying seeded into each pool at market creation to protect
|
|
21
|
-
* from inflation attacks, in raw token units. A suite whose
|
|
22
|
-
* {@link CreditSuite.maxStrategyBorrowAmount} is at or below this is treated as
|
|
23
|
-
* having nothing left to lend.
|
|
24
|
-
**/
|
|
25
|
-
const MIN_STRATEGY_BORROW_AMOUNT = 100000n;
|
|
26
|
-
/**
|
|
27
21
|
* SDK aggregate for one credit-manager branch inside a market.
|
|
28
22
|
*
|
|
29
23
|
* @remarks
|
|
@@ -130,31 +124,6 @@ var CreditSuite = class extends require_onchain_base_SDKConstruct.SDKConstruct {
|
|
|
130
124
|
return this.#degenNFT;
|
|
131
125
|
}
|
|
132
126
|
/**
|
|
133
|
-
* The KYC gate of this suite's strategy; `null` when there is none.
|
|
134
|
-
* Wallet-independent.
|
|
135
|
-
*/
|
|
136
|
-
async kycRequirement(targetCollateral) {
|
|
137
|
-
const nft = await this.degenNFT();
|
|
138
|
-
if (!nft) return null;
|
|
139
|
-
const tokens = await nft.getTokens();
|
|
140
|
-
const token = tokens.find((t) => (0, viem.isAddressEqual)(t, targetCollateral)) ?? tokens[0];
|
|
141
|
-
return {
|
|
142
|
-
protocol: nft.protocol,
|
|
143
|
-
token: token ? this.tokensMeta.getToken(token) : void 0,
|
|
144
|
-
registrationLink: nft.registrationLink
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Whether `wallet` may open this suite's strategy today; `true` when there
|
|
149
|
-
* is no KYC gate.
|
|
150
|
-
*/
|
|
151
|
-
async isEligibleForStrategy(wallet, targetCollateral) {
|
|
152
|
-
const nft = await this.degenNFT();
|
|
153
|
-
if (!nft) return true;
|
|
154
|
-
const requirements = await nft.getOpenAccountRequirements(wallet, { tokenOutAddress: targetCollateral });
|
|
155
|
-
return nft.isRegistered(requirements);
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
127
|
* Builds a transaction that executes a multicall on one of this suite's
|
|
159
128
|
* credit accounts.
|
|
160
129
|
*
|
|
@@ -247,13 +216,14 @@ var CreditSuite = class extends require_onchain_base_SDKConstruct.SDKConstruct {
|
|
|
247
216
|
* row all spread it, so the five fields are filled in one place and cannot
|
|
248
217
|
* drift apart between the halves of the SDK.
|
|
249
218
|
*
|
|
250
|
-
* The curator comes from the same getter
|
|
219
|
+
* The curator comes from the same getter
|
|
220
|
+
* {@link CreditSuiteStrategy.opportunity} reads, so
|
|
251
221
|
* a result and the opportunity beside it name one entity.
|
|
252
222
|
*/
|
|
253
223
|
creditOperationMarket() {
|
|
254
224
|
return {
|
|
255
225
|
creditManager: this.creditManager.address,
|
|
256
|
-
name: this.
|
|
226
|
+
name: this.strategy?.name ?? this.underlyingToken.symbol,
|
|
257
227
|
underlyingToken: this.underlyingToken,
|
|
258
228
|
curator: this.market.curator,
|
|
259
229
|
liquidationDiscount: this.totalLiquidationDiscount()
|
|
@@ -282,19 +252,14 @@ var CreditSuite = class extends require_onchain_base_SDKConstruct.SDKConstruct {
|
|
|
282
252
|
return this.forbiddenTokens.some((f) => (0, viem.isAddressEqual)(f, token));
|
|
283
253
|
}
|
|
284
254
|
/**
|
|
285
|
-
* Largest debt this credit manager
|
|
286
|
-
* and which limit set that number.
|
|
255
|
+
* Largest debt this credit manager allows to borrow, and which limit set that number.
|
|
287
256
|
*
|
|
288
257
|
* Minimum of:
|
|
289
258
|
* - the pool's available liquidity,
|
|
290
259
|
* - this manager's remaining debt allowance, and
|
|
291
260
|
* - the facade's per-account `maxDebt`.
|
|
292
|
-
* While `maxDebtPerBlockMultiplier` is `0` the facade
|
|
293
|
-
* takes no new debt at all, so the answer is `0`.
|
|
294
261
|
*
|
|
295
|
-
*
|
|
296
|
-
* included, which is what the guards hold a simulation to. Opening a position
|
|
297
|
-
* answers to two more — see {@link maxStrategyBorrowAmount}.
|
|
262
|
+
* If the credit manager allows no debt at all, the answer is `0`.
|
|
298
263
|
*/
|
|
299
264
|
maxBorrowAmount() {
|
|
300
265
|
const { pool } = this.market.pool;
|
|
@@ -324,68 +289,25 @@ var CreditSuite = class extends require_onchain_base_SDKConstruct.SDKConstruct {
|
|
|
324
289
|
};
|
|
325
290
|
}
|
|
326
291
|
/**
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
* {@link maxBorrowAmount} held to the two bounds only a position being opened
|
|
331
|
-
* answers to: the remaining quota of the strategy target collateral, which
|
|
332
|
-
* the position has to buy to be worth anything, and the facade's `minDebt`,
|
|
333
|
-
* which a first debt cannot sit under. `amount` is `0` whenever no position
|
|
334
|
-
* can be opened right now, and `limit` names why.
|
|
335
|
-
*
|
|
336
|
-
* An operation on an account that already exists is held to neither: its
|
|
337
|
-
* quota is weighed against the token its own plan buys, and its debt is
|
|
338
|
-
* already over the floor, so a top-up smaller than `minDebt` is legal.
|
|
339
|
-
*/
|
|
340
|
-
maxStrategyBorrowAmount() {
|
|
341
|
-
const lends = this.maxBorrowAmount();
|
|
342
|
-
if (lends.limit === "debtPerBlockLimit") return lends;
|
|
343
|
-
const collateral = this.strategyTargetCollateral;
|
|
344
|
-
let value = lends.amount.value;
|
|
345
|
-
let limit = lends.limit;
|
|
346
|
-
if (collateral !== void 0) {
|
|
347
|
-
const quota = this.market.pool.pqk.quotaAvailable(collateral);
|
|
348
|
-
if (quota < value) {
|
|
349
|
-
value = quota;
|
|
350
|
-
limit = "quotaAvailable";
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
if (value < this.creditFacade.minDebt) return {
|
|
354
|
-
amount: this.market.toUnderlyingAmount(0n),
|
|
355
|
-
limit: "minDebt"
|
|
356
|
-
};
|
|
357
|
-
return {
|
|
358
|
-
amount: this.market.toUnderlyingAmount(value),
|
|
359
|
-
limit
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
/**
|
|
363
|
-
* The single target collateral of this suite's strategy, or `undefined` when
|
|
364
|
-
* none can be resolved.
|
|
292
|
+
* This suite's leveraged strategy, or `undefined` when no target collateral
|
|
293
|
+
* can be resolved.
|
|
365
294
|
*
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
* {@link isStrategyCollateral} accepts with quota required;
|
|
373
|
-
* 3. the biggest-index collateral that {@link isStrategyCollateral} accepts
|
|
374
|
-
* without quota.
|
|
375
|
-
*/
|
|
376
|
-
get strategyTargetCollateral() {
|
|
295
|
+
* The strategy exists whether or not it is offered today, because its
|
|
296
|
+
* target also names existing positions; see
|
|
297
|
+
* {@link CreditSuiteStrategy.isListed} for whether it is listed as an
|
|
298
|
+
* opportunity.
|
|
299
|
+
*/
|
|
300
|
+
get strategy() {
|
|
377
301
|
const legacy = require_onchain_chain_chains.getLegacyStrategyTarget(this.creditManager.address, this.chainId);
|
|
378
|
-
|
|
379
|
-
return
|
|
302
|
+
const target = legacy && this.creditManager.liquidationThresholds.has(legacy) ? legacy : require_onchain_market_credit_collateralUtils.pickStrategyTargetCollateral(this.creditManager.collateralTokens.map((token) => this.#strategyCollateralProps(token)));
|
|
303
|
+
return target ? new require_onchain_market_credit_CreditSuiteStrategy.CreditSuiteStrategy(this, target) : void 0;
|
|
380
304
|
}
|
|
381
305
|
/**
|
|
382
|
-
*
|
|
383
|
-
*
|
|
306
|
+
* Whether `token` can be this suite's strategy target; see
|
|
307
|
+
* {@link isStrategyCollateral}.
|
|
384
308
|
*/
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
if (!collateral) return;
|
|
388
|
-
return require_onchain_market_strategyName.strategyName(this.tokensMeta.mustGetToken(collateral), this.underlyingToken);
|
|
309
|
+
isStrategyCollateral(token, requireQuota = false) {
|
|
310
|
+
return require_onchain_market_credit_collateralUtils.isStrategyCollateral(this.#strategyCollateralProps(token), requireQuota);
|
|
389
311
|
}
|
|
390
312
|
/**
|
|
391
313
|
* Collateral token an existing credit account in this suite is a strategy
|
|
@@ -393,12 +315,13 @@ var CreditSuite = class extends require_onchain_base_SDKConstruct.SDKConstruct {
|
|
|
393
315
|
*
|
|
394
316
|
* Resolution, in order:
|
|
395
317
|
* 1. a hardcoded per-account override, when present;
|
|
396
|
-
* 2. {@link
|
|
318
|
+
* 2. the target of {@link strategy};
|
|
397
319
|
* 3. `null` when neither can be resolved.
|
|
398
320
|
*/
|
|
399
321
|
accountTargetCollateral(creditAccount) {
|
|
400
|
-
const
|
|
401
|
-
|
|
322
|
+
const override = require_onchain_chain_chains.getAccountTargetCollateral(creditAccount, this.chainId);
|
|
323
|
+
if (override) return this.tokensMeta.mustGetToken(override);
|
|
324
|
+
return this.strategy?.token ?? null;
|
|
402
325
|
}
|
|
403
326
|
/**
|
|
404
327
|
* Display name of an existing credit account in this suite, e.g.
|
|
@@ -412,61 +335,6 @@ var CreditSuite = class extends require_onchain_base_SDKConstruct.SDKConstruct {
|
|
|
412
335
|
return target ? require_onchain_market_strategyName.strategyName(target, this.underlyingToken) : this.underlyingToken.symbol;
|
|
413
336
|
}
|
|
414
337
|
/**
|
|
415
|
-
* Describes this suite's leveraged strategy as the shared read model does,
|
|
416
|
-
* or `undefined` when credit suite does not offer a strategy opportunity.
|
|
417
|
-
*/
|
|
418
|
-
strategyOpportunity() {
|
|
419
|
-
const maxBorrowAmount = this.maxStrategyBorrowAmount().amount.value;
|
|
420
|
-
if (maxBorrowAmount <= MIN_STRATEGY_BORROW_AMOUNT) return;
|
|
421
|
-
const collateral = this.strategyTargetCollateral;
|
|
422
|
-
if (!collateral) return;
|
|
423
|
-
if (!require_onchain_market_credit_collateralUtils.isStrategyCollateral(this.#strategyCollateralProps(collateral), true)) return;
|
|
424
|
-
const { market, creditManager: cm } = this;
|
|
425
|
-
const { pool } = market.pool;
|
|
426
|
-
const oracle = market.priceOracle;
|
|
427
|
-
const liquidationThreshold = cm.liquidationThresholds.mustGet(collateral);
|
|
428
|
-
const maxLeverage = cm.maxLeverage(collateral);
|
|
429
|
-
const debtParams = pool.creditManagerDebtParams.get(cm.address);
|
|
430
|
-
const borrowed = debtParams?.borrowed ?? 0n;
|
|
431
|
-
return {
|
|
432
|
-
kind: "strategy",
|
|
433
|
-
chainId: this.chainId,
|
|
434
|
-
creditManager: cm.address,
|
|
435
|
-
targetCollateral: this.tokensMeta.mustGetToken(collateral),
|
|
436
|
-
name: this.strategyName ?? this.underlyingToken.symbol,
|
|
437
|
-
curator: market.curator,
|
|
438
|
-
underlyingToken: this.underlyingToken,
|
|
439
|
-
totalBorrowed: oracle.toAmount(pool.underlying, borrowed),
|
|
440
|
-
allowedDepositTokens: this.allowedDepositTokens(collateral),
|
|
441
|
-
paused: this.isPaused,
|
|
442
|
-
rwa: market.rwa,
|
|
443
|
-
sunset: market.sunset || require_onchain_chain_chains.isSunsetStrategy(cm.address, this.sdk.networkType),
|
|
444
|
-
liquidationThreshold,
|
|
445
|
-
liquidationPremium: cm.liquidationPremium,
|
|
446
|
-
liquidationFee: cm.feeLiquidation,
|
|
447
|
-
expirationDate: this.expirationDate,
|
|
448
|
-
borrowApy: require_onchain_market_math.calcBorrowApy(pool.baseInterestRate, cm.feeInterest),
|
|
449
|
-
quotaRate: require_onchain_market_math.calcQuotaRate(market.pool.pqk.quotaRate(collateral), cm.feeInterest),
|
|
450
|
-
availableLiquidity: oracle.toAmount(pool.underlying, pool.availableLiquidity),
|
|
451
|
-
minDebt: oracle.toAmount(pool.underlying, this.creditFacade.minDebt),
|
|
452
|
-
totalDebtLimit: oracle.toAmount(pool.underlying, debtParams?.limit ?? 0n),
|
|
453
|
-
maxBorrowAmount: oracle.toAmount(pool.underlying, maxBorrowAmount),
|
|
454
|
-
maxLeverage
|
|
455
|
-
};
|
|
456
|
-
}
|
|
457
|
-
/**
|
|
458
|
-
* {@link strategyOpportunity} plus the data only its detail screen needs.
|
|
459
|
-
*/
|
|
460
|
-
strategyOpportunityDetail() {
|
|
461
|
-
const opportunity = this.strategyOpportunity();
|
|
462
|
-
if (!opportunity) return;
|
|
463
|
-
return {
|
|
464
|
-
...opportunity,
|
|
465
|
-
rateCurve: this.market.pool.rateCurve,
|
|
466
|
-
priceFeeds: this.market.priceFeedSummary(opportunity.targetCollateral.address)
|
|
467
|
-
};
|
|
468
|
-
}
|
|
469
|
-
/**
|
|
470
338
|
* Everything a partial liquidation of credit account needs, with any parameter the
|
|
471
339
|
* caller pinned down taken as given and the rest derived from current state.
|
|
472
340
|
*
|
|
@@ -506,27 +374,6 @@ var CreditSuite = class extends require_onchain_base_SDKConstruct.SDKConstruct {
|
|
|
506
374
|
return collateral;
|
|
507
375
|
}
|
|
508
376
|
/**
|
|
509
|
-
* Tokens a user can transfer from their wallet when opening an account in
|
|
510
|
-
* this suite:
|
|
511
|
-
*
|
|
512
|
-
* 1. unwrapped underlying (USDC, never dcUSDC)
|
|
513
|
-
* 2. target collateral
|
|
514
|
-
* 3. remaining CM collaterals in manager order, excluding phantom tokens
|
|
515
|
-
* and tokens without price
|
|
516
|
-
*/
|
|
517
|
-
allowedDepositTokens(targetCollateral) {
|
|
518
|
-
const unwrappedUnderlying = this.market.unwrappedUnderlying;
|
|
519
|
-
const { mainPrices, reservePrices } = this.market.priceOracle;
|
|
520
|
-
return [
|
|
521
|
-
unwrappedUnderlying,
|
|
522
|
-
targetCollateral,
|
|
523
|
-
...this.creditManager.collateralTokens.filter((token) => {
|
|
524
|
-
const contractType = this.tokensMeta.mustGet(token).contractType;
|
|
525
|
-
return !this.market.isUnderlyingLike(token) && !(0, viem.isAddressEqual)(token, targetCollateral) && !contractType?.startsWith("PHANTOM_TOKEN::") && (!!mainPrices.get(token)?.price || !!reservePrices.get(token)?.price);
|
|
526
|
-
})
|
|
527
|
-
].map((token) => this.tokensMeta.mustGetToken(token));
|
|
528
|
-
}
|
|
529
|
-
/**
|
|
530
377
|
* Shared inputs of {@link isStrategyCollateral} for one of this suite's
|
|
531
378
|
* collateral tokens.
|
|
532
379
|
*/
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_onchain_chain_chains = require("../../chain/chains.js");
|
|
3
|
+
const require_onchain_market_math = require("../math.js");
|
|
4
|
+
const require_onchain_market_strategyName = require("../strategyName.js");
|
|
5
|
+
let viem = require("viem");
|
|
6
|
+
//#region src/onchain/market/credit/CreditSuiteStrategy.ts
|
|
7
|
+
/**
|
|
8
|
+
* Amount of underlying seeded into each pool at market creation to protect
|
|
9
|
+
* from inflation attacks, in raw token units. A strategy whose
|
|
10
|
+
* {@link CreditSuiteStrategy.maxBorrowAmount} is at or below this is treated
|
|
11
|
+
* as having nothing left to lend.
|
|
12
|
+
**/
|
|
13
|
+
const MIN_STRATEGY_BORROW_AMOUNT = 100000n;
|
|
14
|
+
/**
|
|
15
|
+
* The leveraged strategy a credit suite runs: one target collateral bought
|
|
16
|
+
* with underlying borrowed from the pool.
|
|
17
|
+
*
|
|
18
|
+
* A view over live suite state, see {@link CreditSuite.strategy}.
|
|
19
|
+
*/
|
|
20
|
+
var CreditSuiteStrategy = class {
|
|
21
|
+
/**
|
|
22
|
+
* Credit suite this strategy borrows through.
|
|
23
|
+
*/
|
|
24
|
+
suite;
|
|
25
|
+
/**
|
|
26
|
+
* Collateral token a position in this strategy is built to hold.
|
|
27
|
+
*/
|
|
28
|
+
targetCollateral;
|
|
29
|
+
constructor(suite, targetCollateral) {
|
|
30
|
+
this.suite = suite;
|
|
31
|
+
this.targetCollateral = targetCollateral;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* {@link targetCollateral} as the shared read model describes it.
|
|
35
|
+
*/
|
|
36
|
+
get token() {
|
|
37
|
+
return this.suite.sdk.tokensMeta.mustGetToken(this.targetCollateral);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Display name of this strategy, e.g. `"wstETH / WETH"`.
|
|
41
|
+
*/
|
|
42
|
+
get name() {
|
|
43
|
+
return require_onchain_market_strategyName.strategyName(this.token, this.suite.underlyingToken);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Tokens a user can transfer from their wallet when opening a position in
|
|
47
|
+
* this strategy:
|
|
48
|
+
*
|
|
49
|
+
* 1. unwrapped underlying (USDC, never dcUSDC)
|
|
50
|
+
* 2. target collateral
|
|
51
|
+
* 3. remaining CM collaterals in manager order, excluding phantom tokens
|
|
52
|
+
* and tokens without price
|
|
53
|
+
*/
|
|
54
|
+
get allowedDepositTokens() {
|
|
55
|
+
const { market, creditManager, sdk } = this.suite;
|
|
56
|
+
const { tokensMeta } = sdk;
|
|
57
|
+
const { targetCollateral } = this;
|
|
58
|
+
const { mainPrices, reservePrices } = market.priceOracle;
|
|
59
|
+
const rest = creditManager.collateralTokens.filter((token) => {
|
|
60
|
+
const contractType = tokensMeta.mustGet(token).contractType;
|
|
61
|
+
return !market.isUnderlyingLike(token) && !(0, viem.isAddressEqual)(token, targetCollateral) && !contractType?.startsWith("PHANTOM_TOKEN::") && (!!mainPrices.get(token)?.price || !!reservePrices.get(token)?.price);
|
|
62
|
+
});
|
|
63
|
+
return [
|
|
64
|
+
market.unwrappedUnderlying,
|
|
65
|
+
targetCollateral,
|
|
66
|
+
...rest
|
|
67
|
+
].map((token) => tokensMeta.mustGetToken(token));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Largest debt one new position can take right now, and which limit set
|
|
71
|
+
* that number.
|
|
72
|
+
*
|
|
73
|
+
* `amount` is `0` whenever no position can be opened right now,
|
|
74
|
+
* and `limit` explains why.
|
|
75
|
+
*
|
|
76
|
+
*/
|
|
77
|
+
maxBorrowAmount() {
|
|
78
|
+
const { suite } = this;
|
|
79
|
+
const lends = suite.maxBorrowAmount();
|
|
80
|
+
if (lends.limit === "debtPerBlockLimit") return lends;
|
|
81
|
+
let value = lends.amount.value;
|
|
82
|
+
let limit = lends.limit;
|
|
83
|
+
const quota = suite.market.pool.pqk.quotaAvailable(this.targetCollateral);
|
|
84
|
+
if (quota < value) {
|
|
85
|
+
value = quota;
|
|
86
|
+
limit = "quotaAvailable";
|
|
87
|
+
}
|
|
88
|
+
if (value < suite.creditFacade.minDebt) return {
|
|
89
|
+
amount: suite.market.toUnderlyingAmount(0n),
|
|
90
|
+
limit: "minDebt"
|
|
91
|
+
};
|
|
92
|
+
return {
|
|
93
|
+
amount: suite.market.toUnderlyingAmount(value),
|
|
94
|
+
limit
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Whether this strategy is offered as an opportunity right now: it lends
|
|
99
|
+
* more than the pool's seed amount, and its target can be opened today.
|
|
100
|
+
*/
|
|
101
|
+
get isListed() {
|
|
102
|
+
return this.maxBorrowAmount().amount.value > MIN_STRATEGY_BORROW_AMOUNT && this.suite.isStrategyCollateral(this.targetCollateral, true);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Describes this strategy as the shared read model does. Whether it is
|
|
106
|
+
* listed at all is {@link isListed}.
|
|
107
|
+
*/
|
|
108
|
+
opportunity() {
|
|
109
|
+
const { suite, targetCollateral } = this;
|
|
110
|
+
const { market, creditManager: cm } = suite;
|
|
111
|
+
const { pool } = market.pool;
|
|
112
|
+
const oracle = market.priceOracle;
|
|
113
|
+
const debtParams = pool.creditManagerDebtParams.get(cm.address);
|
|
114
|
+
const borrowed = debtParams?.borrowed ?? 0n;
|
|
115
|
+
return {
|
|
116
|
+
kind: "strategy",
|
|
117
|
+
chainId: suite.chainId,
|
|
118
|
+
creditManager: cm.address,
|
|
119
|
+
targetCollateral: this.token,
|
|
120
|
+
name: this.name,
|
|
121
|
+
curator: market.curator,
|
|
122
|
+
underlyingToken: suite.underlyingToken,
|
|
123
|
+
totalBorrowed: oracle.toAmount(pool.underlying, borrowed),
|
|
124
|
+
allowedDepositTokens: this.allowedDepositTokens,
|
|
125
|
+
paused: suite.isPaused,
|
|
126
|
+
rwa: market.rwa,
|
|
127
|
+
sunset: market.sunset || require_onchain_chain_chains.isSunsetStrategy(cm.address, suite.sdk.networkType),
|
|
128
|
+
liquidationThreshold: cm.liquidationThresholds.mustGet(targetCollateral),
|
|
129
|
+
liquidationPremium: cm.liquidationPremium,
|
|
130
|
+
liquidationFee: cm.feeLiquidation,
|
|
131
|
+
expirationDate: suite.expirationDate,
|
|
132
|
+
borrowApy: require_onchain_market_math.calcBorrowApy(pool.baseInterestRate, cm.feeInterest),
|
|
133
|
+
quotaRate: require_onchain_market_math.calcQuotaRate(market.pool.pqk.quotaRate(targetCollateral), cm.feeInterest),
|
|
134
|
+
availableLiquidity: oracle.toAmount(pool.underlying, pool.availableLiquidity),
|
|
135
|
+
minDebt: oracle.toAmount(pool.underlying, suite.creditFacade.minDebt),
|
|
136
|
+
totalDebtLimit: oracle.toAmount(pool.underlying, debtParams?.limit ?? 0n),
|
|
137
|
+
maxBorrowAmount: oracle.toAmount(pool.underlying, this.maxBorrowAmount().amount.value),
|
|
138
|
+
maxLeverage: cm.maxLeverage(targetCollateral)
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* {@link opportunity} plus the data only its detail screen needs.
|
|
143
|
+
*/
|
|
144
|
+
opportunityDetail() {
|
|
145
|
+
const { market } = this.suite;
|
|
146
|
+
return {
|
|
147
|
+
...this.opportunity(),
|
|
148
|
+
rateCurve: market.pool.rateCurve,
|
|
149
|
+
priceFeeds: market.priceFeedSummary(this.targetCollateral)
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* The KYC gate of this strategy; `null` when there is none.
|
|
154
|
+
* Wallet-independent.
|
|
155
|
+
*/
|
|
156
|
+
async kycRequirement() {
|
|
157
|
+
const nft = await this.suite.degenNFT();
|
|
158
|
+
if (!nft) return null;
|
|
159
|
+
const tokens = await nft.getTokens();
|
|
160
|
+
const token = tokens.find((t) => (0, viem.isAddressEqual)(t, this.targetCollateral)) ?? tokens[0];
|
|
161
|
+
return {
|
|
162
|
+
protocol: nft.protocol,
|
|
163
|
+
token: token ? this.suite.sdk.tokensMeta.getToken(token) : void 0,
|
|
164
|
+
registrationLink: nft.registrationLink
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Whether `wallet` may open this strategy today; `true` when there is no
|
|
169
|
+
* KYC gate.
|
|
170
|
+
*/
|
|
171
|
+
async isEligible(wallet) {
|
|
172
|
+
const nft = await this.suite.degenNFT();
|
|
173
|
+
if (!nft) return true;
|
|
174
|
+
const requirements = await nft.getOpenAccountRequirements(wallet, { tokenOutAddress: this.targetCollateral });
|
|
175
|
+
return nft.isRegistered(requirements);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
//#endregion
|
|
179
|
+
exports.CreditSuiteStrategy = CreditSuiteStrategy;
|
|
@@ -3,6 +3,7 @@ const require_onchain_market_credit_CreditConfiguratorV310Contract = require("./
|
|
|
3
3
|
const require_onchain_market_credit_CreditFacadeV310BaseContract = require("./CreditFacadeV310BaseContract.js");
|
|
4
4
|
const require_onchain_market_credit_CreditFacadeV310Contract = require("./CreditFacadeV310Contract.js");
|
|
5
5
|
const require_onchain_market_credit_CreditManagerV310Contract = require("./CreditManagerV310Contract.js");
|
|
6
|
+
const require_onchain_market_credit_CreditSuiteStrategy = require("./CreditSuiteStrategy.js");
|
|
6
7
|
const require_onchain_market_credit_collateralUtils = require("./collateralUtils.js");
|
|
7
8
|
const require_onchain_market_credit_CreditSuite = require("./CreditSuite.js");
|
|
8
9
|
const require_onchain_market_credit_expectedBalanceDeltas = require("./expectedBalanceDeltas.js");
|
|
@@ -12,6 +13,7 @@ exports.CreditFacadeV310BaseContract = require_onchain_market_credit_CreditFacad
|
|
|
12
13
|
exports.CreditFacadeV310Contract = require_onchain_market_credit_CreditFacadeV310Contract.CreditFacadeV310Contract;
|
|
13
14
|
exports.CreditManagerV310Contract = require_onchain_market_credit_CreditManagerV310Contract.CreditManagerV310Contract;
|
|
14
15
|
exports.CreditSuite = require_onchain_market_credit_CreditSuite.CreditSuite;
|
|
16
|
+
exports.CreditSuiteStrategy = require_onchain_market_credit_CreditSuiteStrategy.CreditSuiteStrategy;
|
|
15
17
|
exports.creditFacadeV310Abi = require_onchain_market_credit_CreditFacadeV310BaseContract.creditFacadeV310Abi;
|
|
16
18
|
exports.dominantCollateral = require_onchain_market_credit_collateralUtils.dominantCollateral;
|
|
17
19
|
exports.expectedBalanceDeltas = require_onchain_market_credit_expectedBalanceDeltas.expectedBalanceDeltas;
|