@gearbox-protocol/sdk 17.2.1 → 17.3.1
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/dev/index.js +1 -0
- package/dist/cjs/dev/isRateLimitError.js +20 -5
- package/dist/cjs/dev/resilientTransport.js +31 -8
- package/dist/cjs/onchain/accounts/intents/leverage-band.js +1 -1
- package/dist/cjs/onchain/accounts/intents/maxBorrow.js +15 -11
- package/dist/cjs/onchain/accounts/intents/testing/sdk-mock.js +5 -2
- 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 +25 -138
- 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/market/pool/PoolQuotaKeeperV310Contract.js +4 -1
- package/dist/cjs/onchain/opportunities/OpportunitiesService.js +7 -9
- package/dist/cjs/sdk/prepare/PrepareApi.js +1 -1
- package/dist/esm/dev/index.js +2 -2
- package/dist/esm/dev/isRateLimitError.js +20 -5
- package/dist/esm/dev/resilientTransport.js +31 -9
- package/dist/esm/onchain/accounts/intents/leverage-band.js +1 -1
- package/dist/esm/onchain/accounts/intents/maxBorrow.js +15 -11
- package/dist/esm/onchain/accounts/intents/testing/sdk-mock.js +5 -2
- 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 +27 -140
- 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/market/pool/PoolQuotaKeeperV310Contract.js +4 -1
- package/dist/esm/onchain/opportunities/OpportunitiesService.js +7 -9
- package/dist/esm/sdk/prepare/PrepareApi.js +1 -1
- package/dist/types/dev/index.d.ts +2 -2
- package/dist/types/dev/isRateLimitError.d.ts +2 -1
- package/dist/types/dev/resilientTransport.d.ts +18 -2
- package/dist/types/model/errors/operation-errors.d.ts +6 -1
- package/dist/types/onchain/accounts/intents/maxBorrow.d.ts +12 -10
- 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 +18 -52
- 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/dist/types/onchain/market/pool/PoolQuotaKeeperV310Contract.d.ts +2 -0
- package/package.json +1 -1
package/dist/cjs/dev/index.js
CHANGED
|
@@ -78,6 +78,7 @@ exports.logSplitterTransport = require_dev_logSplitterTransport.logSplitterTrans
|
|
|
78
78
|
exports.makePendingWithdrawalsClaimable = require_dev_withdrawalUtils.makePendingWithdrawalsClaimable;
|
|
79
79
|
exports.migrateFaucet = require_dev_migrateFaucet.migrateFaucet;
|
|
80
80
|
exports.providerConfigSchema = require_dev_RevolverTransport.providerConfigSchema;
|
|
81
|
+
exports.rateLimitOptionsSchema = require_dev_resilientTransport.rateLimitOptionsSchema;
|
|
81
82
|
exports.registerMidasInvestor = require_dev_kycUtils.registerMidasInvestor;
|
|
82
83
|
exports.registerRWAInvestor = require_dev_kycUtils.registerRWAInvestor;
|
|
83
84
|
exports.registerSecuritizeInvestor = require_dev_kycUtils.registerSecuritizeInvestor;
|
|
@@ -7,6 +7,16 @@ const RATE_LIMIT_PATTERNS = [
|
|
|
7
7
|
/exceeded its compute units/i,
|
|
8
8
|
/retry.{0,10}(in|after)\s+\d/i
|
|
9
9
|
];
|
|
10
|
+
const RETRY_HINT_RE = /retry.{0,10}(?:in|after)\s+(\d+(?:\.\d+)?)\s*(ms|milliseconds?|s|secs?|seconds?|m|mins?|minutes?)?\b/i;
|
|
11
|
+
function extractRetryHint(msg) {
|
|
12
|
+
const match = msg.match(RETRY_HINT_RE);
|
|
13
|
+
if (!match) return;
|
|
14
|
+
const value = Number(match[1]);
|
|
15
|
+
const unit = match[2]?.toLowerCase() ?? "s";
|
|
16
|
+
if (unit.startsWith("ms") || unit.startsWith("milli")) return value;
|
|
17
|
+
if (unit.startsWith("m")) return value * 6e4;
|
|
18
|
+
return value * 1e3;
|
|
19
|
+
}
|
|
10
20
|
function extractRetryAfter(headers) {
|
|
11
21
|
if (!headers) return;
|
|
12
22
|
const val = headers.get("retry-after");
|
|
@@ -17,24 +27,29 @@ function extractRetryAfter(headers) {
|
|
|
17
27
|
if (!Number.isNaN(date)) return Math.max(0, date - Date.now());
|
|
18
28
|
}
|
|
19
29
|
/**
|
|
20
|
-
* Whether the error indicates rate limiting. Returns `retryAfterMs` from the `Retry-After` header when available
|
|
30
|
+
* Whether the error indicates rate limiting. Returns `retryAfterMs` from the `Retry-After` header when available,
|
|
31
|
+
* otherwise from a retry hint in the error message (e.g. "retry in 10s").
|
|
21
32
|
*/
|
|
22
33
|
function isRateLimitError(e) {
|
|
23
34
|
if (e instanceof viem.BaseError) {
|
|
24
35
|
let retryAfterMs;
|
|
25
36
|
if (e.walk((err) => {
|
|
26
37
|
if (err instanceof viem.HttpRequestError && err.status === 429) {
|
|
27
|
-
retryAfterMs = extractRetryAfter(err.headers);
|
|
38
|
+
retryAfterMs = extractRetryAfter(err.headers) ?? extractRetryHint(err.details);
|
|
28
39
|
return true;
|
|
29
40
|
}
|
|
30
41
|
return false;
|
|
31
42
|
}) !== null) return [true, retryAfterMs];
|
|
32
43
|
if (e.walk((err) => {
|
|
33
44
|
const msg = err.details ?? err.message ?? "";
|
|
34
|
-
|
|
35
|
-
|
|
45
|
+
if (RATE_LIMIT_PATTERNS.some((re) => re.test(msg))) {
|
|
46
|
+
retryAfterMs = extractRetryHint(msg);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}) !== null) return [true, retryAfterMs];
|
|
36
51
|
}
|
|
37
|
-
if (RATE_LIMIT_PATTERNS.some((re) => re.test(e.message))) return [true,
|
|
52
|
+
if (RATE_LIMIT_PATTERNS.some((re) => re.test(e.message))) return [true, extractRetryHint(e.message)];
|
|
38
53
|
return [false, void 0];
|
|
39
54
|
}
|
|
40
55
|
//#endregion
|
|
@@ -5,6 +5,20 @@ const require_dev_isTransientError = require("./isTransientError.js");
|
|
|
5
5
|
let viem = require("viem");
|
|
6
6
|
let zod_v4 = require("zod/v4");
|
|
7
7
|
//#region src/dev/resilientTransport.ts
|
|
8
|
+
/** Schema for {@link RateLimitOptions}. */
|
|
9
|
+
const rateLimitOptionsSchema = zod_v4.z.object({
|
|
10
|
+
/**
|
|
11
|
+
* Max retry attempts for rate-limit errors.
|
|
12
|
+
* @default 3
|
|
13
|
+
**/
|
|
14
|
+
retryCount: zod_v4.z.number().int().min(0).default(3),
|
|
15
|
+
/**
|
|
16
|
+
* Upper bound (ms) for rate-limit delay, applied both to backoff and to
|
|
17
|
+
* provider-supplied retry hints.
|
|
18
|
+
* @default 5_000
|
|
19
|
+
**/
|
|
20
|
+
maxDelay: zod_v4.z.number().min(0).default(5e3)
|
|
21
|
+
});
|
|
8
22
|
/** Schema for {@link ResilientTransportOptions}. */
|
|
9
23
|
const resilientTransportOptionsSchema = zod_v4.z.object({
|
|
10
24
|
/**
|
|
@@ -28,10 +42,11 @@ const resilientTransportOptionsSchema = zod_v4.z.object({
|
|
|
28
42
|
**/
|
|
29
43
|
jitter: zod_v4.z.number().min(0).default(100),
|
|
30
44
|
/**
|
|
31
|
-
* Retry on rate-limit (HTTP 429) errors.
|
|
45
|
+
* Retry on rate-limit (HTTP 429) errors. `false` disables, `true` uses
|
|
46
|
+
* default {@link RateLimitOptions}, an object overrides them.
|
|
32
47
|
* @default true
|
|
33
48
|
**/
|
|
34
|
-
handleRateLimit: zod_v4.z.boolean().
|
|
49
|
+
handleRateLimit: zod_v4.z.union([zod_v4.z.boolean(), rateLimitOptionsSchema]).transform((v) => v === true ? rateLimitOptionsSchema.parse({}) : v).prefault(true),
|
|
35
50
|
/**
|
|
36
51
|
* Retry on transient network/infrastructure errors.
|
|
37
52
|
* @default true
|
|
@@ -52,17 +67,24 @@ function resilientTransport(underlyingTransport, options) {
|
|
|
52
67
|
return (transportOpts) => {
|
|
53
68
|
const base = underlyingTransport(transportOpts);
|
|
54
69
|
const rpcRequest = base.request;
|
|
70
|
+
const rateLimit = opts.handleRateLimit;
|
|
71
|
+
const retryCount = rateLimit ? Math.max(opts.retryCount, rateLimit.retryCount) : opts.retryCount;
|
|
55
72
|
const request = async (args) => (0, viem.withRetry)(() => rpcRequest(args), {
|
|
56
|
-
retryCount
|
|
73
|
+
retryCount,
|
|
57
74
|
delay({ count, error }) {
|
|
58
|
-
|
|
75
|
+
const jitter = Math.floor(Math.random() * opts.jitter);
|
|
76
|
+
if (rateLimit) {
|
|
59
77
|
const [isRate, retryAfterMs] = require_dev_isRateLimitError.isRateLimitError(error);
|
|
60
|
-
if (isRate
|
|
78
|
+
if (isRate) {
|
|
79
|
+
const base = retryAfterMs ?? opts.delay * 2 ** count;
|
|
80
|
+
return Math.min(rateLimit.maxDelay, base) + jitter;
|
|
81
|
+
}
|
|
61
82
|
}
|
|
62
|
-
return Math.min(opts.maxDelay, opts.delay * 2 ** count) +
|
|
83
|
+
return Math.min(opts.maxDelay, opts.delay * 2 ** count) + jitter;
|
|
63
84
|
},
|
|
64
|
-
shouldRetry({ error }) {
|
|
65
|
-
if (
|
|
85
|
+
shouldRetry({ count, error }) {
|
|
86
|
+
if (rateLimit && require_dev_isRateLimitError.isRateLimitError(error)[0]) return count < rateLimit.retryCount;
|
|
87
|
+
if (count >= opts.retryCount) return false;
|
|
66
88
|
if (opts.handleTransient && require_dev_isTransientError.isTransientError(error)) return true;
|
|
67
89
|
if (opts.handleOutOfSync && require_dev_isOutOfSyncError.isOutOfSyncError(error)) return true;
|
|
68
90
|
return false;
|
|
@@ -75,5 +97,6 @@ function resilientTransport(underlyingTransport, options) {
|
|
|
75
97
|
};
|
|
76
98
|
}
|
|
77
99
|
//#endregion
|
|
100
|
+
exports.rateLimitOptionsSchema = rateLimitOptionsSchema;
|
|
78
101
|
exports.resilientTransport = resilientTransport;
|
|
79
102
|
exports.resilientTransportOptionsSchema = resilientTransportOptionsSchema;
|
|
@@ -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,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
|
|
24
|
-
*
|
|
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
|
|
28
|
-
* ceiling answered as `0n` because the collateral is too
|
|
29
|
-
* would tell a form nothing about what it is holding —
|
|
30
|
-
* to see is the one they are short of. Collateral
|
|
31
|
-
* therefore answers with it, whether or not the market
|
|
32
|
-
* a loan under the floor is refused by `borrow`
|
|
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
|
|
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")
|
|
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
|
|
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,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
|
|
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
|
-
*
|
|
293
|
-
*
|
|
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
|
-
*
|
|
324
|
-
*
|
|
292
|
+
* This suite's leveraged strategy, or `undefined` when no target collateral
|
|
293
|
+
* can be resolved.
|
|
325
294
|
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
339
|
-
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;
|
|
340
304
|
}
|
|
341
305
|
/**
|
|
342
|
-
*
|
|
343
|
-
*
|
|
306
|
+
* Whether `token` can be this suite's strategy target; see
|
|
307
|
+
* {@link isStrategyCollateral}.
|
|
344
308
|
*/
|
|
345
|
-
|
|
346
|
-
|
|
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
|
|
318
|
+
* 2. the target of {@link strategy};
|
|
357
319
|
* 3. `null` when neither can be resolved.
|
|
358
320
|
*/
|
|
359
321
|
accountTargetCollateral(creditAccount) {
|
|
360
|
-
const
|
|
361
|
-
|
|
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
|
*/
|