@gearbox-protocol/sdk 16.0.0-next.50 → 16.0.0-next.52

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 (93) hide show
  1. package/dist/cjs/model/index.js +1 -0
  2. package/dist/cjs/model/result.js +10 -0
  3. package/dist/cjs/onchain/accounts/intents/leverage-band.js +1 -1
  4. package/dist/cjs/onchain/accounts/intents/maxWithdrawCollateral.js +8 -19
  5. package/dist/cjs/onchain/accounts/intents/open-strategy.js +1 -1
  6. package/dist/cjs/onchain/accounts/intents/realize.js +1 -1
  7. package/dist/cjs/onchain/accounts/intents/testing/market.js +6 -5
  8. package/dist/cjs/onchain/accounts/intents/testing/sdk-mock.js +21 -74
  9. package/dist/cjs/onchain/accounts/intents/utils/pick-token.js +1 -1
  10. package/dist/cjs/onchain/accounts/intents/utils/price-impact.js +2 -2
  11. package/dist/cjs/onchain/accounts/intents/utils/router-path.js +1 -1
  12. package/dist/cjs/onchain/accounts/intents/view.js +1 -1
  13. package/dist/cjs/onchain/accounts/liquidations/LiquidationsService.js +3 -3
  14. package/dist/cjs/onchain/chain/chains.js +9 -3
  15. package/dist/cjs/onchain/index.js +1 -1
  16. package/dist/cjs/onchain/market/MarketSuite.js +0 -28
  17. package/dist/cjs/onchain/market/credit/collateralUtils.js +2 -2
  18. package/dist/cjs/onchain/market/index.js +1 -1
  19. package/dist/cjs/onchain/market/oracle/PriceOracleBaseContract.js +66 -13
  20. package/dist/cjs/onchain/market/oracle/TestPriceOracle.mock.js +215 -0
  21. package/dist/cjs/onchain/market/oracle/index.js +1 -1
  22. package/dist/cjs/onchain/positions/PositionsService.js +5 -7
  23. package/dist/cjs/onchain/positions/calcHealthFactor.js +4 -3
  24. package/dist/cjs/onchain/router/AbstractRouterContract.js +2 -24
  25. package/dist/cjs/onchain/router/RouterV310Contract.js +1 -12
  26. package/dist/cjs/preview/preview/buildDelayedStrategyPositionOperationPreview.js +26 -38
  27. package/dist/cjs/preview/preview/previewAdjustStrategyPosition.js +2 -3
  28. package/dist/cjs/preview/preview/previewExitOrRepayStrategyPosition.js +4 -5
  29. package/dist/cjs/preview/preview/previewOpenStrategyPosition.js +3 -7
  30. package/dist/cjs/preview/preview/previewOperation.js +1 -2
  31. package/dist/esm/dev/AccountOpener.js +1 -1
  32. package/dist/esm/dev/withdrawalUtils.js +1 -1
  33. package/dist/esm/model/index.js +2 -2
  34. package/dist/esm/model/result.js +10 -1
  35. package/dist/esm/onchain/accounts/CreditAccountsServiceV310.js +2 -2
  36. package/dist/esm/onchain/accounts/intents/leverage-band.js +1 -1
  37. package/dist/esm/onchain/accounts/intents/maxWithdrawCollateral.js +8 -19
  38. package/dist/esm/onchain/accounts/intents/open-strategy.js +1 -1
  39. package/dist/esm/onchain/accounts/intents/realize.js +1 -1
  40. package/dist/esm/onchain/accounts/intents/testing/market.js +6 -5
  41. package/dist/esm/onchain/accounts/intents/testing/sdk-mock.js +22 -75
  42. package/dist/esm/onchain/accounts/intents/utils/pick-token.js +1 -1
  43. package/dist/esm/onchain/accounts/intents/utils/price-impact.js +2 -2
  44. package/dist/esm/onchain/accounts/intents/utils/router-path.js +1 -1
  45. package/dist/esm/onchain/accounts/intents/view.js +1 -1
  46. package/dist/esm/onchain/accounts/liquidations/LiquidationsService.js +4 -4
  47. package/dist/esm/onchain/accounts/withdrawal-compressor/RedemptionLoggerV310Contract.js +1 -1
  48. package/dist/esm/onchain/accounts/withdrawal-compressor/WithdrawalCompressorV310Contract.js +1 -1
  49. package/dist/esm/onchain/accounts/withdrawal-compressor/WithdrawalCompressorV311Contract.js +1 -1
  50. package/dist/esm/onchain/accounts/withdrawal-compressor/WithdrawalCompressorV313Contract.js +1 -1
  51. package/dist/esm/onchain/base/TokensMeta.js +3 -3
  52. package/dist/esm/onchain/chain/chains.js +9 -3
  53. package/dist/esm/onchain/core/createAddressProvider.js +1 -1
  54. package/dist/esm/onchain/index.js +1 -1
  55. package/dist/esm/onchain/market/MarketSuite.js +0 -28
  56. package/dist/esm/onchain/market/adapters/contracts/AccountMigratorAdapterContract.js +1 -1
  57. package/dist/esm/onchain/market/adapters/contracts/ERC4626AdapterContract.js +1 -1
  58. package/dist/esm/onchain/market/credit/CreditFacadeV310BaseContract.js +1 -1
  59. package/dist/esm/onchain/market/credit/collateralUtils.js +2 -2
  60. package/dist/esm/onchain/market/index.js +1 -1
  61. package/dist/esm/onchain/market/oracle/PriceOracleBaseContract.js +66 -13
  62. package/dist/esm/onchain/market/oracle/TestPriceOracle.mock.js +213 -0
  63. package/dist/esm/onchain/market/oracle/index.js +1 -1
  64. package/dist/esm/onchain/market/pool/PoolV310Contract.js +1 -1
  65. package/dist/esm/onchain/market/zapper/IETHZapperContract.js +1 -1
  66. package/dist/esm/onchain/market/zapper/ZapperContract.js +1 -1
  67. package/dist/esm/onchain/positions/PositionsService.js +5 -7
  68. package/dist/esm/onchain/positions/calcHealthFactor.js +4 -3
  69. package/dist/esm/onchain/router/AbstractRouterContract.js +2 -24
  70. package/dist/esm/onchain/router/RouterV310Contract.js +1 -12
  71. package/dist/esm/onchain/utils/viem/simulateWithPriceUpdates.js +1 -1
  72. package/dist/esm/preview/preview/buildDelayedStrategyPositionOperationPreview.js +26 -38
  73. package/dist/esm/preview/preview/previewAdjustStrategyPosition.js +2 -3
  74. package/dist/esm/preview/preview/previewExitOrRepayStrategyPosition.js +4 -5
  75. package/dist/esm/preview/preview/previewOpenStrategyPosition.js +3 -7
  76. package/dist/esm/preview/preview/previewOperation.js +1 -2
  77. package/dist/esm/preview/simulate/simulatePoolOperation.js +1 -1
  78. package/dist/esm/preview/trace/extractTransfers.js +1 -1
  79. package/dist/types/model/index.d.ts +2 -2
  80. package/dist/types/model/previews.d.ts +4 -3
  81. package/dist/types/model/result.d.ts +16 -1
  82. package/dist/types/onchain/accounts/intents/maxWithdrawCollateral.d.ts +5 -3
  83. package/dist/types/onchain/accounts/intents/testing/market.d.ts +5 -1
  84. package/dist/types/onchain/accounts/intents/testing/sdk-mock.d.ts +1 -2
  85. package/dist/types/onchain/index.d.ts +2 -2
  86. package/dist/types/onchain/market/MarketSuite.d.ts +2 -29
  87. package/dist/types/onchain/market/index.d.ts +2 -2
  88. package/dist/types/onchain/market/oracle/PriceOracleBaseContract.d.ts +15 -5
  89. package/dist/types/onchain/market/oracle/types.d.ts +40 -28
  90. package/dist/types/onchain/positions/calcHealthFactor.d.ts +7 -3
  91. package/dist/types/onchain/router/AbstractRouterContract.d.ts +0 -4
  92. package/dist/types/preview/preview/buildDelayedStrategyPositionOperationPreview.d.ts +4 -6
  93. package/package.json +2 -10
