@gearbox-protocol/sdk 17.2.0 → 17.3.0

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.
Files changed (65) hide show
  1. package/dist/cjs/onchain/accounts/intents/leverage-band.js +1 -1
  2. package/dist/cjs/onchain/accounts/intents/maxBorrow.js +15 -11
  3. package/dist/cjs/onchain/accounts/intents/testing/sdk-mock.js +5 -2
  4. package/dist/cjs/onchain/chain/chains.js +1 -2
  5. package/dist/cjs/onchain/index.js +2 -0
  6. package/dist/cjs/onchain/market/MarketSuite.js +11 -4
  7. package/dist/cjs/onchain/market/credit/CreditSuite.js +25 -138
  8. package/dist/cjs/onchain/market/credit/CreditSuiteStrategy.js +179 -0
  9. package/dist/cjs/onchain/market/credit/index.js +2 -0
  10. package/dist/cjs/onchain/market/index.js +2 -0
  11. package/dist/cjs/onchain/market/pool/PoolQuotaKeeperV310Contract.js +4 -1
  12. package/dist/cjs/onchain/opportunities/OpportunitiesService.js +7 -9
  13. package/dist/cjs/rewards/{multichain.js → RewardsService.js} +18 -30
  14. package/dist/cjs/rewards/errors.js +4 -4
  15. package/dist/cjs/rewards/helpers.js +88 -0
  16. package/dist/cjs/rewards/index.js +2 -2
  17. package/dist/cjs/rewards/merkl-api.js +14 -25
  18. package/dist/cjs/rewards/toMerklRewards.js +21 -77
  19. package/dist/cjs/rewards/toTurtleRewards.js +46 -62
  20. package/dist/cjs/rewards/turtle-api.js +17 -0
  21. package/dist/cjs/rewards/types.js +1 -0
  22. package/dist/cjs/sdk/prepare/PrepareApi.js +1 -1
  23. package/dist/esm/onchain/accounts/intents/leverage-band.js +1 -1
  24. package/dist/esm/onchain/accounts/intents/maxBorrow.js +15 -11
  25. package/dist/esm/onchain/accounts/intents/testing/sdk-mock.js +5 -2
  26. package/dist/esm/onchain/chain/chains.js +1 -2
  27. package/dist/esm/onchain/index.js +2 -1
  28. package/dist/esm/onchain/market/MarketSuite.js +11 -4
  29. package/dist/esm/onchain/market/credit/CreditSuite.js +27 -140
  30. package/dist/esm/onchain/market/credit/CreditSuiteStrategy.js +178 -0
  31. package/dist/esm/onchain/market/credit/index.js +2 -1
  32. package/dist/esm/onchain/market/index.js +2 -1
  33. package/dist/esm/onchain/market/pool/PoolQuotaKeeperV310Contract.js +4 -1
  34. package/dist/esm/onchain/opportunities/OpportunitiesService.js +7 -9
  35. package/dist/esm/rewards/{multichain.js → RewardsService.js} +19 -31
  36. package/dist/esm/rewards/errors.js +4 -4
  37. package/dist/esm/rewards/helpers.js +83 -0
  38. package/dist/esm/rewards/index.js +2 -2
  39. package/dist/esm/rewards/merkl-api.js +15 -25
  40. package/dist/esm/rewards/toMerklRewards.js +23 -78
  41. package/dist/esm/rewards/toTurtleRewards.js +46 -62
  42. package/dist/esm/rewards/turtle-api.js +17 -1
  43. package/dist/esm/rewards/types.js +1 -0
  44. package/dist/esm/sdk/prepare/PrepareApi.js +1 -1
  45. package/dist/types/model/errors/operation-errors.d.ts +6 -1
  46. package/dist/types/onchain/accounts/intents/maxBorrow.d.ts +12 -10
  47. package/dist/types/onchain/index.d.ts +2 -1
  48. package/dist/types/onchain/market/MarketSuite.d.ts +6 -0
  49. package/dist/types/onchain/market/credit/CreditSuite.d.ts +18 -52
  50. package/dist/types/onchain/market/credit/CreditSuiteStrategy.d.ts +78 -0
  51. package/dist/types/onchain/market/credit/index.d.ts +2 -1
  52. package/dist/types/onchain/market/credit/types.d.ts +3 -1
  53. package/dist/types/onchain/market/index.d.ts +2 -1
  54. package/dist/types/onchain/market/pool/PoolQuotaKeeperV310Contract.d.ts +2 -0
  55. package/dist/types/rewards/RewardsService.d.ts +29 -0
  56. package/dist/types/rewards/errors.d.ts +2 -2
  57. package/dist/types/rewards/helpers.d.ts +44 -0
  58. package/dist/types/rewards/index.d.ts +3 -3
  59. package/dist/types/rewards/merkl-api.d.ts +5 -10
  60. package/dist/types/rewards/toMerklRewards.d.ts +5 -48
  61. package/dist/types/rewards/toTurtleRewards.d.ts +3 -3
  62. package/dist/types/rewards/turtle-api.d.ts +5 -1
  63. package/dist/types/rewards/types.d.ts +41 -0
  64. package/package.json +1 -1
  65. package/dist/types/rewards/multichain.d.ts +0 -28