@@ -1,5 +1,6 @@
1
1
  import { AddressMap } from "../../utils/AddressMap.js";
2
2
  import { AddressSet } from "../../utils/AddressSet.js";
3
+ import { BigIntMath } from "../../utils/bigint-math.js";
3
4
  import { AP_PRICE_FEED_COMPRESSOR, AP_WETH_TOKEN } from "../../constants/address-provider.js";
4
5
  import { NATIVE_ADDRESS } from "../../constants/addresses.js";
5
6
  import "../../constants/math.js";
@@ -10,10 +11,13 @@ import "../../utils/index.js";
10
11
  import { BaseContract } from "../../base/BaseContract.js";
11
12
  import "../../base/index.js";
12
13
  import { usdToNumber } from "../math.js";
14
+ import { safeValue } from "../../../model/result.js";
15
+ import "../../../model/index.js";
13
16
  import { priceFeedCompressorAbi } from "../../../abi/compressors/priceFeedCompressor.js";
14
17
  import { PriceFeedRef } from "../pricefeeds/PriceFeedRef.js";
15
18
  import { getRawPriceUpdates } from "../pricefeeds/getRawPriceUpdates.js";
16
19
  import "../pricefeeds/index.js";
20
+ import { unpriceableTokenError } from "./errors.js";
17
21
  import PriceFeedAnswerMap from "./PriceFeedAnswerMap.js";
18
22
  import { isAddressEqual, stringToHex } from "viem";
19
23
  import { format, formatDistanceToNow } from "date-fns";
@@ -141,10 +145,13 @@ var PriceOracleBaseContract = class extends BaseContract {
141
145
  **/
142
146
  safeConvert(from, to, amount) {
143
147
  try {
144
- return this.convert(from, to, amount);
145
- } catch (e) {
146
- this.logger?.debug(`cannot convert ${this.labelAddress(from)} to ${this.labelAddress(to)}: ${e}`);
147
- return null;
148
+ return { value: this.convert(from, to, amount) };
149
+ } catch {
150
+ try {
151
+ return { value: this.convert(from, to, amount, true) };
152
+ } catch {
153
+ return safeValue(0n, unpriceableTokenError(from));
154
+ }
148
155
  }
149
156
  }
150
157
  /**
@@ -152,26 +159,72 @@ var PriceOracleBaseContract = class extends BaseContract {
152
159
  **/
153
160
  safeConvertToUSD(token, amount) {
154
161
  try {
155
- return this.convertToUSD(token, amount);
156
- } catch (e) {
157
- this.logger?.debug(`cannot price ${this.labelAddress(token)}: ${e}`);
158
- return null;
162
+ return { value: this.convertToUSD(token, amount) };
163
+ } catch {
164
+ try {
165
+ return { value: this.convertToUSD(token, amount, true) };
166
+ } catch {
167
+ return safeValue(0n, unpriceableTokenError(token));
168
+ }
169
+ }
170
+ }
171
+ /**
172
+ * {@inheritDoc IPriceOracleContract.safeConvertMinUSD}
173
+ **/
174
+ safeConvertMinUSD(token, amount) {
175
+ let main;
176
+ try {
177
+ main = this.convertToUSD(token, amount);
178
+ } catch {
179
+ return safeValue(0n, unpriceableTokenError(token));
180
+ }
181
+ let feedToken = token;
182
+ if (isAddressEqual(token, "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE")) try {
183
+ feedToken = this.sdk.addressProvider.getAddress(AP_WETH_TOKEN, 0);
184
+ } catch {}
185
+ if (!this.reservePriceFeeds.has(feedToken)) return { value: 0n };
186
+ try {
187
+ return { value: BigIntMath.min(main, this.convertToUSD(token, amount, true)) };
188
+ } catch {
189
+ return safeValue(0n, unpriceableTokenError(token));
190
+ }
191
+ }
192
+ /**
193
+ * {@inheritDoc IPriceOracleContract.safeConvertFromUSD}
194
+ **/
195
+ safeConvertFromUSD(token, amount) {
196
+ try {
197
+ return { value: this.convertFromUSD(token, amount) };
198
+ } catch {
199
+ try {
200
+ return { value: this.convertFromUSD(token, amount, true) };
201
+ } catch {
202
+ return safeValue(0n, unpriceableTokenError(token));
203
+ }
159
204
  }
160
205
  }
161
206
  /**
162
- * {@inheritDoc IPriceOracleContract.safeUsdValue}
207
+ * {@inheritDoc IPriceOracleContract.safeConvertAssets}
163
208
  **/
164
- safeUsdValue(token, amount) {
165
- const usd = this.safeConvertToUSD(token, amount);
166
- return usd === null ? null : usdToNumber(usd);
209
+ safeConvertAssets(assets, to) {
210
+ let value = 0n;
211
+ let error;
212
+ for (const { token, balance } of assets) {
213
+ if (balance <= 10n) continue;
214
+ const converted = this.safeConvert(token, to, balance);
215
+ value += converted.value;
216
+ error ??= converted.error;
217
+ }
218
+ return safeValue(value, error);
167
219
  }
168
220
  /**
169
221
  * {@inheritDoc IPriceOracleContract.toAmount}
170
222
  **/
171
223
  toAmount = (token, value) => {
224
+ const priced = this.safeConvertToUSD(token, value);
172
225
  return {
173
226
  value,
174
- valueUsd: this.safeUsdValue(token, value)
227
+ valueUsd: priced.error ? null : usdToNumber(priced.value)
175
228
  };
176
229
  };
177
230
  /**
@@ -0,0 +1,213 @@
1
+ import { iPriceOracleV310Abi } from "../../../abi/310/generated.js";
2
+ import { getChain } from "../../chain/chains.js";
3
+ import "../../chain/index.js";
4
+ import "../../constants/address-provider.js";
5
+ import "../../constants/math.js";
6
+ import "../../constants/index.js";
7
+ import { toBN } from "../../utils/formatter.js";
8
+ import "../../utils/index.js";
9
+ import { ChainContractsRegister } from "../../base/ChainContractsRegister.js";
10
+ import "../../base/index.js";
11
+ import { PriceFeedRef } from "../pricefeeds/PriceFeedRef.js";
12
+ import "../pricefeeds/index.js";
13
+ import { PriceOracleBaseContract } from "./PriceOracleBaseContract.js";
14
+ import { bytesToHex, createPublicClient, custom, getAddress, hexToBytes } from "viem";
15
+ //#region src/onchain/market/oracle/TestPriceOracle.mock.ts
16
+ /**
17
+ * Well-known mainnet tokens for tests. The oracle prefills their metadata;
18
+ * a seed can then be as small as `{ price: 2000 }`.
19
+ **/
20
+ const MockTokens = {
21
+ WETH: getAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"),
22
+ USDC: getAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
23
+ DAI: getAddress("0x6B175474E89094C44Da98b954EedeAC495271d0F"),
24
+ cbETH: getAddress("0xBe9895146f7AF43049ca1c1AE358B0541Ea49704"),
25
+ dcUSDC: getAddress("0x50A9C808cd114E8fEA72f03aE2B1A8825677D56D"),
26
+ ACRED: getAddress("0x17418038ecF73BA4026c4f428547BF099706F27B"),
27
+ srpACRED_USDC: getAddress("0xe4a38b653B2580C9D72a50F190Ddd6E2d2D2a412")
28
+ };
29
+ const MOCK_TOKEN_META = {
30
+ [MockTokens.WETH]: {
31
+ decimals: 18,
32
+ symbol: "WETH",
33
+ name: "Wrapped Ether"
34
+ },
35
+ [MockTokens.USDC]: {
36
+ decimals: 6,
37
+ symbol: "USDC",
38
+ name: "USD Coin"
39
+ },
40
+ [MockTokens.DAI]: {
41
+ decimals: 18,
42
+ symbol: "DAI",
43
+ name: "Dai Stablecoin"
44
+ },
45
+ [MockTokens.cbETH]: {
46
+ decimals: 18,
47
+ symbol: "cbETH",
48
+ name: "Coinbase Wrapped Staked ETH"
49
+ },
50
+ [MockTokens.dcUSDC]: {
51
+ decimals: 6,
52
+ symbol: "dcUSDC",
53
+ name: "Default compliant USD Coin"
54
+ },
55
+ [MockTokens.ACRED]: {
56
+ decimals: 6,
57
+ symbol: "ACRED",
58
+ name: "Apollo Diversified Credit Securitize Fund"
59
+ },
60
+ [MockTokens.srpACRED_USDC]: {
61
+ decimals: 6,
62
+ symbol: "srpACRED_USDC",
63
+ name: "Securitize pending redemption Apollo Diversified Credit Securitize Fund to USD Coin"
64
+ }
65
+ };
66
+ const TEST_ORACLE_ADDRESS = getAddress("0x040ac1e000000000000000000000000000000310");
67
+ const FAILED_ANSWER = {
68
+ price: 0n,
69
+ success: false,
70
+ updatedAt: 0n
71
+ };
72
+ /**
73
+ * Register whose client rejects any RPC request: the oracle never talks to a
74
+ * node. `addressProvider.getAddress` answers `AP_WETH_TOKEN` with mainnet WETH
75
+ * so `#priceableToken` and `safeConvertMinUSD` exercise their real native
76
+ * branches.
77
+ **/
78
+ var TestOracleSdk = class extends ChainContractsRegister {
79
+ addressProvider = { getAddress: (contract, version) => {
80
+ if (contract === "WETH_TOKEN") return MockTokens.WETH;
81
+ throw new Error(`Address ${contract}, version: ${version} not found`);
82
+ } };
83
+ priceFeeds = { generatePriceFeedsUpdateTxs: () => {
84
+ throw new Error("TestPriceOracle: no priceFeeds");
85
+ } };
86
+ };
87
+ /**
88
+ * A real {@link PriceOracleBaseContract} seeded from plain records, for tests.
89
+ *
90
+ * Conversion, safe-price fallback, and native-through-WETH all run the
91
+ * production code. Feed-tree methods (`priceFeedData`, `priceUpdatesFor*`, …)
92
+ * are not wired and throw if touched.
93
+ **/
94
+ var TestPriceOracle = class extends PriceOracleBaseContract {
95
+ constructor(tokens = {}) {
96
+ const register = new TestOracleSdk(createPublicClient({
97
+ chain: getChain("Mainnet"),
98
+ transport: custom({ request: async () => {
99
+ throw new Error("not implemented");
100
+ } })
101
+ }));
102
+ super(register, {
103
+ abi: iPriceOracleV310Abi,
104
+ addr: TEST_ORACLE_ADDRESS,
105
+ name: "TestPriceOracle"
106
+ }, {
107
+ priceFeedMap: [],
108
+ priceFeedTree: []
109
+ });
110
+ for (const [token, meta] of Object.entries(MOCK_TOKEN_META)) {
111
+ const addr = getAddress(token);
112
+ this.register.tokensMeta.upsert(addr, {
113
+ addr,
114
+ ...meta
115
+ });
116
+ }
117
+ for (const [token, cfg] of Object.entries(tokens)) this.addToken(token, cfg);
118
+ }
119
+ /**
120
+ * Registers token meta and optional feed answers. Chainable, like all
121
+ * modifiers.
122
+ **/
123
+ addToken(token, cfg) {
124
+ const addr = getAddress(token);
125
+ const known = MOCK_TOKEN_META[addr];
126
+ const symbol = cfg.symbol ?? known?.symbol ?? `T_${addr.slice(2, 8)}`;
127
+ this.register.tokensMeta.upsert(addr, {
128
+ addr,
129
+ symbol,
130
+ name: cfg.name ?? known?.name ?? symbol,
131
+ decimals: cfg.decimals ?? known?.decimals ?? 18
132
+ });
133
+ if (cfg.price !== void 0) this.setMainPrice(addr, cfg.price);
134
+ if (cfg.reservePrice !== void 0) this.setReservePrice(addr, cfg.reservePrice);
135
+ return this;
136
+ }
137
+ setMainPrice(token, price) {
138
+ this.#setAnswer(token, "main", rawPrice(price));
139
+ return this;
140
+ }
141
+ setReservePrice(token, price) {
142
+ this.#setAnswer(token, "reserve", rawPrice(price));
143
+ return this;
144
+ }
145
+ /**
146
+ * Feed stays configured, its answer failed — {@link PriceFeedAnswerMap.price}
147
+ * throws. Distinct from {@link removeMainFeed}.
148
+ **/
149
+ failMainAnswer(token) {
150
+ this.#failAnswer(token, "main");
151
+ return this;
152
+ }
153
+ /**
154
+ * Feed stays configured, its answer failed. Distinct from
155
+ * {@link removeReserveFeed}: `safeConvertMinUSD` treats a present-but-failed
156
+ * reserve feed as an error, and a missing one as an untrusted 0.
157
+ **/
158
+ failReserveAnswer(token) {
159
+ this.#failAnswer(token, "reserve");
160
+ return this;
161
+ }
162
+ /**
163
+ * No main feed at all: distinct from a failed answer.
164
+ **/
165
+ removeMainFeed(token) {
166
+ this.mainPriceFeeds.delete(token);
167
+ this.mainPrices.delete(token);
168
+ return this;
169
+ }
170
+ /**
171
+ * No reserve feed at all: distinct from a failed answer, and what
172
+ * `safeConvertMinUSD` treats as an untrusted token (value 0, no error).
173
+ **/
174
+ removeReserveFeed(token) {
175
+ this.reservePriceFeeds.delete(token);
176
+ this.reservePrices.delete(token);
177
+ return this;
178
+ }
179
+ async updateAndConvert(_from, _to, _amount) {
180
+ throw new Error("TestPriceOracle: updateAndConvert needs a node");
181
+ }
182
+ #setAnswer(token, kind, price) {
183
+ this.#ensureFeed(token, kind);
184
+ this.#answers(kind).upsert(token, {
185
+ price,
186
+ success: true,
187
+ updatedAt: 0n
188
+ });
189
+ }
190
+ #failAnswer(token, kind) {
191
+ this.#ensureFeed(token, kind);
192
+ this.#answers(kind).upsert(token, FAILED_ANSWER);
193
+ }
194
+ #ensureFeed(token, kind) {
195
+ const feeds = kind === "main" ? this.mainPriceFeeds : this.reservePriceFeeds;
196
+ if (!feeds.has(token)) feeds.upsert(token, new PriceFeedRef(this.register, feedAddr(token, kind), 0));
197
+ }
198
+ #answers(kind) {
199
+ return kind === "main" ? this.mainPrices : this.reservePrices;
200
+ }
201
+ };
202
+ /** Human USD → 8-decimal fixed point; bigint passes through. */
203
+ function rawPrice(price) {
204
+ return typeof price === "bigint" ? price : toBN(String(price), 8);
205
+ }
206
+ /** Deterministic unused address per token feed; never resolved. */
207
+ function feedAddr(token, kind) {
208
+ const bytes = hexToBytes(getAddress(token));
209
+ bytes[0] = kind === "main" ? 254 : 253;
210
+ return getAddress(bytesToHex(bytes));
211
+ }
212
+ //#endregion
213
+ export { MockTokens, TEST_ORACLE_ADDRESS, TestPriceOracle };
@@ -1,6 +1,6 @@
1
1
  import { collateralPriceInUnderlying } from "./collateralPriceInUnderlying.js";