@@ -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.strategyTargetCollateral;
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,17 +20,19 @@ 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: the pool's free
24
- * liquidity, the manager's own allowance and the facade's `maxDebt`, whichever
25
- * binds first.
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
- * The facade's `minDebt` is deliberately not applied. It is a floor, and a
28
- * ceiling answered as `0n` because the collateral is too small for this market
29
- * would tell a form nothing about what it is holding — the number a user needs
30
- * to see is the one they are short of. Collateral that carries something
31
- * therefore answers with it, whether or not the market would lend that little;
32
- * a loan under the floor is refused by `borrow` itself, with `debtOutOfRange`
33
- * naming both ends.
27
+ * The facade's `minDebt` is not applied to the collateral's own ceiling. It
28
+ * is a floor, and a ceiling answered as `0n` because the collateral is too
29
+ * small for this market would tell a form nothing about what it is holding —
30
+ * the number a user needs to see is the one they are short of. Collateral
31
+ * that carries something therefore answers with it, whether or not the market
32
+ * would lend that little; a loan under the floor is refused by `borrow`
33
+ * itself, with `debtOutOfRange` naming both ends. A market whose own capacity
34
+ * is under `minDebt` is different: the answer is `0n`, because no loan of any
35
+ * size exists there.
34
36
  *
35
37
  * Nothing is fetched or simulated — the account does not exist yet and every
36
38
  * input is loaded market state, so a form can call this on each keystroke.
@@ -81,7 +83,9 @@ function maxBorrow(props) {
81
83
  const weighted = valuation.checkedUsd(holding) * valuation.lt(collateralToken);
82
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;
83
85
  if (backed <= 0n) return 0n;
84
- const ceiling = require_onchain_utils_bigint_math.BigIntMath.min(priceOracle.safeConvertFromUSD(underlying, backed / targetHF).value, suite.maxBorrowAmount().amount.value);
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);
85
89
  const unwrapsPayout = !!rwaAsset && require_onchain_accounts_intents_utils_common.eq(borrowToken, rwaAsset);
86
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;
87
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,
@@ -219,7 +222,6 @@ function buildMockSdk(args) {
219
222
  market,
220
223
  isPaused: facadePaused || poolPaused,
221
224
  forbiddenTokens: [...forbidden],
222
- strategyTargetCollateral,
223
225
  isExpired: expirationDate > 0 && expirationDate < (args.timestamp ?? 0)
224
226
  };
225
227
  const routeCalls = (tokenIn, tokenOut) => {
@@ -356,6 +358,7 @@ function buildMockSdk(args) {
356
358
  }
357
359
  };
358
360
  Object.assign(sdk, { positions: new require_onchain_positions_PositionsService.PositionsService(sdk) });
361
+ Object.assign(creditManagerSuite, { sdk });
359
362
  return sdk;
360
363
  }
361
364
  /** A full account payload carrying exactly what the slice builder reads back. */
@@ -97,8 +97,7 @@ const chains = {
97
97
  "0x89014edc549ffa5c5b6e859b1496731bd035c247": "0x31454faa1daa04cacf59a6bd37681da9160d092a",
98
98
  "0x3b7ab1f4fee570933b24b202de90ffda82f6cae0": "0x31454faa1daa04cacf59a6bd37681da9160d092a",
99
99
  "0x721798d8ccf31ae75c12db82fa72b3806759cbc9": "0x31454faa1daa04cacf59a6bd37681da9160d092a",
100
- "0xd7273d9594ac88f993eda9773041e621633acea0": "0x1a711a5bc48b5c1352c1882fa65dc14b5b9e829d",
101
- "0x34442ca47435e90b80d835aab9737166e76d9962": "0x403cc0d2694ec2639101f32b146b90d766461ce9"
100
+ "0xd7273d9594ac88f993eda9773041e621633acea0": "0x1a711a5bc48b5c1352c1882fa65dc14b5b9e829d"
102
101
  }),