2
+ import { unpriceableTokenError } from "./errors.js";
2
3
  import { PriceOracleV310Contract } from "./PriceOracleV310Contract.js";
3
4
  import { createPriceOracle } from "./createPriceOracle.js";
4
- import { unpriceableTokenError } from "./errors.js";
5
5
  import "./types.js";
6
6
  export { PriceOracleV310Contract, collateralPriceInUnderlying, createPriceOracle, unpriceableTokenError };
@@ -1,3 +1,4 @@
1
+ import { iPausableAbi } from "../../../abi/iPausable.js";
1
2
  import { iPoolV310Abi } from "../../../abi/310/generated.js";
2
3
  import { AddressMap } from "../../utils/AddressMap.js";
3
4
  import { RAY } from "../../constants/math.js";
@@ -7,7 +8,6 @@ import "../../utils/index.js";
7
8
  import { SdkRWADataNotLoadedError } from "../../core/errors.js";
8
9
  import { BaseContract } from "../../base/BaseContract.js";
9
10
  import "../../base/index.js";
10
- import { iPausableAbi } from "../../../abi/iPausable.js";
11
11
  //#region src/onchain/market/pool/PoolV310Contract.ts
12
12
  const abi = [...iPoolV310Abi, ...iPausableAbi];
13
13
  var PoolV310Contract = class extends BaseContract {
@@ -1,5 +1,5 @@
1
- import { ZapperContract } from "./ZapperContract.js";
2
1
  import { iethZapperAbi } from "../../../abi/iETHZapper.js";
2
+ import { ZapperContract } from "./ZapperContract.js";
3
3
  //#region src/onchain/market/zapper/IETHZapperContract.ts
4
4
  const abi = iethZapperAbi;
5
5
  var IETHZapperContract = class extends ZapperContract {
@@ -1,8 +1,8 @@
1
+ import { iZapperAbi } from "../../../abi/iZapper.js";
1
2
  import { BaseContract } from "../../base/BaseContract.js";
2
3
  import "../../base/index.js";
3
4
  import { sdkErr, sdkOk } from "../../../model/result.js";
4
5
  import "../../../model/index.js";
5
- import { iZapperAbi } from "../../../abi/iZapper.js";
6
6
  //#region src/onchain/market/zapper/ZapperContract.ts
7
7
  /**
8
8
  * Base contract for every Gearbox zapper. Specialized zappers (e.g.
@@ -292,15 +292,12 @@ var PositionsService = class extends SDKConstruct {
292
292
  quota: priceOracle.toTokenAmount(market.underlying, t.quota),
293
293
  withdrawals: withdrawals.get(t.token) ?? []
294
294
  });
295
- if (recomputeTotals) {
296
- const usd = priceOracle.safeConvertToUSD(t.token, t.balance) || 0n;
297
- totalValueUSD += usd;
298
- }
295
+ if (recomputeTotals) totalValueUSD += priceOracle.safeConvertToUSD(t.token, t.balance).value;
299
296
  }
300
- if (recomputeTotals) totalValue = market.valueInUnderlying(ca.tokens.map((t) => ({
297
+ if (recomputeTotals) totalValue = priceOracle.safeConvertAssets(ca.tokens.map((t) => ({
301
298
  token: t.token,
302
299
  balance: t.balance
303
- }))).value;
300
+ })), market.underlying).value;
304
301
  const snapshot = {
305
302
  ...accountSnapshotFromCreditAccountData(ca),
306
303
  totalValue
@@ -308,7 +305,8 @@ var PositionsService = class extends SDKConstruct {
308
305
  const borrowRate = this.borrowRate(snapshot);
309
306
  const timeToLiquidation = this.timeToLiquidation(snapshot);
310
307
  const liquidationPrice = this.liquidationPrice(snapshot);
311
- const totalDebtUSD = priceFailed ? priceOracle.safeConvertToUSD(market.underlying, totalDebtValue) ?? 0n : ca.totalDebtUSD;
308
+ let totalDebtUSD = ca.totalDebtUSD;
309
+ if (priceFailed) totalDebtUSD = priceOracle.safeConvertToUSD(market.underlying, totalDebtValue).value;
312
310
  return {
313
311
  kind: "strategy",
314
312
  chainId: this.sdk.chainId,
@@ -23,10 +23,11 @@ function calcHealthFactor(props) {
23
23
  const active = new AddressMap(Object.entries(activeQuotas));
24
24
  const priceOf = (token, forCollateral) => {
25
25
  const main = pricesByToken.get(token);
26
- if (!safePrices || !forCollateral) return main;
26
+ if (!safePrices || !forCollateral || isAddressEqual(token, underlying)) return main;
27
+ if (main === void 0) return;
27
28
  const reserve = reservePricesByToken.get(token);
28
- if (main !== void 0 && reserve !== void 0) return BigIntMath.min(main, reserve);
29
- return main ?? reserve;
29
+ if (reserve === void 0) return 0n;
30
+ return BigIntMath.min(main, reserve);
30
31
  };
31
32
  const convertToUSD = (token, amount, forCollateral = false) => {
32
33
  const price = priceOf(token, forCollateral);
@@ -85,7 +85,7 @@ var AbstractRouterContract = class extends BaseContract {
85
85
  leftoverBalances.upsert(token, limitLeftover(balance, token) ?? balance);
86
86
  if (isEnabled && !keepAssets.has(token)) usdBalances.push({
87
87
  token,
88
- balance: this.safeConvertToUSD(priceOracle, token, balance)
88
+ balance: priceOracle.safeConvertToUSD(token, balance).value
89
89
  });
90
90
  }
91
91
  usdBalances.sort((a, b) => {
@@ -98,7 +98,7 @@ var AbstractRouterContract = class extends BaseContract {
98
98
  const lt = this.sdk.marketRegister.findCreditManager(ca.creditManager).creditManager.liquidationThresholds.mustGet(highestToken.token);
99
99
  const requiredDebtUSD = ca.totalDebtUSD * PERCENTAGE_FACTOR / BigInt(lt);
100
100
  if (highestToken.balance < requiredDebtUSD) return;
101
- const tokenAmount = this.safeConvertFromUSD(priceOracle, highestToken.token, requiredDebtUSD);
101
+ const tokenAmount = priceOracle.safeConvertFromUSD(highestToken.token, requiredDebtUSD).value;
102
102
  if (tokenAmount === 0n) return;
103
103
  let leftoverBalance = leftoverBalances.get(highestToken.token) ?? 0n;
104
104
  leftoverBalance -= tokenAmount;
@@ -113,28 +113,6 @@ var AbstractRouterContract = class extends BaseContract {
113
113
  tokensToClaim: new AssetsMap()
114
114
  };
115
115
  }
116
- safeConvertToUSD(priceOracle, token, balance) {
117
- try {
118
- return priceOracle.convertToUSD(token, balance);
119
- } catch {
120
- try {
121
- return priceOracle.convertToUSD(token, balance, true);
122
- } catch {
123
- return 0n;
124
- }
125
- }
126
- }
127
- safeConvertFromUSD(priceOracle, token, balance) {
128
- try {
129
- return priceOracle.convertFromUSD(token, balance);
130
- } catch {
131
- try {
132
- return priceOracle.convertFromUSD(token, balance, true);
133
- } catch {
134
- return 0n;
135
- }
136
- }
137
- }
138
116
  };
139
117
  //#endregion
140
118
  export { AbstractRouterContract };
@@ -227,7 +227,7 @@ var RouterV310Contract = class extends AbstractRouterContract {
227
227
  }).map(({ token, balance }) => {
228
228
  return {
229
229
  token,
230
- balance: this.#convertToUSDForSort(priceOracle, token, balance)
230
+ balance: priceOracle.safeConvertToUSD(token, balance).value
231
231
  };
232
232
  }).sort((a, b) => {
233
233
  return a.balance > b.balance ? -1 : 1;
@@ -240,17 +240,6 @@ var RouterV310Contract = class extends AbstractRouterContract {
240
240
  for (const [token, numSplits] of this.#numSplits.entries()) map.upsert(token, numSplits);
241
241
  return (token) => map.get(token) ?? 1n;
242
242
  }
243
- /**
244
- * Tries to get some value even when prices are broken, since it's only needed for sorting
245
- * @param oracle
246
- * @param token
247
- * @param amount
248
- * @returns
249
- */
250
- #convertToUSDForSort(oracle, token, amount) {
251
- const scale = 10n ** BigInt(this.tokensMeta.get(token)?.decimals ?? 0);
252
- return amount * (oracle.mainPrices.get(token)?.price || oracle.reservePrices.get(token)?.price || scale) / scale;
253
- }
254
243
  #debugTokenData(tData) {
255
244
  return tData.map((t) => ({
256
245
  token: this.labelAddress(t.token),
@@ -1,6 +1,6 @@
1
1
  import { errorAbis } from "../../../abi/errors.js";
2
- import { generateCastTraceCall } from "./cast.js";
3
2
  import { iUpdatablePriceFeedAbi } from "../../../abi/iUpdatablePriceFeed.js";
3
+ import { generateCastTraceCall } from "./cast.js";
4
4
  import { simulateMulticall } from "./simulateMulticall.js";
5
5
  import { BaseError, CallExecutionError, ContractFunctionRevertedError, decodeFunctionData, decodeFunctionResult, encodeFunctionData, parseAbi } from "viem";
6
6
  import { getAction, parseAccount } from "viem/utils";
@@ -3,7 +3,6 @@ import { BigIntMath } from "../../onchain/utils/bigint-math.js";
3
3
  import { DUST_THRESHOLD } from "../../onchain/constants/math.js";
4
4
  import { asEstimated } from "../../model/previews.js";
5
5
  import "../../model/index.js";
6
- import { unpriceableTokenError } from "../../onchain/market/oracle/errors.js";
7
6
  import "../../onchain/index.js";
8
7
  import { isAddressEqual } from "viem";
9
8
  //#region src/preview/preview/buildDelayedStrategyPositionOperationPreview.ts
@@ -13,10 +12,9 @@ import { isAddressEqual } from "viem";
13
12
  * the claim itself followed by the intent-specific tail
14
13
  *
15
14
  * Pure function: the input states are never mutated and no network access is performed.
16
- * Swaps are estimated with the injected conversion; remaining holdings are
17
- * priced by `MarketSuite.valueInUnderlying`. Tokens that cannot be priced
18
- * contribute nothing and set a non-fatal `unpriceableToken` warning on the
19
- * preview.
15
+ * Swaps and remaining holdings are priced by the market oracle. Tokens that
16
+ * cannot be priced contribute nothing and set a non-fatal `unpriceableToken`
17
+ * warning on the preview.
20
18
  *
21
19
  * The changes (e.g. `totalDebtChange`) are reported relative to the account
22
20
  * state before the whole transaction.
@@ -30,36 +28,26 @@ import { isAddressEqual } from "viem";
30
28
  * @param sdk - Market data source for the position metrics of the resulting
31
29
  * state; read synchronously, no network access.
32
30
  */
33
- function buildDelayedStrategyPositionOperationPreview(afterInstant, before, detected, convert, receivedToken, sdk) {
31
+ function buildDelayedStrategyPositionOperationPreview(afterInstant, before, detected, receivedToken, sdk) {
34
32
  const { request, intent } = detected;
35
33
  const post = afterInstant.clone();
36
- const converter = makeSafeConverter(convert);
34
+ const oracle = sdk.marketRegister.findByCreditManager(post.creditManager).priceOracle;
35
+ let warning;
36
+ const convert = (from, to, amount) => {
37
+ const priced = oracle.safeConvert(from, to, amount);
38
+ warning ??= priced.error;
39
+ return priced.value;
40
+ };
37
41
  const claimed = applyClaim(post, request);
38
42
  const collateralWithdrawn = new AssetsMap();
39
43
  switch (intent?.type) {
40
- case "CLOSE_ACCOUNT": return buildClosePreview(post, converter, receivedToken, sdk);
44
+ case "CLOSE_ACCOUNT": return buildClosePreview(post, receivedToken, sdk, warning);
41
45
  case "DECREASE_LEVERAGE":
42
- repayFromClaim(post, request.claimToken, converter.convert, claimed);
46
+ repayFromClaim(post, request.claimToken, convert, claimed);
43
47
  break;
44
- case "WITHDRAW_COLLATERAL": applyWithdrawCollateral(post, request, intent, converter, collateralWithdrawn);
48
+ case "WITHDRAW_COLLATERAL": applyWithdrawCollateral(post, request, intent, convert, collateralWithdrawn);
45
49
  }
46
- return buildAdjustPreview(post, before, collateralWithdrawn, converter, sdk);
47
- }
48
- function makeSafeConverter(convert) {
49
- let warning;
50
- return {
51
- convert: (token, to, amount) => {
52
- try {
53
- return convert(token, to, amount);
54
- } catch {
55
- warning ??= unpriceableTokenError(token);
56
- return 0n;
57
- }
58
- },
59
- get warning() {
60
- return warning;
61
- }
62
- };
50
+ return buildAdjustPreview(post, before, collateralWithdrawn, sdk, warning);
63
51
  }
64
52
  /**
65
53
  * Claim step, common to all intents: burns the phantom token (balance and
@@ -105,7 +93,7 @@ function applyClaim(post, request) {
105
93
  * debt with them, but no more than the intent's `debtRepaid` target;
106
94
  * whatever is not spent on repayment stays on the account as underlying.
107
95
  */
108
- function applyWithdrawCollateral(post, request, intent, converter, collateralWithdrawn) {
96
+ function applyWithdrawCollateral(post, request, intent, convert, collateralWithdrawn) {
109
97
  const { withdrawToken, withdrawAmount, debtRepaid } = intent;
110
98
  const { claimToken } = request;
111
99
  const sameToken = isAddressEqual(withdrawToken, claimToken);
@@ -115,19 +103,19 @@ function applyWithdrawCollateral(post, request, intent, converter, collateralWit
115
103
  const missing = withdrawAmount - fromBalance;
116
104
  if (missing > 0n && !sameToken) {
117
105
  const available = post.balances.getOrZero(claimToken);
118
- const cost = converter.convert(withdrawToken, claimToken, missing);
106
+ const cost = convert(withdrawToken, claimToken, missing);
119
107
  if (cost > 0n && cost <= available) {
120
108
  post.balances.dec(claimToken, cost);
121
109
  withdrawn += missing;
122
110
  } else if (available > 0n) {
123
111
  post.balances.dec(claimToken, available);
124
- withdrawn += converter.convert(claimToken, withdrawToken, available);
112
+ withdrawn += convert(claimToken, withdrawToken, available);
125
113
  }
126
114
  }
127
115
  if (withdrawn > 0n) collateralWithdrawn.upsert(withdrawToken, withdrawn);
128
116
  const remaining = post.balances.getOrZero(claimToken);
129
117
  if (remaining > 0n) {
130
- const proceeds = converter.convert(claimToken, post.underlying, remaining);
118
+ const proceeds = convert(claimToken, post.underlying, remaining);
131
119
  post.balances.dec(claimToken, remaining);
132
120
  post.balances.inc(post.underlying, proceeds);
133
121
  post.repay(BigIntMath.min(proceeds, debtRepaid));
@@ -151,10 +139,10 @@ function repayFromClaim(post, claimToken, convert, amount) {
151
139
  * underlying, the debt is repaid in full and the remainder is withdrawn to
152
140
  * the user as `receivedToken`.
153
141
  */
154
- function buildClosePreview(post, converter, receivedToken, sdk) {
142
+ function buildClosePreview(post, receivedToken, sdk, warning) {
155
143
  const market = sdk.marketRegister.findByCreditManager(post.creditManager);
156
- const priced = market.valueInUnderlying(post.balances.toAssets(), 0n);
157
144
  const oracle = market.priceOracle;
145
+ const priced = oracle.safeConvertAssets(post.balances.toAssets(), market.underlying);
158
146
  const suite = sdk.marketRegister.findCreditManager(post.creditManager);
159
147
  return {
160
148
  operation: "CloseCreditAccount",
@@ -170,15 +158,15 @@ function buildClosePreview(post, converter, receivedToken, sdk) {
170
158
  name: suite.accountStrategyName(post.creditAccount),
171
159
  targetCollateral: suite.accountTargetCollateral(post.creditAccount),
172
160
  receivedAmount: oracle.toTokenAmount(receivedToken, BigIntMath.max(priced.value - post.totalDebt, 0n)),
173
- warning: converter.warning ?? (priced.unpriceable ? unpriceableTokenError(priced.unpriceable) : void 0)
161
+ warning: warning ?? priced.error
174
162
  };
175
163
  }
176
- function buildAdjustPreview(post, before, collateralWithdrawn, converter, sdk) {
164
+ function buildAdjustPreview(post, before, collateralWithdrawn, sdk, warning) {
177
165
  const market = sdk.marketRegister.findByCreditManager(post.creditManager);
178
- const priced = market.valueInUnderlying(post.balances.toAssets());
166
+ const oracle = market.priceOracle;
167
+ const priced = oracle.safeConvertAssets(post.balances.toAssets(), market.underlying);
179
168
  const snap = post.toSnapshot(priced.value);
180
169
  const suite = sdk.marketRegister.findCreditManager(post.creditManager);
181
- const oracle = market.priceOracle;
182
170
  return {
183
171
  operation: "AdjustCreditAccount",
184
172
  ...asEstimated(sdk.positions.projection(snap, { availableLiquidityChange: before.totalDebt - post.totalDebt })),
@@ -193,7 +181,7 @@ function buildAdjustPreview(post, before, collateralWithdrawn, converter, sdk) {
193
181
  ...oracle.toAmount(market.underlying, q.balance)
194
182
  })),
195
183
  assetsChange: post.balances.difference(before.balances).toAssets(DUST_THRESHOLD).map((a) => oracle.toTokenAmount(a.token, a.balance)),
196
- warning: converter.warning ?? (priced.unpriceable ? unpriceableTokenError(priced.unpriceable) : void 0)
184
+ warning: warning ?? priced.error
197
185
  };
198
186
  }
199
187
  //#endregion
@@ -2,7 +2,6 @@ import { AP_WETH_TOKEN } from "../../onchain/constants/address-provider.js";
2
2
  import { DUST_THRESHOLD } from "../../onchain/constants/math.js";
3
3
  import { asEstimated } from "../../model/previews.js";
4
4
  import "../../model/index.js";
5
- import { unpriceableTokenError } from "../../onchain/market/oracle/errors.js";
6
5
  import "../../onchain/index.js";
7
6
  import { replayMulticall } from "./replayMulticall.js";
8
7
  import { unwrapNativeCollateral } from "./unwrapNativeCollateral.js";
@@ -25,8 +24,8 @@ function previewAdjustStrategyPosition(input, operation, options) {
25
24
  const { assets: collateralAdded, warning: unwrapWarning } = unwrapNativeCollateral(after.collateralAdded.toAssets(), value, sdk.addressProvider.getAddress(AP_WETH_TOKEN, 0));
26
25
  warning ??= unwrapWarning;
27
26
  const assetsChange = account.balances.difference(before.balances).toAssets(DUST_THRESHOLD);
28
- const priced = market.valueInUnderlying(account.balances.toAssets());
29
- if (priced.unpriceable) warning ??= unpriceableTokenError(priced.unpriceable);
27
+ const priced = oracle.safeConvertAssets(account.balances.toAssets(), market.underlying);
28
+ warning ??= priced.error;
30
29
  const snap = account.toSnapshot(priced.value);
31
30
  return {
32
31
  operation: "AdjustCreditAccount",