103
102
  sunsetPools: new require_onchain_utils_AddressSet.AddressSet([
104
103
  "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") for (const suite of this.creditManagers) {
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.maxBorrowAmount} 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 {@link strategyOpportunity} reads, so
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.strategyName ?? this.underlyingToken.symbol,
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,15 +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 one new position can take from this credit manager right now,
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`.
261
+ *
262
+ * If the credit manager allows no debt at all, the answer is `0`.
294
263
  */
295
264
  maxBorrowAmount() {
296
265
  const { pool } = this.market.pool;
@@ -320,32 +289,25 @@ var CreditSuite = class extends require_onchain_base_SDKConstruct.SDKConstruct {
320
289
  };
321
290
  }
322
291
  /**
323
- * The single target collateral of this suite's strategy, or `undefined` when
324
- * none can be resolved.
292
+ * This suite's leveraged strategy, or `undefined` when no target collateral
293
+ * can be resolved.
325
294
  *
326
- * Resolution, in order:
327
- * 1. a hardcoded legacy mapping for this credit manager, when that token is
328
- * still a collateral of the manager (it may be absent on an older
329
- * snapshot, or after it was delisted);
330
- * 2. the collateral with the biggest index in
331
- * {@link ICreditManagerContract.collateralTokens} that
332
- * {@link isStrategyCollateral} accepts with quota required;
333
- * 3. the biggest-index collateral that {@link isStrategyCollateral} accepts
334
- * without quota.
335
- */
336
- 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() {
337
301
  const legacy = require_onchain_chain_chains.getLegacyStrategyTarget(this.creditManager.address, this.chainId);
338
- if (legacy && this.creditManager.liquidationThresholds.has(legacy)) return legacy;
339
- return require_onchain_market_credit_collateralUtils.pickStrategyTargetCollateral(this.creditManager.collateralTokens.map((token) => this.#strategyCollateralProps(token)));
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;
340
304
  }
341
305
  /**
342
- * Display name of this suite's leveraged strategy, e.g. `"wstETH / WETH"`,
343
- * or `undefined` when {@link strategyTargetCollateral} cannot be resolved.
306
+ * Whether `token` can be this suite's strategy target; see
307
+ * {@link isStrategyCollateral}.
344
308
  */
345
- get strategyName() {
346
- const collateral = this.strategyTargetCollateral;
347
- if (!collateral) return;
348
- 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);
349
311
  }
350
312
  /**
351
313
  * Collateral token an existing credit account in this suite is a strategy
@@ -353,12 +315,13 @@ var CreditSuite = class extends require_onchain_base_SDKConstruct.SDKConstruct {
353
315
  *
354
316
  * Resolution, in order:
355
317
  * 1. a hardcoded per-account override, when present;
356
- * 2. {@link strategyTargetCollateral};
318
+ * 2. the target of {@link strategy};
357
319
  * 3. `null` when neither can be resolved.
358
320
  */
359
321
  accountTargetCollateral(creditAccount) {
360
- const addr = require_onchain_chain_chains.getAccountTargetCollateral(creditAccount, this.chainId) ?? this.strategyTargetCollateral;
361
- return addr ? this.tokensMeta.mustGetToken(addr) : null;
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;
362
325
  }
363
326
  /**
364
327
  * Display name of an existing credit account in this suite, e.g.
@@ -372,61 +335,6 @@ var CreditSuite = class extends require_onchain_base_SDKConstruct.SDKConstruct {
372
335
  return target ? require_onchain_market_strategyName.strategyName(target, this.underlyingToken) : this.underlyingToken.symbol;
373
336
  }
374
337
  /**
375
- * Describes this suite's leveraged strategy as the shared read model does,
376
- * or `undefined` when credit suite does not offer a strategy opportunity.
377
- */
378
- strategyOpportunity() {
379
- const maxBorrowAmount = this.maxBorrowAmount().amount.value;
380
- if (maxBorrowAmount <= MIN_STRATEGY_BORROW_AMOUNT) return;
381
- const collateral = this.strategyTargetCollateral;
382
- if (!collateral) return;
383
- if (!require_onchain_market_credit_collateralUtils.isStrategyCollateral(this.#strategyCollateralProps(collateral), true)) return;
384
- const { market, creditManager: cm } = this;
385
- const { pool } = market.pool;
386
- const oracle = market.priceOracle;
387
- const liquidationThreshold = cm.liquidationThresholds.mustGet(collateral);
388
- const maxLeverage = cm.maxLeverage(collateral);
389
- const debtParams = pool.creditManagerDebtParams.get(cm.address);
390
- const borrowed = debtParams?.borrowed ?? 0n;
391
- return {
392
- kind: "strategy",
393
- chainId: this.chainId,
394
- creditManager: cm.address,
395
- targetCollateral: this.tokensMeta.mustGetToken(collateral),
396
- name: this.strategyName ?? this.underlyingToken.symbol,
397
- curator: market.curator,
398
- underlyingToken: this.underlyingToken,
399
- totalBorrowed: oracle.toAmount(pool.underlying, borrowed),
400
- allowedDepositTokens: this.allowedDepositTokens(collateral),
401
- paused: this.isPaused,
402
- rwa: market.rwa,
403
- sunset: market.sunset || require_onchain_chain_chains.isSunsetStrategy(cm.address, this.sdk.networkType),
404
- liquidationThreshold,
405
- liquidationPremium: cm.liquidationPremium,
406
- liquidationFee: cm.feeLiquidation,
407
- expirationDate: this.expirationDate,
408
- borrowApy: require_onchain_market_math.calcBorrowApy(pool.baseInterestRate, cm.feeInterest),
409
- quotaRate: require_onchain_market_math.calcQuotaRate(market.pool.pqk.quotaRate(collateral), cm.feeInterest),
410
- availableLiquidity: oracle.toAmount(pool.underlying, pool.availableLiquidity),
411
- minDebt: oracle.toAmount(pool.underlying, this.creditFacade.minDebt),
412
- totalDebtLimit: oracle.toAmount(pool.underlying, debtParams?.limit ?? 0n),
413
- maxBorrowAmount: oracle.toAmount(pool.underlying, maxBorrowAmount),
414
- maxLeverage
415
- };
416
- }
417
- /**
418
- * {@link strategyOpportunity} plus the data only its detail screen needs.
419
- */
420
- strategyOpportunityDetail() {
421
- const opportunity = this.strategyOpportunity();
422
- if (!opportunity) return;
423
- return {
424
- ...opportunity,
425
- rateCurve: this.market.pool.rateCurve,
426
- priceFeeds: this.market.priceFeedSummary(opportunity.targetCollateral.address)
427
- };
428
- }
429
- /**
430
338
  * Everything a partial liquidation of credit account needs, with any parameter the
431
339
  * caller pinned down taken as given and the rest derived from current state.
432
340
  *
@@ -466,27 +374,6 @@ var CreditSuite = class extends require_onchain_base_SDKConstruct.SDKConstruct {
466
374
  return collateral;
467
375
  }
468
376
  /**
469
- * Tokens a user can transfer from their wallet when opening an account in
470
- * this suite:
471
- *
472
- * 1. unwrapped underlying (USDC, never dcUSDC)
473
- * 2. target collateral
474
- * 3. remaining CM collaterals in manager order, excluding phantom tokens
475
- * and tokens without price
476
- */
477
- allowedDepositTokens(targetCollateral) {
478
- const unwrappedUnderlying = this.market.unwrappedUnderlying;
479
- const { mainPrices, reservePrices } = this.market.priceOracle;
480
- return [
481
- unwrappedUnderlying,
482
- targetCollateral,
483
- ...this.creditManager.collateralTokens.filter((token) => {
484
- const contractType = this.tokensMeta.mustGet(token).contractType;
485
- return !this.market.isUnderlyingLike(token) && !(0, viem.isAddressEqual)(token, targetCollateral) && !contractType?.startsWith("PHANTOM_TOKEN::") && (!!mainPrices.get(token)?.price || !!reservePrices.get(token)?.price);
486
- })
487
- ].map((token) => this.tokensMeta.mustGetToken(token));
488
- }
489
- /**
490
377
  * Shared inputs of {@link isStrategyCollateral} for one of this suite's
491
378
  * collateral tokens.
492
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;
@@ -94,6 +94,7 @@ const require_onchain_market_rwa_midas_constants = require("./rwa/midas/constant
94
94
  const require_onchain_market_rwa_midas_MidasDegenNFT = require("./rwa/midas/MidasDegenNFT.js");
95
95
  const require_onchain_market_rwa_securitize_constants = require("./rwa/securitize/constants.js");
96
96
  const require_onchain_market_strategyName = require("./strategyName.js");
97
+ const require_onchain_market_credit_CreditSuiteStrategy = require("./credit/CreditSuiteStrategy.js");
97
98
  const require_onchain_market_credit_collateralUtils = require("./credit/collateralUtils.js");
98
99
  const require_onchain_market_credit_CreditSuite = require("./credit/CreditSuite.js");
99
100
  const require_onchain_market_credit_expectedBalanceDeltas = require("./credit/expectedBalanceDeltas.js");
@@ -170,6 +171,7 @@ exports.CreditFacadeV310BaseContract = require_onchain_market_credit_CreditFacad
170
171
  exports.CreditFacadeV310Contract = require_onchain_market_credit_CreditFacadeV310Contract.CreditFacadeV310Contract;
171
172
  exports.CreditManagerV310Contract = require_onchain_market_credit_CreditManagerV310Contract.CreditManagerV310Contract;
172
173
  exports.CreditSuite = require_onchain_market_credit_CreditSuite.CreditSuite;
174
+ exports.CreditSuiteStrategy = require_onchain_market_credit_CreditSuiteStrategy.CreditSuiteStrategy;
173
175
  exports.Curve2AssetsAdapterContract = require_onchain_market_adapters_contracts_Curve2AssetsAdapterContract.Curve2AssetsAdapterContract;
174
176
  exports.Curve3AssetsAdapterContract = require_onchain_market_adapters_contracts_Curve3AssetsAdapterContract.Curve3AssetsAdapterContract;
175
177
  exports.Curve4AssetsAdapterContract = require_onchain_market_adapters_contracts_Curve4AssetsAdapterContract.Curve4AssetsAdapterContract;
@@ -1,6 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_abi_310_generated = require("../../../abi/310/generated.js");
3
3
  const require_onchain_utils_AddressMap = require("../../utils/AddressMap.js");
4
+ const require_onchain_utils_bigint_math = require("../../utils/bigint-math.js");
4
5
  const require_onchain_utils_formatter = require("../../utils/formatter.js");
5
6
  require("../../utils/index.js");
6
7
  const require_onchain_base_BaseContract = require("../../base/BaseContract.js");
@@ -41,10 +42,12 @@ var PoolQuotaKeeperV310Contract = class extends require_onchain_base_BaseContrac
41
42
  /**
42
43
  * How much more quota the market will take for a token, in the underlying.
43
44
  * `0n` when the market has no quota entry; not the same as {@link hasActiveQuota}.
45
+ * Never negative: a limit lowered under what is already quoted leaves no
46
+ * room, not a debt.
44
47
  */
45
48
  quotaAvailable(token) {
46
49
  const quota = this.quotas.get(token);
47
- return quota ? quota.limit - quota.totalQuoted : 0n;
50
+ return quota ? require_onchain_utils_bigint_math.BigIntMath.max(0n, quota.limit - quota.totalQuoted) : 0n;
48
51
  }
49
52
  /**
50
53
  * Annual quota rate paid on a quoted token, in basis points, or `0` when the
@@ -43,12 +43,11 @@ var OpportunitiesService = class extends require_onchain_base_SDKConstruct.SDKCo
43
43
  * strategy.
44
44
  **/
45
45
  async getStrategy(key) {
46
- const suite = this.sdk.marketRegister.findCreditManager(key.creditManager);
47
- const detail = suite.strategyOpportunityDetail();
48
- if (!detail) throw new Error(`credit manager ${key.creditManager} does not currently offer a strategy`);
49
- const kyc = await suite.kycRequirement(detail.targetCollateral.address);
46
+ const strategy = this.sdk.marketRegister.findCreditManager(key.creditManager).strategy;
47
+ if (!strategy?.isListed) throw new Error(`credit manager ${key.creditManager} does not currently offer a strategy`);
48
+ const kyc = await strategy.kycRequirement();
50
49
  return {
51
- ...detail,
50
+ ...strategy.opportunityDetail(),
52
51
  kyc
53
52
  };
54
53
  }
@@ -60,10 +59,9 @@ var OpportunitiesService = class extends require_onchain_base_SDKConstruct.SDKCo
60
59
  * strategy.
61
60
  **/
62
61
  async isEligibleForStrategy(key, wallet) {
63
- const suite = this.sdk.marketRegister.findCreditManager(key.creditManager);
64
- const opportunity = suite.strategyOpportunity();
65
- if (!opportunity) throw new Error(`credit manager ${key.creditManager} does not currently offer a strategy`);
66
- return suite.isEligibleForStrategy(wallet, opportunity.targetCollateral.address);
62
+ const strategy = this.sdk.marketRegister.findCreditManager(key.creditManager).strategy;
63
+ if (!strategy?.isListed) throw new Error(`credit manager ${key.creditManager} does not currently offer a strategy`);
64
+ return strategy.isEligible(wallet);
67
65
  }
68
66
  };
69
67
  //#endregion