@liquidium/client 0.3.4 → 0.4.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/index.js CHANGED
@@ -30,11 +30,7 @@ var SupplyAction = {
30
30
  var OutflowType = {
31
31
  borrow: "borrow",
32
32
  feeClaim: "feeClaim",
33
- withdraw: "withdraw"
34
- };
35
- var InflowSubmitType = {
36
- DEPOSIT: "DEPOSIT",
37
- REPAY: "REPAY"
33
+ withdrawal: "withdrawal"
38
34
  };
39
35
 
40
36
  // src/core/config.ts
@@ -986,13 +982,10 @@ function getVariantKey(variant) {
986
982
 
987
983
  // src/core/wallet-actions.ts
988
984
  var TransferMode = {
989
- ck: "ck",
990
985
  native: "native"
991
986
  };
992
987
  var WalletExecutionKind = {
993
- sendEthTransaction: "send-eth-transaction",
994
- signMessage: "sign-message",
995
- signPsbt: "sign-psbt"
988
+ signMessage: "sign-message"
996
989
  };
997
990
  var WalletActionKind = {
998
991
  createAccount: "create-account",
@@ -1030,38 +1023,6 @@ function executeWith(options) {
1030
1023
  account: options.account ?? action.account
1031
1024
  });
1032
1025
  }
1033
- case WalletExecutionKind.signPsbt: {
1034
- if (!options.walletAdapter.signPsbt) {
1035
- throw new LiquidiumError(
1036
- LiquidiumErrorCode.VALIDATION_ERROR,
1037
- "Wallet adapter does not support PSBT signing"
1038
- );
1039
- }
1040
- const signedPsbtBase64 = await options.walletAdapter.signPsbt({
1041
- chain: Chain.BTC,
1042
- psbtBase64: action.psbtBase64,
1043
- account: options.account ?? action.account,
1044
- actionType: action.actionType,
1045
- transferMode: action.transferMode
1046
- });
1047
- return action.submit({ signedPsbtBase64 });
1048
- }
1049
- case WalletExecutionKind.sendEthTransaction: {
1050
- if (!options.walletAdapter.sendEthTransaction) {
1051
- throw new LiquidiumError(
1052
- LiquidiumErrorCode.VALIDATION_ERROR,
1053
- "Wallet adapter does not support ETH transaction sending"
1054
- );
1055
- }
1056
- const txHash = await options.walletAdapter.sendEthTransaction({
1057
- chain: Chain.ETH,
1058
- transaction: action.transaction,
1059
- account: options.account ?? action.account,
1060
- actionType: action.actionType,
1061
- transferMode: action.transferMode
1062
- });
1063
- return action.submit({ txHash });
1064
- }
1065
1026
  default:
1066
1027
  throw new LiquidiumError(
1067
1028
  LiquidiumErrorCode.VALIDATION_ERROR,
@@ -1368,8 +1329,8 @@ function mapCanisterWalletToWallet(canisterWallet) {
1368
1329
  );
1369
1330
  }
1370
1331
  }
1371
- var KNOWN_ASSET_TAGS = ["BTC", "SOL", "USDC", "USDT"];
1372
- var KNOWN_CHAIN_TAGS = ["BTC", "ETH", "SOL"];
1332
+ var KNOWN_ASSET_TAGS = ["BTC", "USDC", "USDT"];
1333
+ var KNOWN_CHAIN_TAGS = ["BTC", "ETH"];
1373
1334
  function extractVariantTag(variant, knownTags) {
1374
1335
  const [key] = Object.keys(variant);
1375
1336
  if (!key) {
@@ -1682,49 +1643,24 @@ function decodeLoanCreatedEvent(payload) {
1682
1643
  }
1683
1644
 
1684
1645
  // src/core/sdk-api-paths.ts
1685
- var SDK_API_VERSION = {
1686
- activities: "v1",
1687
- history: "v1",
1688
- inflow: "v1",
1689
- instantLoans: "v1"
1690
- };
1646
+ var SDK_API_V1_PATH = "/v1";
1647
+ var SDK_API_V2_PATH = "/v2";
1691
1648
  var SdkApiQueryParam = {
1692
1649
  cursor: "cursor",
1693
1650
  from: "from",
1651
+ filter: "filter",
1694
1652
  limit: "limit",
1695
1653
  market: "market",
1654
+ operations: "operations",
1696
1655
  poolId: "poolId",
1697
1656
  profileId: "profileId",
1698
- state: "state",
1699
- statuses: "statuses",
1700
- to: "to",
1701
- types: "types"
1657
+ states: "states",
1658
+ to: "to"
1702
1659
  };
1703
- var ACTIVITIES = `/${SDK_API_VERSION.activities}/activities`;
1704
- var HISTORY_POOL = `/${SDK_API_VERSION.history}/history/pool`;
1705
- var HISTORY_POOL_CONFIG = `/${SDK_API_VERSION.history}/history/pool-config`;
1706
- var HISTORY_RATES = `/${SDK_API_VERSION.history}/history/rates`;
1707
- var HISTORY_USERS = `/${SDK_API_VERSION.history}/history/users`;
1708
- var INFLOW = `/${SDK_API_VERSION.inflow}/inflow`;
1709
- var INSTANT_LOANS = `/${SDK_API_VERSION.instantLoans}/instant-loans`;
1710
- function buildHistoryPoolPath(poolId, query) {
1711
- const base = `${HISTORY_POOL}/${encodeURIComponent(poolId)}`;
1712
- const qs = query.toString();
1713
- return qs ? `${base}?${qs}` : base;
1714
- }
1715
- function buildHistoryPoolConfigPath(poolId, cursor) {
1716
- const base = `${HISTORY_POOL_CONFIG}/${encodeURIComponent(poolId)}`;
1717
- if (!cursor) {
1718
- return base;
1719
- }
1720
- const query = new URLSearchParams({ [SdkApiQueryParam.cursor]: cursor });
1721
- return `${base}?${query.toString()}`;
1722
- }
1723
- function buildHistoryRatesPath(poolId, query) {
1724
- const base = `${HISTORY_RATES}/${encodeURIComponent(poolId)}`;
1725
- const qs = query.toString();
1726
- return qs ? `${base}?${qs}` : base;
1727
- }
1660
+ var ACTIVITIES = `${SDK_API_V2_PATH}/activities`;
1661
+ var HISTORY_USERS = `${SDK_API_V2_PATH}/history/users`;
1662
+ var INFLOW = `${SDK_API_V2_PATH}/inflow`;
1663
+ var INSTANT_LOANS = `${SDK_API_V1_PATH}/instant-loans`;
1728
1664
  function buildHistoryUserTransactionsPath(user, query) {
1729
1665
  const base = `${HISTORY_USERS}/${encodeURIComponent(user)}/transactions`;
1730
1666
  const qs = query.toString();
@@ -1739,8 +1675,8 @@ function buildActivitiesPath(request) {
1739
1675
  const query = new URLSearchParams({
1740
1676
  [SdkApiQueryParam.profileId]: request.profileId
1741
1677
  });
1742
- if (request.state) {
1743
- query.set(SdkApiQueryParam.state, request.state);
1678
+ if (request.filter) {
1679
+ query.set(SdkApiQueryParam.filter, request.filter);
1744
1680
  }
1745
1681
  return `${ACTIVITIES}?${query.toString()}`;
1746
1682
  }
@@ -1798,12 +1734,6 @@ function parseBigInt(value, label) {
1798
1734
  );
1799
1735
  }
1800
1736
  }
1801
- function parseOptionalBigInt(value, label) {
1802
- if (value === void 0) {
1803
- return void 0;
1804
- }
1805
- return parseBigInt(value, label);
1806
- }
1807
1737
 
1808
1738
  // src/modules/instant-loans/ref-code.ts
1809
1739
  var REF_LENGTH = 6;
@@ -1874,28 +1804,8 @@ var ActivityFilter = {
1874
1804
  completed: "completed",
1875
1805
  all: "all"
1876
1806
  };
1877
- var ActivityDirection = {
1878
- inflow: "inflow",
1879
- outflow: "outflow"
1880
- };
1881
- var ActivityKind = {
1882
- deposit: "deposit",
1883
- repayment: "repayment",
1884
- borrow: "borrow",
1885
- withdraw: "withdraw"
1886
- };
1887
- var ActivityStatus = {
1888
- requested: "requested",
1889
- pending: "pending",
1890
- detected: "detected",
1891
- processing: "processing",
1892
- sent: "sent",
1893
- confirmed: "confirmed",
1894
- failed: "failed"
1895
- };
1896
1807
 
1897
1808
  // src/modules/activities/activities.ts
1898
- var PRE_TERMINAL_ETH_ACTIVITY_ID_PREFIX = "pre_terminal_eth_";
1899
1809
  var ActivitiesModule = class {
1900
1810
  constructor(apiClient, canisterContext) {
1901
1811
  this.apiClient = apiClient;
@@ -1904,11 +1814,11 @@ var ActivitiesModule = class {
1904
1814
  apiClient;
1905
1815
  canisterContext;
1906
1816
  /**
1907
- * Lists profile activities. Defaults to all activities.
1817
+ * Lists profile activities. Defaults to active activities.
1908
1818
  *
1909
1819
  * Uses the Liquidium SDK API.
1910
1820
  *
1911
- * @param request - Profile id or instant-loan short reference plus optional state filter.
1821
+ * @param request - Profile id or instant-loan short reference plus optional lifecycle filter.
1912
1822
  * @returns Activities owned by the resolved profile.
1913
1823
  */
1914
1824
  async list(request) {
@@ -1917,7 +1827,7 @@ var ActivitiesModule = class {
1917
1827
  const response = await apiClient.get(
1918
1828
  buildActivitiesPath({
1919
1829
  profileId,
1920
- state: request.filter ?? ActivityFilter.all
1830
+ filter: request.filter ?? ActivityFilter.active
1921
1831
  })
1922
1832
  );
1923
1833
  return response.activities.map(mapActivity);
@@ -1998,76 +1908,48 @@ function mapInstantLoanLookupError(error) {
1998
1908
  return new LiquidiumError(LiquidiumErrorCode.INTERNAL, key);
1999
1909
  }
2000
1910
  function mapActivity(wire) {
2001
- const { amount, stage, status, topUp: topUpWire, ...activity } = wire;
2002
- const effectiveStage = stage ?? deriveActivityStage(wire);
2003
- const topUp = activity.direction === ActivityDirection.inflow && topUpWire ? mapActivityTopUp(topUpWire) : void 0;
2004
- if (activity.direction === ActivityDirection.inflow) {
2005
- return {
2006
- ...activity,
2007
- direction: ActivityDirection.inflow,
2008
- kind: mapInflowActivityKind(activity.kind),
2009
- status: mapInflowActivityStatus(
2010
- mapActivityStatus(status, effectiveStage)
2011
- ),
2012
- ...topUp ? { topUp } : {},
2013
- amount: parseBigInt(amount, "activity amount")
2014
- };
2015
- }
2016
- return {
2017
- ...activity,
2018
- direction: ActivityDirection.outflow,
2019
- kind: mapOutflowActivityKind(activity.kind),
2020
- status: mapOutflowActivityStatus(mapActivityStatus(status, effectiveStage)),
2021
- amount: parseBigInt(amount, "activity amount")
1911
+ const amount = parseBigInt(wire.amount, "activity amount");
1912
+ const activityBase = {
1913
+ id: wire.id,
1914
+ poolId: wire.poolId,
1915
+ asset: wire.asset,
1916
+ chain: wire.chain,
1917
+ amount,
1918
+ timestampMs: wire.timestampMs
2022
1919
  };
2023
- }
2024
- function mapInflowActivityKind(kind) {
2025
- if (kind === "deposit" || kind === "repayment") {
2026
- return kind;
2027
- }
2028
- throw new LiquidiumError(
2029
- LiquidiumErrorCode.INTERNAL,
2030
- `Invalid inflow activity kind: ${kind}`
2031
- );
2032
- }
2033
- function mapOutflowActivityKind(kind) {
2034
- if (kind === "borrow" || kind === "withdraw") {
2035
- return kind;
1920
+ if (isInflowOperation(wire.status.operation)) {
1921
+ const activity = {
1922
+ ...activityBase,
1923
+ status: wire.status
1924
+ };
1925
+ if (wire.txids) {
1926
+ activity.txids = wire.txids;
1927
+ }
1928
+ if (wire.topUp) {
1929
+ activity.topUp = mapActivityTopUp(wire.topUp);
1930
+ }
1931
+ return activity;
2036
1932
  }
2037
- throw new LiquidiumError(
2038
- LiquidiumErrorCode.INTERNAL,
2039
- `Invalid outflow activity kind: ${kind}`
2040
- );
2041
- }
2042
- function mapInflowActivityStatus(status) {
2043
- if (status !== "sent") {
2044
- return status;
1933
+ if (isOutflowOperation(wire.status.operation)) {
1934
+ const activity = {
1935
+ ...activityBase,
1936
+ status: wire.status
1937
+ };
1938
+ if (wire.txids) {
1939
+ activity.txids = wire.txids;
1940
+ }
1941
+ return activity;
2045
1942
  }
2046
1943
  throw new LiquidiumError(
2047
1944
  LiquidiumErrorCode.INTERNAL,
2048
- `Invalid inflow activity status: ${status}`
1945
+ `Invalid activity operation: ${wire.status.operation}`
2049
1946
  );
2050
1947
  }
2051
- function mapOutflowActivityStatus(status) {
2052
- if (status !== "detected" && status !== "processing") {
2053
- return status;
2054
- }
2055
- throw new LiquidiumError(
2056
- LiquidiumErrorCode.INTERNAL,
2057
- `Invalid outflow activity status: ${status}`
2058
- );
1948
+ function isInflowOperation(operation) {
1949
+ return operation === "deposit" || operation === "repayment";
2059
1950
  }
2060
- function mapActivityStatus(status, stage) {
2061
- if (status !== "pending") {
2062
- return status;
2063
- }
2064
- if (stage === "deposited") {
2065
- return "detected";
2066
- }
2067
- if (stage === "confirmed" || stage === "finalising" || stage === "pending") {
2068
- return "processing";
2069
- }
2070
- return "pending";
1951
+ function isOutflowOperation(operation) {
1952
+ return operation === "borrow" || operation === "withdrawal";
2071
1953
  }
2072
1954
  function mapActivityTopUp(wire) {
2073
1955
  return {
@@ -2083,24 +1965,6 @@ function mapActivityTopUp(wire) {
2083
1965
  )
2084
1966
  };
2085
1967
  }
2086
- function deriveActivityStage(wire) {
2087
- if (wire.id.startsWith(PRE_TERMINAL_ETH_ACTIVITY_ID_PREFIX) && wire.direction === ActivityDirection.inflow && wire.chain === Chain.ETH) {
2088
- return "deposited";
2089
- }
2090
- return void 0;
2091
- }
2092
-
2093
- // src/core/rates.ts
2094
- var RATE_SCALE = 1000000000000000000000000000n;
2095
- var RATE_DECIMALS = BigInt(RATE_SCALE.toString().length - 1);
2096
-
2097
- // src/modules/history/types.ts
2098
- var UserHistoryStatus = {
2099
- requested: "requested",
2100
- pending: "pending",
2101
- confirmed: "confirmed",
2102
- failed: "failed"
2103
- };
2104
1968
 
2105
1969
  // src/modules/history/history.ts
2106
1970
  var HistoryModule = class {
@@ -2118,154 +1982,41 @@ var HistoryModule = class {
2118
1982
  return this.apiClient;
2119
1983
  }
2120
1984
  /**
2121
- * Returns paginated rate and utilization history for a pool.
2122
- *
2123
- * @param poolId - The pool principal text.
2124
- * @param window - Optional time window with from/to timestamps and limit.
2125
- * @returns A page of pool rate history entries and the next cursor when more results are available.
2126
- */
2127
- async getPoolHistory(poolId, window = {}) {
2128
- const apiClient = this.requireApi();
2129
- const query = createHistoryWindowQuery(window);
2130
- const requestPath = buildHistoryPoolPath(poolId, query);
2131
- const response = await apiClient.get(requestPath);
2132
- return {
2133
- items: response.items.map((item) => ({
2134
- date: item.date,
2135
- rateDecimals: RATE_DECIMALS,
2136
- avgBorrowRate: parseBigInt(item.avgBorrowRate, "pool borrow rate"),
2137
- avgLendRate: parseBigInt(item.avgLendRate, "pool lend rate"),
2138
- avgUtilizationRate: parseBigInt(
2139
- item.avgUtilizationRate,
2140
- "pool utilization rate"
2141
- )
2142
- })),
2143
- nextCursor: response.nextCursor
2144
- };
2145
- }
2146
- /**
2147
- * Returns paginated configuration change history for a pool.
2148
- *
2149
- * @param poolId - The pool principal text.
2150
- * @param cursor - An optional pagination cursor from a previous response.
2151
- * @returns A page of pool configuration changes and the next cursor when more results are available.
2152
- */
2153
- async getPoolConfigHistory(poolId, cursor) {
2154
- const apiClient = this.requireApi();
2155
- const requestPath = buildHistoryPoolConfigPath(poolId, cursor);
2156
- const response = await apiClient.get(requestPath);
2157
- return {
2158
- items: response.items.map((item) => ({
2159
- type: item.type,
2160
- poolId: item.poolId,
2161
- asset: item.asset,
2162
- chain: item.chain,
2163
- timestamp: item.timestamp,
2164
- totalSupply: parseBigInt(item.totalSupply, "pool history totalSupply"),
2165
- totalDebt: parseBigInt(item.totalDebt, "pool history totalDebt"),
2166
- supplyCap: parseOptionalBigInt(
2167
- item.supplyCap,
2168
- "pool history supplyCap"
2169
- ),
2170
- borrowCap: parseOptionalBigInt(
2171
- item.borrowCap,
2172
- "pool history borrowCap"
2173
- ),
2174
- maxLtv: parseBigInt(item.maxLtv, "pool history maxLtv"),
2175
- liquidationThreshold: parseBigInt(
2176
- item.liquidationThreshold,
2177
- "pool history liquidationThreshold"
2178
- ),
2179
- liquidationBonus: parseBigInt(
2180
- item.liquidationBonus,
2181
- "pool history liquidationBonus"
2182
- ),
2183
- protocolLiquidationFee: parseBigInt(
2184
- item.protocolLiquidationFee,
2185
- "pool history protocolLiquidationFee"
2186
- ),
2187
- reserveFactor: parseBigInt(
2188
- item.reserveFactor,
2189
- "pool history reserveFactor"
2190
- ),
2191
- baseRate: parseBigInt(item.baseRate, "pool history baseRate"),
2192
- optimalUtilizationRate: parseBigInt(
2193
- item.optimalUtilizationRate,
2194
- "pool history optimalUtilizationRate"
2195
- ),
2196
- rateSlopeBefore: parseBigInt(
2197
- item.rateSlopeBefore,
2198
- "pool history rateSlopeBefore"
2199
- ),
2200
- rateSlopeAfter: parseBigInt(
2201
- item.rateSlopeAfter,
2202
- "pool history rateSlopeAfter"
2203
- ),
2204
- lendingIndex: parseBigInt(
2205
- item.lendingIndex,
2206
- "pool history lendingIndex"
2207
- ),
2208
- borrowIndex: parseBigInt(item.borrowIndex, "pool history borrowIndex"),
2209
- sameAssetBorrowing: item.sameAssetBorrowing,
2210
- frozen: item.frozen
2211
- })),
2212
- nextCursor: response.nextCursor
2213
- };
2214
- }
2215
- /**
2216
- * Returns borrow rate history for a pool.
1985
+ * Returns transaction history for a user.
2217
1986
  *
2218
- * @param poolId - The pool principal text.
2219
- * @param window - Optional time window with from/to timestamps and limit.
2220
- * @returns Paginated APY samples.
1987
+ * @param user - The Liquidium profile principal text.
1988
+ * @param filters - Optional pool, operation, state, time range, and pagination filters.
1989
+ * @returns Paginated user history entries.
2221
1990
  */
2222
- async getBorrowRateHistory(poolId, window = {}) {
2223
- const apiClient = this.requireApi();
2224
- const query = createHistoryWindowQuery(window);
2225
- const requestPath = buildHistoryRatesPath(poolId, query);
2226
- const response = await apiClient.get(requestPath);
2227
- return {
2228
- items: response.items.map((item) => ({
2229
- date: item.date,
2230
- rateDecimals: RATE_DECIMALS,
2231
- avgRate: parseBigInt(item.avgRate, "borrow rate")
2232
- })),
2233
- nextCursor: response.nextCursor
2234
- };
2235
- }
2236
- async getUserTransactionHistory(user, marketOrFilters, filters = {}) {
1991
+ async getUserTransactionHistory(user, filters = {}) {
2237
1992
  const apiClient = this.requireApi();
2238
- const normalizedFilters = normalizeTransactionHistoryFilters(
2239
- marketOrFilters,
2240
- filters
2241
- );
2242
1993
  const query = new URLSearchParams();
2243
- if (normalizedFilters.cursor) {
2244
- query.set(SdkApiQueryParam.cursor, normalizedFilters.cursor);
1994
+ if (filters.cursor) {
1995
+ query.set(SdkApiQueryParam.cursor, filters.cursor);
2245
1996
  }
2246
- if (normalizedFilters.market) {
2247
- query.set(SdkApiQueryParam.market, normalizedFilters.market);
1997
+ if (filters.market) {
1998
+ query.set(SdkApiQueryParam.market, filters.market);
2248
1999
  }
2249
- if (normalizedFilters.poolId) {
2250
- query.set(SdkApiQueryParam.poolId, normalizedFilters.poolId);
2000
+ if (filters.poolId) {
2001
+ query.set(SdkApiQueryParam.poolId, filters.poolId);
2251
2002
  }
2252
- if (normalizedFilters.types?.length) {
2253
- query.set(SdkApiQueryParam.types, normalizedFilters.types.join(","));
2003
+ if (filters.operations?.length) {
2004
+ query.set(SdkApiQueryParam.operations, filters.operations.join(","));
2254
2005
  }
2255
- if (normalizedFilters.statuses?.length) {
2006
+ if (filters.states?.length) {
2256
2007
  query.set(
2257
- SdkApiQueryParam.statuses,
2258
- normalizedFilters.statuses.map(mapHistoryStatusToApi).join(",")
2008
+ SdkApiQueryParam.states,
2009
+ createHistoryStateFilterParam(filters.states)
2259
2010
  );
2260
2011
  }
2261
- if (normalizedFilters.from) {
2262
- query.set(SdkApiQueryParam.from, normalizedFilters.from);
2012
+ if (filters.from) {
2013
+ query.set(SdkApiQueryParam.from, filters.from);
2263
2014
  }
2264
- if (normalizedFilters.to) {
2265
- query.set(SdkApiQueryParam.to, normalizedFilters.to);
2015
+ if (filters.to) {
2016
+ query.set(SdkApiQueryParam.to, filters.to);
2266
2017
  }
2267
- if (normalizedFilters.limit !== void 0) {
2268
- query.set(SdkApiQueryParam.limit, String(normalizedFilters.limit));
2018
+ if (filters.limit !== void 0) {
2019
+ query.set(SdkApiQueryParam.limit, String(filters.limit));
2269
2020
  }
2270
2021
  const requestPath = buildHistoryUserTransactionsPath(user, query);
2271
2022
  const response = await apiClient.get(requestPath);
@@ -2274,30 +2025,33 @@ var HistoryModule = class {
2274
2025
  nextCursor: response.nextCursor
2275
2026
  };
2276
2027
  }
2277
- async getLiquidationHistory(user, marketOrFilters, filters = {}) {
2028
+ /**
2029
+ * Returns liquidation history for a user.
2030
+ *
2031
+ * @param user - The Liquidium profile principal text.
2032
+ * @param filters - Optional pool, time range, and pagination filters.
2033
+ * @returns Paginated liquidation history entries.
2034
+ */
2035
+ async getLiquidationHistory(user, filters = {}) {
2278
2036
  const apiClient = this.requireApi();
2279
- const normalizedFilters = normalizeLiquidationHistoryFilters(
2280
- marketOrFilters,
2281
- filters
2282
- );
2283
2037
  const query = new URLSearchParams();
2284
- if (normalizedFilters.cursor) {
2285
- query.set(SdkApiQueryParam.cursor, normalizedFilters.cursor);
2038
+ if (filters.cursor) {
2039
+ query.set(SdkApiQueryParam.cursor, filters.cursor);
2286
2040
  }
2287
- if (normalizedFilters.market) {
2288
- query.set(SdkApiQueryParam.market, normalizedFilters.market);
2041
+ if (filters.market) {
2042
+ query.set(SdkApiQueryParam.market, filters.market);
2289
2043
  }
2290
- if (normalizedFilters.poolId) {
2291
- query.set(SdkApiQueryParam.poolId, normalizedFilters.poolId);
2044
+ if (filters.poolId) {
2045
+ query.set(SdkApiQueryParam.poolId, filters.poolId);
2292
2046
  }
2293
- if (normalizedFilters.from) {
2294
- query.set(SdkApiQueryParam.from, normalizedFilters.from);
2047
+ if (filters.from) {
2048
+ query.set(SdkApiQueryParam.from, filters.from);
2295
2049
  }
2296
- if (normalizedFilters.to) {
2297
- query.set(SdkApiQueryParam.to, normalizedFilters.to);
2050
+ if (filters.to) {
2051
+ query.set(SdkApiQueryParam.to, filters.to);
2298
2052
  }
2299
- if (normalizedFilters.limit !== void 0) {
2300
- query.set(SdkApiQueryParam.limit, String(normalizedFilters.limit));
2053
+ if (filters.limit !== void 0) {
2054
+ query.set(SdkApiQueryParam.limit, String(filters.limit));
2301
2055
  }
2302
2056
  const requestPath = buildHistoryUserLiquidationsPath(user, query);
2303
2057
  const response = await apiClient.get(requestPath);
@@ -2310,77 +2064,59 @@ var HistoryModule = class {
2310
2064
  function mapUserTransactionHistoryEntry(item) {
2311
2065
  return {
2312
2066
  id: item.id,
2313
- type: mapUserTransactionHistoryType(item.type),
2314
2067
  amount: parseBigInt(item.amount, "history user amount"),
2315
2068
  poolId: item.poolId,
2316
2069
  timestamp: item.timestamp,
2317
- status: mapHistoryStatusFromApi(item.status),
2070
+ status: item.status,
2318
2071
  txids: item.txids
2319
2072
  };
2320
2073
  }
2321
2074
  function mapUserLiquidationHistoryEntry(item) {
2322
- if (item.type !== "liquidation") {
2075
+ if (item.status.operation !== "liquidation" || item.status.state !== "completed") {
2323
2076
  throw new LiquidiumError(
2324
2077
  LiquidiumErrorCode.INTERNAL,
2325
- `Invalid liquidation history type: ${item.type}`
2078
+ `Invalid liquidation history status: ${item.status.state}`
2326
2079
  );
2327
2080
  }
2328
2081
  return {
2329
2082
  id: item.id,
2330
- type: "liquidation",
2331
2083
  amount: parseBigInt(item.amount, "history user amount"),
2332
2084
  poolId: item.poolId,
2333
2085
  timestamp: item.timestamp,
2334
- status: mapLiquidationHistoryStatus(item.status),
2086
+ status: item.status,
2335
2087
  txids: item.txids
2336
2088
  };
2337
2089
  }
2338
- function mapUserTransactionHistoryType(type) {
2339
- if (type !== "liquidation") {
2340
- return type;
2341
- }
2342
- throw new LiquidiumError(
2343
- LiquidiumErrorCode.INTERNAL,
2344
- "Transaction history response included a liquidation entry"
2345
- );
2346
- }
2347
- function mapLiquidationHistoryStatus(status) {
2348
- const mappedStatus = mapHistoryStatusFromApi(status);
2349
- if (mappedStatus === UserHistoryStatus.confirmed) {
2350
- return mappedStatus;
2351
- }
2352
- throw new LiquidiumError(
2353
- LiquidiumErrorCode.INTERNAL,
2354
- `Invalid liquidation history status: ${status}`
2355
- );
2356
- }
2357
- function createHistoryWindowQuery(window) {
2358
- const query = new URLSearchParams();
2359
- if (window.cursor) query.set(SdkApiQueryParam.cursor, window.cursor);
2360
- if (window.from) query.set(SdkApiQueryParam.from, window.from);
2361
- if (window.to) query.set(SdkApiQueryParam.to, window.to);
2362
- if (window.limit !== void 0) {
2363
- query.set(SdkApiQueryParam.limit, String(window.limit));
2090
+ function createHistoryStateFilterParam(states) {
2091
+ for (const state of states) {
2092
+ validateHistoryStateFilter(state);
2364
2093
  }
2365
- return query;
2094
+ return [...new Set(states)].join(",");
2366
2095
  }
2367
- function normalizeTransactionHistoryFilters(marketOrFilters, filters) {
2368
- if (typeof marketOrFilters === "string") {
2369
- return { ...filters, market: marketOrFilters };
2370
- }
2371
- return { ...marketOrFilters ?? {}, ...filters };
2372
- }
2373
- function normalizeLiquidationHistoryFilters(marketOrFilters, filters) {
2374
- if (typeof marketOrFilters === "string") {
2375
- return { ...filters, market: marketOrFilters };
2096
+ function validateHistoryStateFilter(state) {
2097
+ switch (state) {
2098
+ case "action_required":
2099
+ case "confirming":
2100
+ case "processing":
2101
+ case "completed":
2102
+ case "failed":
2103
+ return;
2104
+ default:
2105
+ throw new LiquidiumError(
2106
+ LiquidiumErrorCode.VALIDATION_ERROR,
2107
+ `History state filter is not supported: ${state}`
2108
+ );
2376
2109
  }
2377
- return { ...marketOrFilters ?? {}, ...filters };
2378
2110
  }
2379
- function mapHistoryStatusFromApi(status) {
2380
- return status.toLowerCase();
2381
- }
2382
- function mapHistoryStatusToApi(status) {
2383
- return status.toUpperCase();
2111
+
2112
+ // src/core/status.ts
2113
+ function createLiquidiumStatus(params) {
2114
+ return {
2115
+ operation: params.operation,
2116
+ state: params.state,
2117
+ confirmations: params.confirmations ?? null,
2118
+ requiredConfirmations: params.requiredConfirmations ?? null
2119
+ };
2384
2120
  }
2385
2121
 
2386
2122
  // src/core/utils/api-response-parsers.ts
@@ -3486,13 +3222,12 @@ function assertSupportsIcrcAccountInflowTarget(asset) {
3486
3222
  }
3487
3223
 
3488
3224
  // src/core/borrow-minimums.ts
3225
+ var MINIMUM_BTC_BORROW_AMOUNT_SATS = 5100n;
3226
+ var MINIMUM_STABLECOIN_BORROW_AMOUNT_BASE_UNITS = 1000000n;
3489
3227
  var MIN_BORROW_AMOUNTS_BY_ASSET = {
3490
- BTC: 5100n,
3491
- // 5,100 sats = 0.000051 BTC
3492
- USDC: 1000000n,
3493
- // 1 USDC
3494
- USDT: 1000000n
3495
- // 1 USDT
3228
+ [Asset.BTC]: MINIMUM_BTC_BORROW_AMOUNT_SATS,
3229
+ [Asset.USDC]: MINIMUM_STABLECOIN_BORROW_AMOUNT_BASE_UNITS,
3230
+ [Asset.USDT]: MINIMUM_STABLECOIN_BORROW_AMOUNT_BASE_UNITS
3496
3231
  };
3497
3232
  function getMinimumBorrowAmount(asset) {
3498
3233
  if (!isMinimumBorrowAsset(asset)) {
@@ -3977,20 +3712,11 @@ function getChainForInstantLoanAsset(asset) {
3977
3712
  return asset;
3978
3713
  }
3979
3714
 
3980
- // src/modules/instant-loans/types.ts
3981
- var InstantLoanStatus = {
3982
- awaitingDeposit: "awaiting_deposit",
3983
- depositDetected: "deposit_detected",
3984
- active: "active",
3985
- settling: "settling",
3986
- closed: "closed",
3987
- expired: "expired"
3988
- };
3989
-
3990
3715
  // src/modules/instant-loans/instant-loans.ts
3991
3716
  var REPAYMENT_BUFFER_SECONDS = 86400n;
3992
- var RATE_SCALE2 = 10n ** 27n;
3717
+ var RATE_SCALE = 10n ** 27n;
3993
3718
  var SECONDS_PER_YEAR = 31536000n;
3719
+ var MILLISECONDS_PER_SECOND3 = 1e3;
3994
3720
  var ETH_STABLECOIN_INFLOW_FEE_FALLBACK = 1500000n;
3995
3721
  var INSTANT_LOAN_MIN_SLIPPAGE_BUFFER_BPS = 200n;
3996
3722
  var INSTANT_LOAN_FIND_QUERY_MAX_LENGTH = 256;
@@ -4002,14 +3728,16 @@ var INSTANT_LOAN_ASSETS = [
4002
3728
  Asset.USDT
4003
3729
  ];
4004
3730
  var InstantLoansModule = class {
4005
- constructor(canisterContext, apiClient, lending, positions) {
3731
+ constructor(canisterContext, apiClient, activities, lending, positions) {
4006
3732
  this.canisterContext = canisterContext;
4007
3733
  this.apiClient = apiClient;
3734
+ this.activities = activities;
4008
3735
  this.lending = lending;
4009
3736
  this.positions = positions;
4010
3737
  }
4011
3738
  canisterContext;
4012
3739
  apiClient;
3740
+ activities;
4013
3741
  lending;
4014
3742
  positions;
4015
3743
  /**
@@ -4209,7 +3937,8 @@ var InstantLoansModule = class {
4209
3937
  const response = await apiClient.get(
4210
3938
  buildInstantLoanFindPath({ query })
4211
3939
  );
4212
- return (response.candidates ?? response.loans ?? []).map(mapCandidateWire);
3940
+ const candidates = "candidates" in response ? response.candidates : response.loans;
3941
+ return candidates.map(mapCandidateWire);
4213
3942
  }
4214
3943
  async getLoanRecord(loanId) {
4215
3944
  try {
@@ -4248,6 +3977,7 @@ var InstantLoansModule = class {
4248
3977
  borrowAmount: record.borrow_amount,
4249
3978
  borrowDestination: accountFromCanister(record.borrow_destination),
4250
3979
  refundDestination: accountFromCanister(record.refund_destination),
3980
+ started: record.started,
4251
3981
  depositDetectedTimestamp: record.deposit_detected_ts[0] ?? null,
4252
3982
  expiryTimestamp: record.expires_at[0] ?? deriveDepositExpiryTimestamp({
4253
3983
  depositDetectedTimestamp: record.deposit_detected_ts[0] ?? null,
@@ -4276,7 +4006,8 @@ var InstantLoansModule = class {
4276
4006
  repayTarget,
4277
4007
  collateralPosition,
4278
4008
  borrowPosition,
4279
- borrowPoolRate
4009
+ borrowPoolRate,
4010
+ activeActivities
4280
4011
  ] = await Promise.all([
4281
4012
  resolveSupplyTarget(this.canisterContext, {
4282
4013
  profileId,
@@ -4292,7 +4023,8 @@ var InstantLoansModule = class {
4292
4023
  }),
4293
4024
  this.positions.getPosition(profileId, collateralPoolId),
4294
4025
  this.positions.getPosition(profileId, borrowPoolId),
4295
- this.positions.market.getPoolRate(borrowPoolId)
4026
+ this.positions.market.getPoolRate(borrowPoolId),
4027
+ this.activities.list({ profileId, filter: ActivityFilter.active })
4296
4028
  ]);
4297
4029
  const totalDebtAmount = calculateTotalDebtAmount(borrowPosition);
4298
4030
  const interestBufferAmount = calculateInterestBufferAmount(
@@ -4309,8 +4041,12 @@ var InstantLoansModule = class {
4309
4041
  const borrowedDecimals = borrowPosition?.borrowedDecimals ?? getAssetNativeDecimals(borrowAsset);
4310
4042
  const debtInterestAmount = borrowPosition?.debtInterest ?? 0n;
4311
4043
  const status = deriveInstantLoanStatus({
4044
+ started: input.started,
4045
+ depositDetectedTimestamp: input.depositDetectedTimestamp,
4046
+ expiryTimestamp: input.expiryTimestamp,
4312
4047
  collateralAmount: currentCollateralAmount,
4313
- totalDebtAmount
4048
+ totalDebtAmount,
4049
+ activeActivities
4314
4050
  });
4315
4051
  const initialDeposit = await this.createInitialDepositQuote({
4316
4052
  collateralAmount,
@@ -4462,7 +4198,7 @@ function calculateInterestBufferAmount(borrowPosition, annualBorrowRate) {
4462
4198
  if (totalDebtAmount <= 0n || annualBorrowRate <= 0n) {
4463
4199
  return 0n;
4464
4200
  }
4465
- const interestBuffer = totalDebtAmount * annualBorrowRate * REPAYMENT_BUFFER_SECONDS / RATE_SCALE2 / SECONDS_PER_YEAR;
4201
+ const interestBuffer = totalDebtAmount * annualBorrowRate * REPAYMENT_BUFFER_SECONDS / RATE_SCALE / SECONDS_PER_YEAR;
4466
4202
  return interestBuffer > 1n ? interestBuffer : 1n;
4467
4203
  }
4468
4204
  function calculateTotalDebtAmount(borrowPosition) {
@@ -4472,13 +4208,75 @@ function calculateTotalDebtAmount(borrowPosition) {
4472
4208
  return borrowPosition.borrowed + borrowPosition.debtInterest;
4473
4209
  }
4474
4210
  function deriveInstantLoanStatus(input) {
4211
+ const activeActivityStatus = deriveActiveInstantLoanActivityStatus(input);
4212
+ if (activeActivityStatus) {
4213
+ return activeActivityStatus;
4214
+ }
4475
4215
  if (input.totalDebtAmount > 0n) {
4476
- return InstantLoanStatus.active;
4216
+ return createLiquidiumStatus({
4217
+ operation: "repayment",
4218
+ state: "active"
4219
+ });
4220
+ }
4221
+ if (input.started) {
4222
+ return createLiquidiumStatus({
4223
+ operation: "repayment",
4224
+ state: "completed"
4225
+ });
4226
+ }
4227
+ if (isInstantLoanDepositExpired(input)) {
4228
+ return createLiquidiumStatus({
4229
+ operation: "deposit",
4230
+ state: "expired"
4231
+ });
4477
4232
  }
4478
4233
  if (input.collateralAmount > 0n) {
4479
- return InstantLoanStatus.depositDetected;
4234
+ return createLiquidiumStatus({
4235
+ operation: "deposit",
4236
+ state: "processing"
4237
+ });
4238
+ }
4239
+ if (input.depositDetectedTimestamp !== null) {
4240
+ return createLiquidiumStatus({
4241
+ operation: "deposit",
4242
+ state: "confirming"
4243
+ });
4244
+ }
4245
+ return createLiquidiumStatus({
4246
+ operation: "deposit",
4247
+ state: "action_required"
4248
+ });
4249
+ }
4250
+ function deriveActiveInstantLoanActivityStatus(input) {
4251
+ if (!input.started) {
4252
+ return findActiveActivityStatus(input.activeActivities, "deposit");
4253
+ }
4254
+ const borrowStatus = findActiveActivityStatus(
4255
+ input.activeActivities,
4256
+ "borrow"
4257
+ );
4258
+ if (borrowStatus) {
4259
+ return borrowStatus;
4480
4260
  }
4481
- return InstantLoanStatus.awaitingDeposit;
4261
+ return findActiveActivityStatus(input.activeActivities, "repayment");
4262
+ }
4263
+ function findActiveActivityStatus(activities, operation) {
4264
+ const activity = activities.find(
4265
+ (candidate) => candidate.status.operation === operation
4266
+ );
4267
+ return activity?.status ?? null;
4268
+ }
4269
+ function isInstantLoanDepositExpired(input) {
4270
+ if (input.started || input.depositDetectedTimestamp === null) {
4271
+ return false;
4272
+ }
4273
+ if (input.expiryTimestamp === null) {
4274
+ return false;
4275
+ }
4276
+ return input.expiryTimestamp <= getCurrentUnixTimestampSeconds();
4277
+ }
4278
+ function getCurrentUnixTimestampSeconds() {
4279
+ return BigInt(Math.floor(Date.now() / MILLISECONDS_PER_SECOND3));
4482
4280
  }
4483
4281
  function deriveDepositExpiryTimestamp(input) {
4484
4282
  if (input.depositDetectedTimestamp === null) {
@@ -4927,6 +4725,60 @@ function accountTypeToString(accountType) {
4927
4725
  }
4928
4726
  }
4929
4727
 
4728
+ // src/core/withdraw-minimums.ts
4729
+ var MINIMUM_BTC_WITHDRAW_AMOUNT_SATS = 5000n;
4730
+ var MINIMUM_STABLECOIN_WITHDRAW_AMOUNT_BASE_UNITS = 1000000n;
4731
+ var MIN_WITHDRAW_AMOUNTS_BY_ASSET = {
4732
+ [Asset.BTC]: MINIMUM_BTC_WITHDRAW_AMOUNT_SATS,
4733
+ [Asset.USDC]: MINIMUM_STABLECOIN_WITHDRAW_AMOUNT_BASE_UNITS,
4734
+ [Asset.USDT]: MINIMUM_STABLECOIN_WITHDRAW_AMOUNT_BASE_UNITS
4735
+ };
4736
+ function getMinimumWithdrawAmount(asset) {
4737
+ if (!isMinimumWithdrawAsset(asset)) {
4738
+ return 0n;
4739
+ }
4740
+ return MIN_WITHDRAW_AMOUNTS_BY_ASSET[asset];
4741
+ }
4742
+ function getWithdrawAmountMinimumValidationError(params) {
4743
+ const minimumAmount = getMinimumWithdrawAmount(params.asset);
4744
+ if (minimumAmount <= 0n || params.amount >= minimumAmount) {
4745
+ return null;
4746
+ }
4747
+ return {
4748
+ asset: params.asset,
4749
+ minimumAmount,
4750
+ message: formatMinimumWithdrawAmountMessage(params.asset, minimumAmount)
4751
+ };
4752
+ }
4753
+ function formatMinimumWithdrawAmountMessage(asset, minimumAmount) {
4754
+ return `Withdraw amount must be at least ${minimumAmount} base units for ${asset}`;
4755
+ }
4756
+ function isMinimumWithdrawAsset(asset) {
4757
+ return Object.hasOwn(MIN_WITHDRAW_AMOUNTS_BY_ASSET, asset);
4758
+ }
4759
+
4760
+ // src/modules/lending/_internal/inflow-fee-rounding.ts
4761
+ var ETH_STABLECOIN_INFLOW_FEE_ROUND_UP_TO_NEAREST_10_CENTS = 100000n;
4762
+ var BTC_INFLOW_FEE_ROUND_UP_TO_NEAREST_SATS = 500n;
4763
+ function roundInflowFeeEstimate(request, totalFee) {
4764
+ if (totalFee <= 0n) {
4765
+ return 0n;
4766
+ }
4767
+ if (isEthStablecoin(request.asset, request.chain)) {
4768
+ return roundUpToNearest(
4769
+ totalFee,
4770
+ ETH_STABLECOIN_INFLOW_FEE_ROUND_UP_TO_NEAREST_10_CENTS
4771
+ );
4772
+ }
4773
+ if (request.asset === Asset.BTC && request.chain === Chain.BTC) {
4774
+ return roundUpToNearest(totalFee, BTC_INFLOW_FEE_ROUND_UP_TO_NEAREST_SATS);
4775
+ }
4776
+ return totalFee;
4777
+ }
4778
+ function roundUpToNearest(amount, roundingUnit) {
4779
+ return ceilDivBigint(amount, roundingUnit) * roundingUnit;
4780
+ }
4781
+
4930
4782
  // src/core/utils/retry.ts
4931
4783
  var MIN_ATTEMPTS = 1;
4932
4784
  var MIN_INITIAL_RETRY_DELAY_MS = 0;
@@ -4978,835 +4830,985 @@ function assertValidRetryOptions(options) {
4978
4830
  }
4979
4831
  }
4980
4832
 
4981
- // src/modules/lending/_internal/inflow-fee-rounding.ts
4982
- var ETH_STABLECOIN_INFLOW_FEE_ROUND_UP_TO_NEAREST_10_CENTS = 100000n;
4983
- var BTC_INFLOW_FEE_ROUND_UP_TO_NEAREST_SATS = 500n;
4984
- function roundInflowFeeEstimate(request, totalFee) {
4985
- if (totalFee <= 0n) {
4986
- return 0n;
4833
+ // src/modules/lending/_internal/supply-flow.ts
4834
+ var SUBMIT_INFLOW_MAX_ATTEMPTS = 4;
4835
+ var SUBMIT_INFLOW_INITIAL_RETRY_DELAY_MS = 1500;
4836
+ var SUBMIT_INFLOW_RETRY_BACKOFF_MULTIPLIER = 2;
4837
+ var ETH_APPROVAL_POLL_INTERVAL_MS = 2e3;
4838
+ var ETH_APPROVAL_MAX_POLLS = 30;
4839
+ var SupplyFlowExecutor = class {
4840
+ constructor(params) {
4841
+ this.params = params;
4987
4842
  }
4988
- if (isEthStablecoin(request.asset, request.chain)) {
4989
- return roundUpToNearest(
4990
- totalFee,
4991
- ETH_STABLECOIN_INFLOW_FEE_ROUND_UP_TO_NEAREST_10_CENTS
4843
+ params;
4844
+ async create(request) {
4845
+ const target = await resolveSupplyTarget(
4846
+ this.params.canisterContext,
4847
+ request
4992
4848
  );
4993
- }
4994
- if (request.asset === Asset.BTC && request.chain === Chain.BTC) {
4995
- return roundUpToNearest(totalFee, BTC_INFLOW_FEE_ROUND_UP_TO_NEAREST_SATS);
4996
- }
4997
- return totalFee;
4998
- }
4999
- function roundUpToNearest(amount, roundingUnit) {
5000
- return ceilDivBigint(amount, roundingUnit) * roundingUnit;
5001
- }
5002
- function mapCanisterOutflowDetails(outflow) {
5003
- const rawOutflowType = getVariantKey(outflow.outflow_type);
5004
- return {
5005
- id: outflow.id,
5006
- outflowType: normalizeOutflowType(rawOutflowType),
5007
- outflowRef: outflow.outflow_ref[0],
5008
- txid: outflow.txid[0],
5009
- amount: outflow.amount,
5010
- receiver: mapCanisterAccountType(outflow.receiver)
5011
- };
5012
- }
5013
- function mapCanisterAccountType(receiver) {
5014
- if ("Native" in receiver) {
5015
- return {
5016
- type: "Native",
5017
- account: receiver.Native.toText()
5018
- };
5019
- }
5020
- if ("AccountIdentifier" in receiver) {
5021
- return {
5022
- type: "AccountIdentifier",
5023
- account: receiver.AccountIdentifier
4849
+ const instruction = {
4850
+ poolId: request.poolId,
4851
+ asset: target.asset,
4852
+ chain: target.chain,
4853
+ action: request.action,
4854
+ target
5024
4855
  };
5025
- }
5026
- if ("Icrc" in receiver) {
5027
- const subaccount = normalizeOptionalSubaccount2(receiver.Icrc.subaccount[0]);
5028
- return {
5029
- type: "Icrc",
5030
- owner: receiver.Icrc.owner.toText(),
5031
- subaccount,
5032
- account: encodeIcrcAccount({
5033
- owner: receiver.Icrc.owner,
5034
- subaccount
5035
- })
4856
+ const mechanism = resolveSupplyMechanism({
4857
+ asset: instruction.asset,
4858
+ chain: instruction.chain,
4859
+ requestedMechanism: request.mechanism
4860
+ });
4861
+ const defaultSubmitInflowRequest = getDefaultSubmitInflowRequest({
4862
+ action: request.action,
4863
+ chain: mechanism === SupplyPlanType.contractInteraction ? Chain.ETH : instruction.chain
4864
+ });
4865
+ let txid;
4866
+ switch (mechanism) {
4867
+ case SupplyPlanType.transfer:
4868
+ if (request.walletAdapter) {
4869
+ txid = await this.sendAndSubmitNativeSupplyInflow({
4870
+ request,
4871
+ instruction,
4872
+ defaultSubmitInflowRequest
4873
+ });
4874
+ }
4875
+ break;
4876
+ case SupplyPlanType.contractInteraction:
4877
+ txid = await this.executeContractSupply({
4878
+ request,
4879
+ instruction,
4880
+ defaultSubmitInflowRequest
4881
+ });
4882
+ break;
4883
+ }
4884
+ return {
4885
+ type: mechanism,
4886
+ target: instruction.target,
4887
+ txid,
4888
+ status: createLiquidiumStatus({
4889
+ operation: mapSupplyActionToStatusOperation(request.action),
4890
+ state: txid ? "confirming" : "action_required"
4891
+ }),
4892
+ submit: async (submitRequest) => {
4893
+ return await this.submitSupplyFlowInflow({
4894
+ instruction,
4895
+ mechanism,
4896
+ defaultSubmitInflowRequest,
4897
+ submitRequest
4898
+ });
4899
+ }
5036
4900
  };
5037
4901
  }
5038
- return {
5039
- type: "External",
5040
- account: receiver.External
5041
- };
5042
- }
5043
- function normalizeOptionalSubaccount2(subaccount) {
5044
- if (!subaccount) {
5045
- return void 0;
4902
+ async getEvmSupplyContext(request) {
4903
+ const selectedPool = await this.params.getPoolById(request.poolId);
4904
+ return await this.getEvmSupplyContextForPool({
4905
+ request,
4906
+ asset: selectedPool.asset,
4907
+ chain: selectedPool.chain
4908
+ });
5046
4909
  }
5047
- return subaccount instanceof Uint8Array ? subaccount : Uint8Array.from(subaccount);
5048
- }
5049
- function normalizeOutflowType(rawOutflowType) {
5050
- switch (rawOutflowType) {
5051
- case "Withdraw":
5052
- return OutflowType.withdraw;
5053
- case "Borrow":
5054
- return OutflowType.borrow;
5055
- case "FeeClaim":
5056
- return OutflowType.feeClaim;
5057
- default:
4910
+ async getEvmSupplyContextForPool(params) {
4911
+ const { request, asset, chain } = params;
4912
+ const evmReadClient = this.requireEvmReadClient(
4913
+ "EVM supply context requires an EVM RPC URL or public client"
4914
+ );
4915
+ const walletAddress = normalizeAndValidateEvmAddress(
4916
+ request.walletAddress,
4917
+ "Invalid EVM wallet address"
4918
+ );
4919
+ if (request.amount <= 0n) {
5058
4920
  throw new LiquidiumError(
5059
- LiquidiumErrorCode.INTERNAL,
5060
- `Unsupported outflow type: ${rawOutflowType}`
4921
+ LiquidiumErrorCode.VALIDATION_ERROR,
4922
+ "EVM supply context requires a positive amount"
4923
+ );
4924
+ }
4925
+ if (!isEthStablecoin(asset, chain)) {
4926
+ throw new LiquidiumError(
4927
+ LiquidiumErrorCode.VALIDATION_ERROR,
4928
+ `EVM supply context only supports ETH stablecoin pools, received ${asset} on ${chain}`
5061
4929
  );
4930
+ }
4931
+ const tokenAddress = getEthStablecoinContractAddress(asset);
4932
+ const spenderAddress = CK_ETH_DEPOSIT_CONTRACT_ADDRESS;
4933
+ const [allowance, balance] = await Promise.all([
4934
+ readErc20Allowance({
4935
+ evmReadClient,
4936
+ tokenAddress,
4937
+ ownerAddress: walletAddress,
4938
+ spenderAddress
4939
+ }),
4940
+ readErc20Balance({
4941
+ evmReadClient,
4942
+ tokenAddress,
4943
+ ownerAddress: walletAddress
4944
+ })
4945
+ ]);
4946
+ const approvalStrategy = getApprovalStrategy({
4947
+ allowance,
4948
+ amount: request.amount
4949
+ });
4950
+ return {
4951
+ profileId: request.profileId,
4952
+ poolId: request.poolId,
4953
+ walletAddress,
4954
+ action: request.action,
4955
+ asset,
4956
+ chain: Chain.ETH,
4957
+ amount: request.amount.toString(),
4958
+ tokenAddress,
4959
+ spenderAddress,
4960
+ depositContractAddress: CK_ETH_DEPOSIT_CONTRACT_ADDRESS,
4961
+ balance: balance.toString(),
4962
+ allowance: allowance.toString(),
4963
+ requiresApproval: approvalStrategy !== EvmSupplyApprovalStrategy.none,
4964
+ approvalStrategy
4965
+ };
5062
4966
  }
5063
- }
5064
- function mapWalletChainToLendingChain(chain) {
5065
- switch (chain) {
5066
- case Chain.BTC:
5067
- return { BTC: null };
5068
- case Chain.ETH:
5069
- return { ETH: null };
4967
+ async sendAndSubmitNativeSupplyInflow(params) {
4968
+ const { request, instruction, defaultSubmitInflowRequest } = params;
4969
+ if (instruction.target.type !== "nativeAddress") {
4970
+ throw new LiquidiumError(
4971
+ LiquidiumErrorCode.VALIDATION_ERROR,
4972
+ "Wallet-executed supply requires a native-address target"
4973
+ );
4974
+ }
4975
+ if (!request.walletAdapter) {
4976
+ throw new LiquidiumError(
4977
+ LiquidiumErrorCode.VALIDATION_ERROR,
4978
+ "Wallet-executed supply requires a wallet adapter"
4979
+ );
4980
+ }
4981
+ const account = request.account?.trim();
4982
+ if (!account) {
4983
+ throw new LiquidiumError(
4984
+ LiquidiumErrorCode.VALIDATION_ERROR,
4985
+ "Wallet-executed transfer supply requires an account"
4986
+ );
4987
+ }
4988
+ if (!request.amount || request.amount <= 0n) {
4989
+ throw new LiquidiumError(
4990
+ LiquidiumErrorCode.VALIDATION_ERROR,
4991
+ "Wallet-executed supply requires a positive amount"
4992
+ );
4993
+ }
4994
+ const txid = await this.sendNativeSupplyTransaction({
4995
+ walletAdapter: request.walletAdapter,
4996
+ chain: instruction.chain,
4997
+ toAddress: instruction.target.address,
4998
+ amount: request.amount,
4999
+ senderAccount: account,
5000
+ asset: instruction.asset,
5001
+ action: request.action
5002
+ });
5003
+ if (shouldSubmitInflow({ instruction, mechanism: SupplyPlanType.transfer })) {
5004
+ try {
5005
+ await this.submitInflowWithRetry(txid, defaultSubmitInflowRequest);
5006
+ } catch {
5007
+ }
5008
+ }
5009
+ return txid;
5070
5010
  }
5071
- }
5072
-
5073
- // src/modules/lending/lending.ts
5074
- var SUBMIT_INFLOW_MAX_ATTEMPTS = 4;
5075
- var SUBMIT_INFLOW_INITIAL_RETRY_DELAY_MS = 1500;
5076
- var SUBMIT_INFLOW_RETRY_BACKOFF_MULTIPLIER = 2;
5077
- var ETH_APPROVAL_POLL_INTERVAL_MS = 2e3;
5078
- var ETH_APPROVAL_MAX_POLLS = 30;
5079
- var LendingModule = class {
5080
- constructor(canisterContext, apiClient, evmReadClient) {
5081
- this.canisterContext = canisterContext;
5082
- this.apiClient = apiClient;
5083
- this.evmReadClient = evmReadClient;
5011
+ async submitSupplyFlowInflow(params) {
5012
+ if (!shouldSubmitInflow(params)) {
5013
+ return {
5014
+ txid: params.submitRequest.txid
5015
+ };
5016
+ }
5017
+ const defaultSubmitInflowRequest = params.defaultSubmitInflowRequest;
5018
+ if (!defaultSubmitInflowRequest) {
5019
+ throw new LiquidiumError(
5020
+ LiquidiumErrorCode.VALIDATION_ERROR,
5021
+ "Supply flow submit requires an inflow operation"
5022
+ );
5023
+ }
5024
+ return await this.params.submitInflow({
5025
+ ...defaultSubmitInflowRequest,
5026
+ ...params.submitRequest
5027
+ });
5084
5028
  }
5085
- canisterContext;
5086
- apiClient;
5087
- evmReadClient;
5088
- /**
5089
- * Prepares a withdraw action that can be signed and submitted later.
5090
- *
5091
- * Use this when you need explicit control over signing and submission.
5092
- *
5093
- * @param request - Profile, pool, amount (pool asset base units), outflow address, and signer wallet.
5094
- * @returns A signable {@link WithdrawAction} with `submit` wired to the canister.
5095
- */
5096
- async prepareWithdraw(request) {
5097
- const destinationAccount = request.receiverAddress.trim();
5098
- const signerAccount = normalizeEvmAddress(
5099
- request.signerWalletAddress.trim()
5029
+ async executeContractSupply(params) {
5030
+ const { request, instruction, defaultSubmitInflowRequest } = params;
5031
+ if (instruction.target.type !== "icrcAccount" || instruction.chain !== Chain.ETH) {
5032
+ throw new LiquidiumError(
5033
+ LiquidiumErrorCode.VALIDATION_ERROR,
5034
+ "Contract-interaction supply is only supported for ETH ICRC-account targets"
5035
+ );
5036
+ }
5037
+ const walletAddressInput = request.account?.trim();
5038
+ if (!walletAddressInput) {
5039
+ throw new LiquidiumError(
5040
+ LiquidiumErrorCode.VALIDATION_ERROR,
5041
+ "Contract-interaction supply requires an account"
5042
+ );
5043
+ }
5044
+ const walletAddress = normalizeAndValidateEvmAddress(
5045
+ walletAddressInput,
5046
+ "Invalid EVM wallet address"
5100
5047
  );
5101
- if (!destinationAccount) {
5048
+ if (!request.amount || request.amount <= 0n) {
5102
5049
  throw new LiquidiumError(
5103
5050
  LiquidiumErrorCode.VALIDATION_ERROR,
5104
- "Withdraw requires a custom outflow account"
5051
+ "Contract-interaction supply requires a positive amount"
5105
5052
  );
5106
5053
  }
5107
- if (!signerAccount) {
5054
+ const walletAdapter = request.walletAdapter;
5055
+ if (!walletAdapter?.sendEthTransaction) {
5108
5056
  throw new LiquidiumError(
5109
5057
  LiquidiumErrorCode.VALIDATION_ERROR,
5110
- "Withdraw requires a signer account"
5058
+ "Contract-interaction supply requires an ETH wallet adapter"
5111
5059
  );
5112
5060
  }
5113
- const receiverAddress = await this.normalizeOutflowReceiverAddress({
5114
- poolId: request.poolId,
5115
- receiverAddress: destinationAccount
5116
- });
5117
- const lendingActor = createLendingActor(this.canisterContext);
5118
- try {
5119
- const expiryTimestamp = computeExpiryTimestampFromNow();
5120
- const nonce = await lendingActor.get_nonce(signerAccount);
5121
- const withdrawRequestData = {
5061
+ this.params.requireApi();
5062
+ this.requireEvmReadClient(
5063
+ "Contract-interaction supply requires an EVM RPC URL or public client"
5064
+ );
5065
+ const evmSupplyContext = await this.getEvmSupplyContextForPool({
5066
+ request: {
5122
5067
  profileId: request.profileId,
5123
5068
  poolId: request.poolId,
5069
+ walletAddress,
5124
5070
  amount: request.amount,
5125
- receiverAddress,
5126
- signerWalletAddress: signerAccount,
5127
- expiryTimestamp
5128
- };
5129
- return {
5130
- kind: WalletActionKind.createWithdraw,
5131
- executionKind: WalletExecutionKind.signMessage,
5132
- actionType: WalletActionKind.createWithdraw,
5133
- transferMode: TransferMode.native,
5134
- account: signerAccount,
5135
- message: createWithdrawAssetMessage(
5136
- {
5137
- pool_id: request.poolId,
5138
- amount: request.amount.toString(),
5139
- account: { type: "External", data: receiverAddress },
5140
- expiry_timestamp: expiryTimestamp
5141
- },
5142
- nonce
5143
- ),
5144
- data: withdrawRequestData,
5145
- submit: async (signatureInfo) => {
5146
- return await this.submitWithdraw(withdrawRequestData, signatureInfo);
5147
- }
5148
- };
5149
- } catch (error) {
5150
- if (error instanceof LiquidiumError) {
5151
- throw error;
5152
- }
5153
- throw mapCanisterCallErrorToLiquidiumError("get_nonce", error);
5071
+ action: request.action
5072
+ },
5073
+ asset: instruction.asset,
5074
+ chain: instruction.chain
5075
+ });
5076
+ const supplyAmount = BigInt(evmSupplyContext.amount);
5077
+ if (BigInt(evmSupplyContext.balance) < supplyAmount) {
5078
+ throw new LiquidiumError(
5079
+ LiquidiumErrorCode.INSUFFICIENT_FUNDS,
5080
+ `Insufficient ${evmSupplyContext.asset} balance for ${request.action}`
5081
+ );
5154
5082
  }
5155
- }
5156
- async submitWithdraw(request, signatureInfo) {
5157
- try {
5158
- const result = await createLendingActor(this.canisterContext).withdraw(
5159
- Principal.fromText(request.profileId),
5160
- {
5161
- data: {
5162
- expiry_timestamp: request.expiryTimestamp,
5163
- account: { External: request.receiverAddress },
5164
- pool_id: Principal.fromText(request.poolId),
5165
- amount: request.amount
5166
- },
5167
- signature_info: {
5168
- Wallet: {
5169
- signature: normalizeWalletSignature(
5170
- signatureInfo.signature,
5171
- signatureInfo.chain
5172
- ),
5173
- chain: mapWalletChainToLendingChain(signatureInfo.chain),
5174
- account: request.signerWalletAddress
5175
- }
5176
- }
5177
- }
5083
+ if (evmSupplyContext.approvalStrategy === EvmSupplyApprovalStrategy.resetThenApproveMax) {
5084
+ await this.sendEthContractTransaction(
5085
+ walletAdapter,
5086
+ walletAddress,
5087
+ createApproveTransaction({
5088
+ tokenAddress: evmSupplyContext.tokenAddress,
5089
+ spenderAddress: evmSupplyContext.spenderAddress,
5090
+ amount: 0n
5091
+ }),
5092
+ `supply-${request.action}-approve-reset`
5178
5093
  );
5179
- if ("Err" in result) {
5180
- throw mapLendingProtocolErrorToLiquidiumError(result.Err);
5181
- }
5182
- return mapExpectedOutflowDetails(
5183
- mapCanisterOutflowDetails(result.Ok),
5184
- OutflowType.withdraw,
5185
- "withdraw"
5094
+ await this.waitForExpectedAllowance({
5095
+ walletAddress,
5096
+ tokenAddress: evmSupplyContext.tokenAddress,
5097
+ spenderAddress: evmSupplyContext.spenderAddress,
5098
+ amount: supplyAmount,
5099
+ expectation: "zero"
5100
+ });
5101
+ }
5102
+ if (evmSupplyContext.approvalStrategy !== EvmSupplyApprovalStrategy.none) {
5103
+ await this.sendEthContractTransaction(
5104
+ walletAdapter,
5105
+ walletAddress,
5106
+ createApproveTransaction({
5107
+ tokenAddress: evmSupplyContext.tokenAddress,
5108
+ spenderAddress: evmSupplyContext.spenderAddress,
5109
+ amount: MAX_UINT256
5110
+ }),
5111
+ `supply-${request.action}-approve-max`
5186
5112
  );
5187
- } catch (error) {
5188
- if (error instanceof LiquidiumError) {
5189
- throw error;
5190
- }
5191
- throw mapCanisterCallErrorToLiquidiumError("withdraw", error);
5113
+ await this.waitForExpectedAllowance({
5114
+ walletAddress,
5115
+ tokenAddress: evmSupplyContext.tokenAddress,
5116
+ spenderAddress: evmSupplyContext.spenderAddress,
5117
+ amount: supplyAmount,
5118
+ expectation: "sufficient"
5119
+ });
5192
5120
  }
5193
- }
5194
- /**
5195
- * Creates a withdraw outflow using the provided wallet adapter.
5196
- *
5197
- * This is the convenience form of `prepareWithdraw(...)` plus execution.
5198
- *
5199
- * @param params - Withdraw request fields plus `signerChain` and `signerWalletAdapter`.
5200
- * @returns The canister {@link OutflowDetails} for the completed withdraw.
5201
- */
5202
- async withdraw(params) {
5203
- const action = await this.prepareWithdraw(params);
5204
- return await executeWith({
5205
- walletAdapter: params.signerWalletAdapter,
5206
- chain: params.signerChain,
5207
- account: action.account
5208
- })(action);
5209
- }
5210
- /**
5211
- * Prepares a borrow action that can be signed and submitted later.
5212
- *
5213
- * Use this when you need explicit control over signing and submission.
5214
- *
5215
- * @param request - Profile, pool, amount (borrow asset base units), outflow address, and signer wallet.
5216
- * @returns A signable {@link BorrowAction} with `submit` wired to the canister.
5217
- */
5218
- async prepareBorrow(request) {
5219
- const destinationAccount = request.receiverAddress.trim();
5220
- const signerAccount = normalizeEvmAddress(
5221
- request.signerWalletAddress.trim()
5121
+ const depositTxid = await this.sendEthContractTransaction(
5122
+ walletAdapter,
5123
+ walletAddress,
5124
+ createDepositErc20Transaction({
5125
+ depositContractAddress: evmSupplyContext.depositContractAddress,
5126
+ tokenAddress: evmSupplyContext.tokenAddress,
5127
+ amount: supplyAmount,
5128
+ poolId: request.poolId,
5129
+ profileId: request.profileId,
5130
+ destinationAccount: instruction.target.account,
5131
+ action: request.action
5132
+ }),
5133
+ `supply-${request.action}-deposit-erc20`
5222
5134
  );
5223
- if (!destinationAccount) {
5224
- throw new LiquidiumError(
5225
- LiquidiumErrorCode.VALIDATION_ERROR,
5226
- "Borrow requires a custom outflow account"
5227
- );
5135
+ try {
5136
+ await this.submitInflowWithRetry(depositTxid, defaultSubmitInflowRequest);
5137
+ } catch {
5228
5138
  }
5229
- if (!signerAccount) {
5230
- throw new LiquidiumError(
5231
- LiquidiumErrorCode.VALIDATION_ERROR,
5232
- "Borrow requires a signer account"
5233
- );
5139
+ return depositTxid;
5140
+ }
5141
+ async sendNativeSupplyTransaction(params) {
5142
+ switch (params.chain) {
5143
+ case Chain.BTC: {
5144
+ if (!params.walletAdapter.sendBtcTransaction) {
5145
+ throw new LiquidiumError(
5146
+ LiquidiumErrorCode.VALIDATION_ERROR,
5147
+ "BTC wallet adapter does not support transaction sending"
5148
+ );
5149
+ }
5150
+ return await params.walletAdapter.sendBtcTransaction({
5151
+ chain: Chain.BTC,
5152
+ toAddress: params.toAddress,
5153
+ amountSats: params.amount,
5154
+ account: params.senderAccount,
5155
+ actionType: `supply-${params.action}`,
5156
+ transferMode: TransferMode.native
5157
+ });
5158
+ }
5159
+ case Chain.ETH: {
5160
+ if (!params.walletAdapter.sendEthTransaction) {
5161
+ throw new LiquidiumError(
5162
+ LiquidiumErrorCode.VALIDATION_ERROR,
5163
+ "ETH wallet adapter does not support transaction sending"
5164
+ );
5165
+ }
5166
+ if (isEthStablecoin(params.asset, params.chain)) {
5167
+ return await params.walletAdapter.sendEthTransaction({
5168
+ chain: Chain.ETH,
5169
+ account: params.senderAccount,
5170
+ actionType: `supply-${params.action}`,
5171
+ transferMode: TransferMode.native,
5172
+ transaction: createTransferErc20Transaction({
5173
+ tokenAddress: getEthStablecoinContractAddress(params.asset),
5174
+ recipientAddress: params.toAddress,
5175
+ amount: params.amount
5176
+ })
5177
+ });
5178
+ }
5179
+ return await params.walletAdapter.sendEthTransaction({
5180
+ chain: Chain.ETH,
5181
+ account: params.senderAccount,
5182
+ actionType: `supply-${params.action}`,
5183
+ transferMode: TransferMode.native,
5184
+ transaction: {
5185
+ to: params.toAddress,
5186
+ value: params.amount.toString()
5187
+ }
5188
+ });
5189
+ }
5190
+ default:
5191
+ throw new LiquidiumError(
5192
+ LiquidiumErrorCode.VALIDATION_ERROR,
5193
+ `Native-address wallet execution is not supported for ${params.chain}`
5194
+ );
5234
5195
  }
5235
- if (request.amount <= 0n) {
5196
+ }
5197
+ async submitInflowWithRetry(txid, extraRequest) {
5198
+ if (!extraRequest) {
5236
5199
  throw new LiquidiumError(
5237
5200
  LiquidiumErrorCode.VALIDATION_ERROR,
5238
- "Borrow amount must be greater than 0"
5201
+ "Inflow submission requires an operation"
5239
5202
  );
5240
5203
  }
5241
- const selectedPool = await this.getPoolById(request.poolId);
5242
- const selectedAsset = selectedPool.asset;
5243
- const minimumBorrowAmountError = getBorrowAmountMinimumValidationError({
5244
- amount: request.amount,
5245
- asset: selectedAsset
5204
+ await retryWithBackoff({
5205
+ execute: async () => {
5206
+ await this.params.submitInflow({ txid, ...extraRequest });
5207
+ },
5208
+ maxAttempts: SUBMIT_INFLOW_MAX_ATTEMPTS,
5209
+ initialRetryDelayMs: SUBMIT_INFLOW_INITIAL_RETRY_DELAY_MS,
5210
+ backoffMultiplier: SUBMIT_INFLOW_RETRY_BACKOFF_MULTIPLIER,
5211
+ shouldRetryError: isRetriableInflowSubmitError
5246
5212
  });
5247
- if (minimumBorrowAmountError) {
5213
+ }
5214
+ async sendEthContractTransaction(walletAdapter, walletAddress, request, actionType) {
5215
+ if (!walletAdapter.sendEthTransaction) {
5248
5216
  throw new LiquidiumError(
5249
5217
  LiquidiumErrorCode.VALIDATION_ERROR,
5250
- minimumBorrowAmountError.message
5218
+ "ETH wallet adapter does not support transaction sending"
5251
5219
  );
5252
5220
  }
5253
- const receiverAddress = normalizeExternalAddress({
5254
- address: destinationAccount,
5255
- asset: selectedAsset,
5256
- chain: selectedPool.chain
5221
+ return await walletAdapter.sendEthTransaction({
5222
+ chain: Chain.ETH,
5223
+ account: walletAddress,
5224
+ actionType,
5225
+ transferMode: TransferMode.native,
5226
+ transaction: request
5257
5227
  });
5258
- const lendingActor = createLendingActor(this.canisterContext);
5259
- try {
5260
- const expiryTimestamp = computeExpiryTimestampFromNow();
5261
- const nonce = await lendingActor.get_nonce(signerAccount);
5262
- const borrowRequestData = {
5263
- profileId: request.profileId,
5264
- poolId: request.poolId,
5265
- amount: request.amount,
5266
- receiverAddress,
5267
- signerWalletAddress: signerAccount,
5268
- expiryTimestamp
5269
- };
5270
- return {
5271
- kind: WalletActionKind.createBorrow,
5272
- executionKind: WalletExecutionKind.signMessage,
5273
- actionType: WalletActionKind.createBorrow,
5274
- transferMode: TransferMode.native,
5275
- account: signerAccount,
5276
- message: createBorrowAssetMessage(
5277
- {
5278
- pool_id: request.poolId,
5279
- amount: request.amount.toString(),
5280
- account: { type: "External", data: receiverAddress },
5281
- expiry_timestamp: expiryTimestamp
5282
- },
5283
- nonce
5284
- ),
5285
- data: borrowRequestData,
5286
- submit: async (signatureInfo) => {
5287
- return await this.submitBorrow(borrowRequestData, signatureInfo);
5228
+ }
5229
+ async waitForExpectedAllowance(params) {
5230
+ const evmReadClient = this.requireEvmReadClient(
5231
+ "Contract-interaction supply requires an EVM RPC URL or public client"
5232
+ );
5233
+ let lastPollingError;
5234
+ for (let pollIndex = 0; pollIndex < ETH_APPROVAL_MAX_POLLS; pollIndex += 1) {
5235
+ try {
5236
+ const allowance = await readErc20Allowance({
5237
+ evmReadClient,
5238
+ tokenAddress: params.tokenAddress,
5239
+ ownerAddress: params.walletAddress,
5240
+ spenderAddress: params.spenderAddress
5241
+ });
5242
+ const matchesExpectation = params.expectation === "zero" ? allowance === 0n : allowance >= params.amount;
5243
+ if (matchesExpectation) {
5244
+ return;
5288
5245
  }
5289
- };
5290
- } catch (error) {
5291
- if (error instanceof LiquidiumError) {
5292
- throw error;
5246
+ } catch (error) {
5247
+ lastPollingError = error;
5293
5248
  }
5294
- throw mapCanisterCallErrorToLiquidiumError("get_nonce", error);
5249
+ await delay(ETH_APPROVAL_POLL_INTERVAL_MS);
5295
5250
  }
5251
+ const lastPollingErrorMessage = getUnknownErrorMessage(lastPollingError);
5252
+ throw new LiquidiumError(
5253
+ LiquidiumErrorCode.SERVICE_UNAVAILABLE,
5254
+ lastPollingErrorMessage ? `Timed out waiting for ${params.expectation} ETH allowance update. Last polling error: ${lastPollingErrorMessage}` : `Timed out waiting for ${params.expectation} ETH allowance update`
5255
+ );
5296
5256
  }
5297
- async submitBorrow(request, signatureInfo) {
5298
- try {
5299
- const result = await createLendingActor(
5300
- this.canisterContext
5301
- ).borrow_assets(Principal.fromText(request.profileId), {
5302
- data: {
5303
- expiry_timestamp: request.expiryTimestamp,
5304
- account: { External: request.receiverAddress },
5305
- pool_id: Principal.fromText(request.poolId),
5306
- amount: request.amount
5307
- },
5308
- signature_info: {
5309
- Wallet: {
5310
- signature: normalizeWalletSignature(
5311
- signatureInfo.signature,
5312
- signatureInfo.chain
5313
- ),
5314
- chain: mapWalletChainToLendingChain(signatureInfo.chain),
5315
- account: request.signerWalletAddress
5316
- }
5317
- }
5318
- });
5319
- if ("Err" in result) {
5320
- throw mapLendingProtocolErrorToLiquidiumError(result.Err);
5321
- }
5322
- return mapExpectedOutflowDetails(
5323
- mapCanisterOutflowDetails(result.Ok),
5324
- OutflowType.borrow,
5325
- "borrow_assets"
5326
- );
5327
- } catch (error) {
5328
- if (error instanceof LiquidiumError) {
5329
- throw error;
5330
- }
5331
- throw mapCanisterCallErrorToLiquidiumError("borrow_assets", error);
5257
+ requireEvmReadClient(message) {
5258
+ if (!this.params.evmReadClient) {
5259
+ throw new LiquidiumError(LiquidiumErrorCode.VALIDATION_ERROR, message);
5332
5260
  }
5261
+ return this.params.evmReadClient;
5333
5262
  }
5334
- /**
5335
- * Creates a borrow outflow using the provided wallet adapter.
5336
- *
5337
- * This is the convenience form of `prepareBorrow(...)` plus execution.
5338
- *
5339
- * @param params - Borrow request fields plus `signerChain` and `signerWalletAdapter`.
5340
- * @returns The lending canister receipt as {@link OutflowDetails}.
5341
- *
5342
- * @remarks
5343
- * `id` is always present. `txid` may be missing on the first response; the SDK does not
5344
- * poll for it. Use history or app-level polling if you need the chain transaction id.
5345
- */
5346
- async borrow(params) {
5347
- const action = await this.prepareBorrow(params);
5348
- return await executeWith({
5349
- walletAdapter: params.signerWalletAdapter,
5350
- chain: params.signerChain,
5351
- account: action.account
5352
- })(action);
5263
+ };
5264
+ async function readErc20Allowance(params) {
5265
+ const allowance = await params.evmReadClient.readContract({
5266
+ address: params.tokenAddress,
5267
+ abi: ERC20_ABI,
5268
+ functionName: "allowance",
5269
+ args: [
5270
+ params.ownerAddress,
5271
+ params.spenderAddress
5272
+ ]
5273
+ });
5274
+ return BigInt(allowance);
5275
+ }
5276
+ async function readErc20Balance(params) {
5277
+ const balance = await params.evmReadClient.readContract({
5278
+ address: params.tokenAddress,
5279
+ abi: ERC20_ABI,
5280
+ functionName: "balanceOf",
5281
+ args: [params.ownerAddress]
5282
+ });
5283
+ return BigInt(balance);
5284
+ }
5285
+ function getApprovalStrategy(params) {
5286
+ if (params.allowance >= params.amount) {
5287
+ return EvmSupplyApprovalStrategy.none;
5353
5288
  }
5354
- /**
5355
- * Resolves a supply target for a deposit or repayment and optionally broadcasts it.
5356
- *
5357
- * Transfer mode can return manual broadcast instructions when wallet fields are
5358
- * omitted. Contract-interaction mode always requires `walletAdapter`, `account`,
5359
- * and `amount` because it must prepare and submit approval/deposit calls.
5360
- *
5361
- * The SDK does not poll for inflow status. When a `txid` is returned, it is the
5362
- * caller's responsibility to track confirmation state using their own polling.
5363
- *
5364
- * @returns A {@link SupplyFlow} receipt with `type`, `target`, `submit`, and
5365
- * an optional `txid` present when the SDK broadcast for you.
5366
- */
5367
- async supply(request) {
5368
- const target = await resolveSupplyTarget(this.canisterContext, request);
5369
- const instruction = {
5370
- poolId: request.poolId,
5371
- asset: target.asset,
5372
- chain: target.chain,
5373
- action: request.action,
5374
- target
5375
- };
5376
- const mechanism = resolveSupplyMechanism({
5377
- asset: instruction.asset,
5378
- chain: instruction.chain,
5379
- requestedMechanism: request.mechanism
5380
- });
5381
- const defaultSubmitInflowRequest = getDefaultSubmitInflowRequest({
5382
- action: request.action,
5383
- chain: mechanism === SupplyPlanType.contractInteraction ? Chain.ETH : instruction.chain
5384
- });
5385
- let txid;
5386
- switch (mechanism) {
5387
- case SupplyPlanType.transfer:
5388
- if (request.walletAdapter) {
5389
- txid = await this.sendAndSubmitNativeSupplyInflow({
5390
- request,
5391
- instruction,
5392
- defaultSubmitInflowRequest
5393
- });
5394
- }
5395
- break;
5396
- case SupplyPlanType.contractInteraction:
5397
- txid = await this.executeContractSupply({
5398
- request,
5399
- instruction,
5400
- defaultSubmitInflowRequest
5401
- });
5402
- break;
5403
- }
5404
- return {
5405
- type: mechanism,
5406
- target: instruction.target,
5407
- txid,
5408
- submit: async (submitRequest) => {
5409
- return await this.submitSupplyFlowInflow({
5410
- instruction,
5411
- mechanism,
5412
- defaultSubmitInflowRequest,
5413
- submitRequest
5414
- });
5415
- }
5416
- };
5289
+ if (params.allowance === 0n) {
5290
+ return EvmSupplyApprovalStrategy.approveMax;
5417
5291
  }
5418
- /**
5419
- * Fetches ERC-20 supply planning data with the configured EVM read client.
5420
- *
5421
- * Requires `evmRpcUrl` or `evmPublicClient` on the client. Used internally by
5422
- * contract-interaction `supply`.
5423
- *
5424
- * @param request - Profile, pool, wallet, amount (token base units), and action.
5425
- * @returns Locally computed {@link EvmSupplyContext} for approvals and deposit.
5426
- */
5427
- async getEvmSupplyContext(request) {
5428
- const selectedPool = await this.getPoolById(request.poolId);
5429
- return await this.getEvmSupplyContextForPool({
5430
- request,
5431
- asset: selectedPool.asset,
5432
- chain: selectedPool.chain
5433
- });
5292
+ return EvmSupplyApprovalStrategy.resetThenApproveMax;
5293
+ }
5294
+ async function delay(timeoutMs) {
5295
+ await new Promise((resolve) => setTimeout(resolve, timeoutMs));
5296
+ }
5297
+ function isRetriableInflowSubmitError(error) {
5298
+ if (!(error instanceof LiquidiumError)) {
5299
+ return false;
5434
5300
  }
5435
- async getEvmSupplyContextForPool(params) {
5436
- const { request, asset, chain } = params;
5437
- const evmReadClient = this.requireEvmReadClient(
5438
- "EVM supply context requires an EVM RPC URL or public client"
5439
- );
5440
- const walletAddress = normalizeAndValidateEvmAddress(
5441
- request.walletAddress,
5442
- "Invalid EVM wallet address"
5443
- );
5444
- if (request.amount <= 0n) {
5445
- throw new LiquidiumError(
5446
- LiquidiumErrorCode.VALIDATION_ERROR,
5447
- "EVM supply context requires a positive amount"
5448
- );
5449
- }
5450
- if (!isEthStablecoin(asset, chain)) {
5451
- throw new LiquidiumError(
5452
- LiquidiumErrorCode.VALIDATION_ERROR,
5453
- `EVM supply context only supports ETH stablecoin pools, received ${asset} on ${chain}`
5454
- );
5455
- }
5456
- const tokenAddress = getEthStablecoinContractAddress(asset);
5457
- const spenderAddress = CK_ETH_DEPOSIT_CONTRACT_ADDRESS;
5458
- const [allowance, balance] = await Promise.all([
5459
- readErc20Allowance({
5460
- evmReadClient,
5461
- tokenAddress,
5462
- ownerAddress: walletAddress,
5463
- spenderAddress
5464
- }),
5465
- readErc20Balance({
5466
- evmReadClient,
5467
- tokenAddress,
5468
- ownerAddress: walletAddress
5469
- })
5470
- ]);
5471
- const approvalStrategy = getApprovalStrategy({
5472
- allowance,
5473
- amount: request.amount
5474
- });
5301
+ return error.code === LiquidiumErrorCode.SERVICE_UNAVAILABLE;
5302
+ }
5303
+ function getUnknownErrorMessage(error) {
5304
+ if (error instanceof Error) {
5305
+ return error.message;
5306
+ }
5307
+ if (typeof error === "string") {
5308
+ return error;
5309
+ }
5310
+ return null;
5311
+ }
5312
+ function getDefaultSubmitInflowRequest(params) {
5313
+ if (params.chain !== Chain.BTC && params.chain !== Chain.ETH) {
5314
+ return void 0;
5315
+ }
5316
+ return {
5317
+ chain: params.chain,
5318
+ operation: mapSupplyActionToStatusOperation(params.action)
5319
+ };
5320
+ }
5321
+ function mapSupplyActionToStatusOperation(action) {
5322
+ return action === SupplyAction.repayment ? "repayment" : "deposit";
5323
+ }
5324
+ function shouldSubmitInflow(params) {
5325
+ if (params.mechanism !== SupplyPlanType.transfer) {
5326
+ return true;
5327
+ }
5328
+ return !isEthStablecoin(params.instruction.asset, params.instruction.chain);
5329
+ }
5330
+ function mapCanisterOutflowDetails(outflow) {
5331
+ const rawOutflowType = getVariantKey(outflow.outflow_type);
5332
+ return {
5333
+ id: outflow.id,
5334
+ outflowType: normalizeOutflowType(rawOutflowType),
5335
+ outflowRef: outflow.outflow_ref[0],
5336
+ txid: outflow.txid[0],
5337
+ amount: outflow.amount,
5338
+ receiver: mapCanisterAccountType(outflow.receiver)
5339
+ };
5340
+ }
5341
+ function mapCanisterAccountType(receiver) {
5342
+ if ("Native" in receiver) {
5475
5343
  return {
5476
- success: true,
5477
- profileId: request.profileId,
5478
- poolId: request.poolId,
5479
- walletAddress,
5480
- action: request.action,
5481
- asset,
5482
- chain: Chain.ETH,
5483
- amount: request.amount.toString(),
5484
- tokenAddress,
5485
- spenderAddress,
5486
- depositContractAddress: CK_ETH_DEPOSIT_CONTRACT_ADDRESS,
5487
- balance: balance.toString(),
5488
- allowance: allowance.toString(),
5489
- requiresApproval: approvalStrategy !== EvmSupplyApprovalStrategy.none,
5490
- approvalStrategy
5344
+ type: "Native",
5345
+ account: receiver.Native.toText()
5491
5346
  };
5492
5347
  }
5493
- /**
5494
- * Returns the read-only deposit address for an ETH stablecoin inflow target.
5495
- *
5496
- * This is a query call that does not create or mutate state. Use it when you
5497
- * need the deposit address without hitting the authorization-gated update path.
5498
- *
5499
- * @param request - Profile, pool, asset, and supply action.
5500
- * @returns The EVM deposit address for the derived account.
5501
- */
5502
- async getDepositAddress(request) {
5503
- if (!isEthStablecoin(request.asset, Chain.ETH)) {
5348
+ if ("AccountIdentifier" in receiver) {
5349
+ return {
5350
+ type: "AccountIdentifier",
5351
+ account: receiver.AccountIdentifier
5352
+ };
5353
+ }
5354
+ if ("Icrc" in receiver) {
5355
+ const subaccount = normalizeOptionalSubaccount2(receiver.Icrc.subaccount[0]);
5356
+ return {
5357
+ type: "Icrc",
5358
+ owner: receiver.Icrc.owner.toText(),
5359
+ subaccount,
5360
+ account: encodeIcrcAccount({
5361
+ owner: receiver.Icrc.owner,
5362
+ subaccount
5363
+ })
5364
+ };
5365
+ }
5366
+ return {
5367
+ type: "External",
5368
+ account: receiver.External
5369
+ };
5370
+ }
5371
+ function normalizeOptionalSubaccount2(subaccount) {
5372
+ if (!subaccount) {
5373
+ return void 0;
5374
+ }
5375
+ return subaccount instanceof Uint8Array ? subaccount : Uint8Array.from(subaccount);
5376
+ }
5377
+ function normalizeOutflowType(rawOutflowType) {
5378
+ switch (rawOutflowType) {
5379
+ case "Withdraw":
5380
+ return OutflowType.withdrawal;
5381
+ case "Borrow":
5382
+ return OutflowType.borrow;
5383
+ case "FeeClaim":
5384
+ return OutflowType.feeClaim;
5385
+ default:
5504
5386
  throw new LiquidiumError(
5505
- LiquidiumErrorCode.VALIDATION_ERROR,
5506
- "getDepositAddress is only supported for ETH stablecoins"
5387
+ LiquidiumErrorCode.INTERNAL,
5388
+ `Unsupported outflow type: ${rawOutflowType}`
5507
5389
  );
5508
- }
5509
- const tokenAddress = getEthStablecoinContractAddress(request.asset);
5510
- const subaccount = encodeInflowSubaccount({
5511
- action: request.action,
5512
- principal: Principal.fromText(request.profileId)
5513
- });
5514
- const result = await createDepositAccountsActor(
5515
- this.canisterContext
5516
- ).get_deposit_address(
5517
- {
5518
- owner: Principal.fromText(request.poolId),
5519
- subaccount: [subaccount]
5520
- },
5521
- [tokenAddress]
5522
- );
5523
- if ("Err" in result) {
5524
- throw mapDepositAccountErrorToLiquidiumError(result.Err);
5525
- }
5526
- return result.Ok;
5527
5390
  }
5391
+ }
5392
+ function mapWalletChainToLendingChain(chain) {
5393
+ switch (chain) {
5394
+ case Chain.BTC:
5395
+ return { BTC: null };
5396
+ case Chain.ETH:
5397
+ return { ETH: null };
5398
+ }
5399
+ }
5400
+
5401
+ // src/modules/lending/lending.ts
5402
+ var LendingModule = class {
5403
+ constructor(canisterContext, apiClient, evmReadClient) {
5404
+ this.canisterContext = canisterContext;
5405
+ this.apiClient = apiClient;
5406
+ this.evmReadClient = evmReadClient;
5407
+ }
5408
+ canisterContext;
5409
+ apiClient;
5410
+ evmReadClient;
5528
5411
  /**
5529
- * Estimates the network/deposit fee for an inflow target.
5412
+ * Prepares a withdraw action that can be signed and submitted later.
5530
5413
  *
5531
- * ETH stablecoin deposit-address estimates are served by the deposit-address
5532
- * canister. BTC estimates include the ckBTC minter deposit fee and ledger fee.
5414
+ * Use this when you need explicit control over signing and submission.
5533
5415
  *
5534
- * @param request - Asset and chain pair to estimate for.
5535
- * @returns Total fee estimate rounded up in the asset's base units.
5416
+ * @param request - Profile, pool, amount (pool asset base units), outflow address, and signer wallet.
5417
+ * @returns A signable {@link WithdrawAction} with `submit` wired to the canister.
5536
5418
  */
5537
- async estimateInflowFee(request) {
5538
- if (isEthStablecoin(request.asset, request.chain)) {
5539
- const result = await createDepositAccountsActor(
5540
- this.canisterContext
5541
- ).estimate_deposit_fee([getEthStablecoinContractAddress(request.asset)]);
5542
- if ("Err" in result) {
5543
- throw mapDepositAccountErrorToLiquidiumError(result.Err);
5544
- }
5545
- return {
5546
- totalFee: roundInflowFeeEstimate(request, result.Ok)
5547
- };
5548
- }
5549
- if (request.asset === Asset.BTC && request.chain === Chain.BTC) {
5550
- const estimate = await this.estimateBtcInflowFee();
5551
- return {
5552
- totalFee: roundInflowFeeEstimate(request, estimate.totalFee)
5553
- };
5554
- }
5555
- throw new LiquidiumError(
5556
- LiquidiumErrorCode.VALIDATION_ERROR,
5557
- `Inflow fee estimates are not supported for ${request.asset} on ${request.chain}`
5419
+ async prepareWithdraw(request) {
5420
+ const destinationAccount = request.receiverAddress.trim();
5421
+ const signerAccount = normalizeEvmAddress(
5422
+ request.signerWalletAddress.trim()
5558
5423
  );
5559
- }
5560
- async estimateBtcInflowFee() {
5561
- const [minterFee, ledgerFee] = await Promise.all([
5562
- createCkBtcMinterActor(this.canisterContext).get_deposit_fee(),
5563
- createCkBtcLedgerActor(this.canisterContext).icrc1_fee()
5564
- ]);
5565
- return { totalFee: minterFee + ledgerFee };
5566
- }
5567
- async sendAndSubmitNativeSupplyInflow(params) {
5568
- const { request, instruction, defaultSubmitInflowRequest } = params;
5569
- if (instruction.target.type !== "nativeAddress") {
5424
+ if (!destinationAccount) {
5570
5425
  throw new LiquidiumError(
5571
5426
  LiquidiumErrorCode.VALIDATION_ERROR,
5572
- "Wallet-executed supply requires a native-address target"
5427
+ "Withdraw requires a custom outflow account"
5573
5428
  );
5574
5429
  }
5575
- if (!request.walletAdapter) {
5430
+ if (!signerAccount) {
5576
5431
  throw new LiquidiumError(
5577
5432
  LiquidiumErrorCode.VALIDATION_ERROR,
5578
- "Wallet-executed supply requires a wallet adapter"
5433
+ "Withdraw requires a signer account"
5579
5434
  );
5580
5435
  }
5581
- const account = request.account?.trim();
5582
- if (!account) {
5436
+ if (request.amount <= 0n) {
5583
5437
  throw new LiquidiumError(
5584
5438
  LiquidiumErrorCode.VALIDATION_ERROR,
5585
- "Wallet-executed transfer supply requires an account"
5439
+ "Withdraw amount must be greater than 0"
5586
5440
  );
5587
5441
  }
5588
- if (!request.amount || request.amount <= 0n) {
5442
+ const selectedPool = await this.getPoolById(request.poolId);
5443
+ const selectedAsset = selectedPool.asset;
5444
+ const minimumWithdrawAmountError = getWithdrawAmountMinimumValidationError({
5445
+ amount: request.amount,
5446
+ asset: selectedAsset
5447
+ });
5448
+ if (minimumWithdrawAmountError) {
5589
5449
  throw new LiquidiumError(
5590
5450
  LiquidiumErrorCode.VALIDATION_ERROR,
5591
- "Wallet-executed supply requires a positive amount"
5451
+ minimumWithdrawAmountError.message
5592
5452
  );
5593
5453
  }
5594
- const txid = await this.sendNativeSupplyTransaction({
5595
- walletAdapter: request.walletAdapter,
5596
- chain: instruction.chain,
5597
- toAddress: instruction.target.address,
5598
- amount: request.amount,
5599
- senderAccount: account,
5600
- asset: instruction.asset,
5601
- action: request.action
5454
+ const receiverAddress = normalizeExternalAddress({
5455
+ address: destinationAccount,
5456
+ asset: selectedAsset,
5457
+ chain: selectedPool.chain
5602
5458
  });
5603
- if (shouldSubmitInflow({ instruction, mechanism: SupplyPlanType.transfer })) {
5604
- await this.submitInflowWithRetry(txid, defaultSubmitInflowRequest);
5459
+ const lendingActor = createLendingActor(this.canisterContext);
5460
+ try {
5461
+ const expiryTimestamp = computeExpiryTimestampFromNow();
5462
+ const nonce = await lendingActor.get_nonce(signerAccount);
5463
+ const withdrawRequestData = {
5464
+ profileId: request.profileId,
5465
+ poolId: request.poolId,
5466
+ amount: request.amount,
5467
+ receiverAddress,
5468
+ signerWalletAddress: signerAccount,
5469
+ expiryTimestamp
5470
+ };
5471
+ return {
5472
+ kind: WalletActionKind.createWithdraw,
5473
+ executionKind: WalletExecutionKind.signMessage,
5474
+ actionType: WalletActionKind.createWithdraw,
5475
+ transferMode: TransferMode.native,
5476
+ account: signerAccount,
5477
+ message: createWithdrawAssetMessage(
5478
+ {
5479
+ pool_id: request.poolId,
5480
+ amount: request.amount.toString(),
5481
+ account: { type: "External", data: receiverAddress },
5482
+ expiry_timestamp: expiryTimestamp
5483
+ },
5484
+ nonce
5485
+ ),
5486
+ data: withdrawRequestData,
5487
+ submit: async (signatureInfo) => {
5488
+ return await this.submitWithdraw(withdrawRequestData, signatureInfo);
5489
+ }
5490
+ };
5491
+ } catch (error) {
5492
+ if (error instanceof LiquidiumError) {
5493
+ throw error;
5494
+ }
5495
+ throw mapCanisterCallErrorToLiquidiumError("get_nonce", error);
5496
+ }
5497
+ }
5498
+ async submitWithdraw(request, signatureInfo) {
5499
+ try {
5500
+ const result = await createLendingActor(this.canisterContext).withdraw(
5501
+ Principal.fromText(request.profileId),
5502
+ {
5503
+ data: {
5504
+ expiry_timestamp: request.expiryTimestamp,
5505
+ account: { External: request.receiverAddress },
5506
+ pool_id: Principal.fromText(request.poolId),
5507
+ amount: request.amount
5508
+ },
5509
+ signature_info: {
5510
+ Wallet: {
5511
+ signature: normalizeWalletSignature(
5512
+ signatureInfo.signature,
5513
+ signatureInfo.chain
5514
+ ),
5515
+ chain: mapWalletChainToLendingChain(signatureInfo.chain),
5516
+ account: request.signerWalletAddress
5517
+ }
5518
+ }
5519
+ }
5520
+ );
5521
+ if ("Err" in result) {
5522
+ throw mapLendingProtocolErrorToLiquidiumError(result.Err);
5523
+ }
5524
+ return mapExpectedOutflowDetails(
5525
+ mapCanisterOutflowDetails(result.Ok),
5526
+ OutflowType.withdrawal,
5527
+ "withdraw"
5528
+ );
5529
+ } catch (error) {
5530
+ if (error instanceof LiquidiumError) {
5531
+ throw error;
5532
+ }
5533
+ throw mapCanisterCallErrorToLiquidiumError("withdraw", error);
5605
5534
  }
5606
- return txid;
5607
5535
  }
5608
- async submitSupplyFlowInflow(params) {
5609
- if (!shouldSubmitInflow(params)) {
5610
- return { success: true, txid: params.submitRequest.txid };
5611
- }
5612
- return await this.submitInflow({
5613
- ...params.defaultSubmitInflowRequest,
5614
- ...params.submitRequest
5615
- });
5536
+ /**
5537
+ * Creates a withdraw outflow using the provided wallet adapter.
5538
+ *
5539
+ * This is the convenience form of `prepareWithdraw(...)` plus execution.
5540
+ *
5541
+ * @param params - Withdraw request fields plus `signerChain` and `signerWalletAdapter`.
5542
+ * @returns The canister {@link OutflowDetails} for the completed withdraw.
5543
+ */
5544
+ async withdraw(params) {
5545
+ const action = await this.prepareWithdraw(params);
5546
+ return await executeWith({
5547
+ walletAdapter: params.signerWalletAdapter,
5548
+ chain: params.signerChain,
5549
+ account: action.account
5550
+ })(action);
5616
5551
  }
5617
- async executeContractSupply(params) {
5618
- const { request, instruction, defaultSubmitInflowRequest } = params;
5619
- if (instruction.target.type !== "icrcAccount" || instruction.chain !== Chain.ETH) {
5552
+ /**
5553
+ * Prepares a borrow action that can be signed and submitted later.
5554
+ *
5555
+ * Use this when you need explicit control over signing and submission.
5556
+ *
5557
+ * @param request - Profile, pool, amount (borrow asset base units), outflow address, and signer wallet.
5558
+ * @returns A signable {@link BorrowAction} with `submit` wired to the canister.
5559
+ */
5560
+ async prepareBorrow(request) {
5561
+ const destinationAccount = request.receiverAddress.trim();
5562
+ const signerAccount = normalizeEvmAddress(
5563
+ request.signerWalletAddress.trim()
5564
+ );
5565
+ if (!destinationAccount) {
5620
5566
  throw new LiquidiumError(
5621
5567
  LiquidiumErrorCode.VALIDATION_ERROR,
5622
- "Contract-interaction supply is only supported for ETH ICRC-account targets"
5568
+ "Borrow requires a custom outflow account"
5623
5569
  );
5624
5570
  }
5625
- const walletAddressInput = request.account?.trim();
5626
- if (!walletAddressInput) {
5571
+ if (!signerAccount) {
5627
5572
  throw new LiquidiumError(
5628
5573
  LiquidiumErrorCode.VALIDATION_ERROR,
5629
- "Contract-interaction supply requires an account"
5574
+ "Borrow requires a signer account"
5630
5575
  );
5631
5576
  }
5632
- const walletAddress = normalizeAndValidateEvmAddress(
5633
- walletAddressInput,
5634
- "Invalid EVM wallet address"
5635
- );
5636
- if (!request.amount || request.amount <= 0n) {
5577
+ if (request.amount <= 0n) {
5637
5578
  throw new LiquidiumError(
5638
5579
  LiquidiumErrorCode.VALIDATION_ERROR,
5639
- "Contract-interaction supply requires a positive amount"
5580
+ "Borrow amount must be greater than 0"
5640
5581
  );
5641
5582
  }
5642
- const walletAdapter = request.walletAdapter;
5643
- if (!walletAdapter?.sendEthTransaction) {
5583
+ const selectedPool = await this.getPoolById(request.poolId);
5584
+ const selectedAsset = selectedPool.asset;
5585
+ const minimumBorrowAmountError = getBorrowAmountMinimumValidationError({
5586
+ amount: request.amount,
5587
+ asset: selectedAsset
5588
+ });
5589
+ if (minimumBorrowAmountError) {
5644
5590
  throw new LiquidiumError(
5645
5591
  LiquidiumErrorCode.VALIDATION_ERROR,
5646
- "Contract-interaction supply requires an ETH wallet adapter"
5592
+ minimumBorrowAmountError.message
5647
5593
  );
5648
5594
  }
5649
- this.requireApi();
5650
- this.requireEvmReadClient(
5651
- "Contract-interaction supply requires an EVM RPC URL or public client"
5652
- );
5653
- const evmSupplyContext = await this.getEvmSupplyContextForPool({
5654
- request: {
5595
+ const receiverAddress = normalizeExternalAddress({
5596
+ address: destinationAccount,
5597
+ asset: selectedAsset,
5598
+ chain: selectedPool.chain
5599
+ });
5600
+ const lendingActor = createLendingActor(this.canisterContext);
5601
+ try {
5602
+ const expiryTimestamp = computeExpiryTimestampFromNow();
5603
+ const nonce = await lendingActor.get_nonce(signerAccount);
5604
+ const borrowRequestData = {
5655
5605
  profileId: request.profileId,
5656
5606
  poolId: request.poolId,
5657
- walletAddress,
5658
5607
  amount: request.amount,
5659
- action: request.action
5660
- },
5661
- asset: instruction.asset,
5662
- chain: instruction.chain
5663
- });
5664
- const supplyAmount = BigInt(evmSupplyContext.amount);
5665
- if (BigInt(evmSupplyContext.balance) < supplyAmount) {
5666
- throw new LiquidiumError(
5667
- LiquidiumErrorCode.INSUFFICIENT_FUNDS,
5668
- `Insufficient ${evmSupplyContext.asset} balance for ${request.action}`
5669
- );
5608
+ receiverAddress,
5609
+ signerWalletAddress: signerAccount,
5610
+ expiryTimestamp
5611
+ };
5612
+ return {
5613
+ kind: WalletActionKind.createBorrow,
5614
+ executionKind: WalletExecutionKind.signMessage,
5615
+ actionType: WalletActionKind.createBorrow,
5616
+ transferMode: TransferMode.native,
5617
+ account: signerAccount,
5618
+ message: createBorrowAssetMessage(
5619
+ {
5620
+ pool_id: request.poolId,
5621
+ amount: request.amount.toString(),
5622
+ account: { type: "External", data: receiverAddress },
5623
+ expiry_timestamp: expiryTimestamp
5624
+ },
5625
+ nonce
5626
+ ),
5627
+ data: borrowRequestData,
5628
+ submit: async (signatureInfo) => {
5629
+ return await this.submitBorrow(borrowRequestData, signatureInfo);
5630
+ }
5631
+ };
5632
+ } catch (error) {
5633
+ if (error instanceof LiquidiumError) {
5634
+ throw error;
5635
+ }
5636
+ throw mapCanisterCallErrorToLiquidiumError("get_nonce", error);
5670
5637
  }
5671
- if (evmSupplyContext.approvalStrategy === EvmSupplyApprovalStrategy.resetThenApproveMax) {
5672
- await this.sendEthContractTransaction(
5673
- walletAdapter,
5674
- walletAddress,
5675
- createApproveTransaction({
5676
- tokenAddress: evmSupplyContext.tokenAddress,
5677
- spenderAddress: evmSupplyContext.spenderAddress,
5678
- amount: 0n
5679
- }),
5680
- `supply-${request.action}-approve-reset`
5681
- );
5682
- await this.waitForExpectedAllowance({
5683
- walletAddress,
5684
- tokenAddress: evmSupplyContext.tokenAddress,
5685
- spenderAddress: evmSupplyContext.spenderAddress,
5686
- amount: supplyAmount,
5687
- expectation: "zero"
5638
+ }
5639
+ async submitBorrow(request, signatureInfo) {
5640
+ try {
5641
+ const result = await createLendingActor(
5642
+ this.canisterContext
5643
+ ).borrow_assets(Principal.fromText(request.profileId), {
5644
+ data: {
5645
+ expiry_timestamp: request.expiryTimestamp,
5646
+ account: { External: request.receiverAddress },
5647
+ pool_id: Principal.fromText(request.poolId),
5648
+ amount: request.amount
5649
+ },
5650
+ signature_info: {
5651
+ Wallet: {
5652
+ signature: normalizeWalletSignature(
5653
+ signatureInfo.signature,
5654
+ signatureInfo.chain
5655
+ ),
5656
+ chain: mapWalletChainToLendingChain(signatureInfo.chain),
5657
+ account: request.signerWalletAddress
5658
+ }
5659
+ }
5688
5660
  });
5689
- }
5690
- if (evmSupplyContext.approvalStrategy !== EvmSupplyApprovalStrategy.none) {
5691
- await this.sendEthContractTransaction(
5692
- walletAdapter,
5693
- walletAddress,
5694
- createApproveTransaction({
5695
- tokenAddress: evmSupplyContext.tokenAddress,
5696
- spenderAddress: evmSupplyContext.spenderAddress,
5697
- amount: MAX_UINT256
5698
- }),
5699
- `supply-${request.action}-approve-max`
5661
+ if ("Err" in result) {
5662
+ throw mapLendingProtocolErrorToLiquidiumError(result.Err);
5663
+ }
5664
+ return mapExpectedOutflowDetails(
5665
+ mapCanisterOutflowDetails(result.Ok),
5666
+ OutflowType.borrow,
5667
+ "borrow_assets"
5700
5668
  );
5701
- await this.waitForExpectedAllowance({
5702
- walletAddress,
5703
- tokenAddress: evmSupplyContext.tokenAddress,
5704
- spenderAddress: evmSupplyContext.spenderAddress,
5705
- amount: supplyAmount,
5706
- expectation: "sufficient"
5707
- });
5669
+ } catch (error) {
5670
+ if (error instanceof LiquidiumError) {
5671
+ throw error;
5672
+ }
5673
+ throw mapCanisterCallErrorToLiquidiumError("borrow_assets", error);
5708
5674
  }
5709
- const depositTxid = await this.sendEthContractTransaction(
5710
- walletAdapter,
5711
- walletAddress,
5712
- createDepositErc20Transaction({
5713
- depositContractAddress: evmSupplyContext.depositContractAddress,
5714
- tokenAddress: evmSupplyContext.tokenAddress,
5715
- amount: supplyAmount,
5716
- poolId: request.poolId,
5717
- profileId: request.profileId,
5718
- destinationAccount: instruction.target.account,
5719
- action: request.action
5720
- }),
5721
- `supply-${request.action}-deposit-erc20`
5675
+ }
5676
+ /**
5677
+ * Creates a borrow outflow using the provided wallet adapter.
5678
+ *
5679
+ * This is the convenience form of `prepareBorrow(...)` plus execution.
5680
+ *
5681
+ * @param params - Borrow request fields plus `signerChain` and `signerWalletAdapter`.
5682
+ * @returns The lending canister receipt as {@link OutflowDetails}.
5683
+ *
5684
+ * @remarks
5685
+ * `id` is always present. `txid` may be missing on the first response; the SDK does not
5686
+ * poll for it. Use history or app-level polling if you need the chain transaction id.
5687
+ */
5688
+ async borrow(params) {
5689
+ const action = await this.prepareBorrow(params);
5690
+ return await executeWith({
5691
+ walletAdapter: params.signerWalletAdapter,
5692
+ chain: params.signerChain,
5693
+ account: action.account
5694
+ })(action);
5695
+ }
5696
+ /**
5697
+ * Resolves a supply target for a deposit or repayment and optionally broadcasts it.
5698
+ *
5699
+ * Transfer mode can return manual broadcast instructions when wallet fields are
5700
+ * omitted. Contract-interaction mode always requires `walletAdapter`, `account`,
5701
+ * and `amount` because it must prepare and submit approval/deposit calls.
5702
+ *
5703
+ * The SDK does not poll for inflow status. When a `txid` is returned, it is the
5704
+ * caller's responsibility to track confirmation state using their own polling.
5705
+ *
5706
+ * @returns A {@link SupplyFlow} receipt with `type`, `target`, `submit`, and
5707
+ * an optional `txid` present when the SDK broadcast for you.
5708
+ */
5709
+ async supply(request) {
5710
+ return await this.createSupplyFlowExecutor().create(request);
5711
+ }
5712
+ /**
5713
+ * Fetches ERC-20 supply planning data with the configured EVM read client.
5714
+ *
5715
+ * Requires `evmRpcUrl` or `evmPublicClient` on the client. Used internally by
5716
+ * contract-interaction `supply`.
5717
+ *
5718
+ * @param request - Profile, pool, wallet, amount (token base units), and action.
5719
+ * @returns Locally computed {@link EvmSupplyContext} for approvals and deposit.
5720
+ */
5721
+ async getEvmSupplyContext(request) {
5722
+ return await this.createSupplyFlowExecutor().getEvmSupplyContext(request);
5723
+ }
5724
+ /**
5725
+ * Returns the read-only deposit address for an ETH stablecoin inflow target.
5726
+ *
5727
+ * This is a query call that does not create or mutate state. Use it when you
5728
+ * need the deposit address without hitting the authorization-gated update path.
5729
+ *
5730
+ * @param request - Profile, pool, asset, and supply action.
5731
+ * @returns The EVM deposit address for the derived account.
5732
+ */
5733
+ async getDepositAddress(request) {
5734
+ if (!isEthStablecoin(request.asset, Chain.ETH)) {
5735
+ throw new LiquidiumError(
5736
+ LiquidiumErrorCode.VALIDATION_ERROR,
5737
+ "getDepositAddress is only supported for ETH stablecoins"
5738
+ );
5739
+ }
5740
+ const tokenAddress = getEthStablecoinContractAddress(request.asset);
5741
+ const subaccount = encodeInflowSubaccount({
5742
+ action: request.action,
5743
+ principal: Principal.fromText(request.profileId)
5744
+ });
5745
+ const result = await createDepositAccountsActor(
5746
+ this.canisterContext
5747
+ ).get_deposit_address(
5748
+ {
5749
+ owner: Principal.fromText(request.poolId),
5750
+ subaccount: [subaccount]
5751
+ },
5752
+ [tokenAddress]
5722
5753
  );
5723
- try {
5724
- await this.submitInflowWithRetry(depositTxid, defaultSubmitInflowRequest);
5725
- } catch {
5754
+ if ("Err" in result) {
5755
+ throw mapDepositAccountErrorToLiquidiumError(result.Err);
5726
5756
  }
5727
- return depositTxid;
5757
+ return result.Ok;
5728
5758
  }
5729
- async sendNativeSupplyTransaction(params) {
5730
- switch (params.chain) {
5731
- case Chain.BTC: {
5732
- if (!params.walletAdapter.sendBtcTransaction) {
5733
- throw new LiquidiumError(
5734
- LiquidiumErrorCode.VALIDATION_ERROR,
5735
- "BTC wallet adapter does not support transaction sending"
5736
- );
5737
- }
5738
- return await params.walletAdapter.sendBtcTransaction({
5739
- chain: Chain.BTC,
5740
- toAddress: params.toAddress,
5741
- amountSats: params.amount,
5742
- account: params.senderAccount,
5743
- actionType: `supply-${params.action}`,
5744
- transferMode: TransferMode.native
5745
- });
5746
- }
5747
- case Chain.ETH: {
5748
- if (!params.walletAdapter.sendEthTransaction) {
5749
- throw new LiquidiumError(
5750
- LiquidiumErrorCode.VALIDATION_ERROR,
5751
- "ETH wallet adapter does not support transaction sending"
5752
- );
5753
- }
5754
- if (isEthStablecoin(params.asset, params.chain)) {
5755
- return await params.walletAdapter.sendEthTransaction({
5756
- chain: Chain.ETH,
5757
- account: params.senderAccount,
5758
- actionType: `supply-${params.action}`,
5759
- transferMode: TransferMode.native,
5760
- transaction: createTransferErc20Transaction({
5761
- tokenAddress: getEthStablecoinContractAddress(params.asset),
5762
- recipientAddress: params.toAddress,
5763
- amount: params.amount
5764
- })
5765
- });
5766
- }
5767
- return await params.walletAdapter.sendEthTransaction({
5768
- chain: Chain.ETH,
5769
- account: params.senderAccount,
5770
- actionType: `supply-${params.action}`,
5771
- transferMode: TransferMode.native,
5772
- transaction: {
5773
- to: params.toAddress,
5774
- value: params.amount.toString()
5775
- }
5776
- });
5759
+ /**
5760
+ * Estimates the network/deposit fee for an inflow target.
5761
+ *
5762
+ * ETH stablecoin deposit-address estimates are served by the deposit-address
5763
+ * canister. BTC estimates include the ckBTC minter deposit fee and ledger fee.
5764
+ *
5765
+ * @param request - Asset and chain pair to estimate for.
5766
+ * @returns Total fee estimate rounded up in the asset's base units.
5767
+ */
5768
+ async estimateInflowFee(request) {
5769
+ if (isEthStablecoin(request.asset, request.chain)) {
5770
+ const result = await createDepositAccountsActor(
5771
+ this.canisterContext
5772
+ ).estimate_deposit_fee([getEthStablecoinContractAddress(request.asset)]);
5773
+ if ("Err" in result) {
5774
+ throw mapDepositAccountErrorToLiquidiumError(result.Err);
5777
5775
  }
5778
- default:
5779
- throw new LiquidiumError(
5780
- LiquidiumErrorCode.VALIDATION_ERROR,
5781
- `Native-address wallet execution is not supported for ${params.chain}`
5782
- );
5776
+ return {
5777
+ totalFee: roundInflowFeeEstimate(request, result.Ok)
5778
+ };
5783
5779
  }
5780
+ if (request.asset === Asset.BTC && request.chain === Chain.BTC) {
5781
+ const estimate = await this.estimateBtcInflowFee();
5782
+ return {
5783
+ totalFee: roundInflowFeeEstimate(request, estimate.totalFee)
5784
+ };
5785
+ }
5786
+ throw new LiquidiumError(
5787
+ LiquidiumErrorCode.VALIDATION_ERROR,
5788
+ `Inflow fee estimates are not supported for ${request.asset} on ${request.chain}`
5789
+ );
5784
5790
  }
5785
- async submitInflowWithRetry(txid, extraRequest) {
5786
- await retryWithBackoff({
5787
- execute: async () => {
5788
- await this.submitInflow({ txid, ...extraRequest });
5789
- },
5790
- maxAttempts: SUBMIT_INFLOW_MAX_ATTEMPTS,
5791
- initialRetryDelayMs: SUBMIT_INFLOW_INITIAL_RETRY_DELAY_MS,
5792
- backoffMultiplier: SUBMIT_INFLOW_RETRY_BACKOFF_MULTIPLIER,
5793
- shouldRetryError: isRetriableInflowSubmitError
5794
- });
5791
+ async estimateBtcInflowFee() {
5792
+ const [minterFee, ledgerFee] = await Promise.all([
5793
+ createCkBtcMinterActor(this.canisterContext).get_deposit_fee(),
5794
+ createCkBtcLedgerActor(this.canisterContext).icrc1_fee()
5795
+ ]);
5796
+ return { totalFee: minterFee + ledgerFee };
5795
5797
  }
5796
5798
  /**
5797
5799
  * Submits an inflow transaction id for faster indexing.
5798
5800
  *
5799
5801
  * Uses the Liquidium SDK API.
5800
5802
  *
5801
- * @param request - Broadcast `txid` plus optional `chain` and inflow `type`.
5803
+ * @param request - Broadcast `txid` plus inflow `operation` and optional `chain`.
5802
5804
  * @returns Acknowledgement including the submitted `txid`.
5803
5805
  */
5804
5806
  async submitInflow(request) {
5805
5807
  const apiClient = this.requireApi();
5806
- return await apiClient.post(
5807
- SdkApiPath.inflow,
5808
- request
5809
- );
5808
+ const response = await apiClient.post(SdkApiPath.inflow, request);
5809
+ return {
5810
+ txid: response.txid
5811
+ };
5810
5812
  }
5811
5813
  /**
5812
5814
  * Returns whether borrowing is currently disabled by the protocol.
@@ -5837,11 +5839,16 @@ var LendingModule = class {
5837
5839
  }
5838
5840
  return this.apiClient;
5839
5841
  }
5840
- requireEvmReadClient(message) {
5841
- if (!this.evmReadClient) {
5842
- throw new LiquidiumError(LiquidiumErrorCode.VALIDATION_ERROR, message);
5843
- }
5844
- return this.evmReadClient;
5842
+ createSupplyFlowExecutor() {
5843
+ return new SupplyFlowExecutor({
5844
+ canisterContext: this.canisterContext,
5845
+ evmReadClient: this.evmReadClient,
5846
+ getPoolById: async (poolId) => await this.getPoolById(poolId),
5847
+ requireApi: () => {
5848
+ this.requireApi();
5849
+ },
5850
+ submitInflow: async (request) => await this.submitInflow(request)
5851
+ });
5845
5852
  }
5846
5853
  async getPoolById(poolId) {
5847
5854
  const pools = await createFlexibleLendingActor(
@@ -5859,88 +5866,7 @@ var LendingModule = class {
5859
5866
  }
5860
5867
  return decodedPool;
5861
5868
  }
5862
- async normalizeOutflowReceiverAddress(params) {
5863
- const selectedPool = await this.getPoolById(params.poolId);
5864
- return normalizeExternalAddress({
5865
- address: params.receiverAddress,
5866
- asset: selectedPool.asset,
5867
- chain: selectedPool.chain
5868
- });
5869
- }
5870
- async sendEthContractTransaction(walletAdapter, walletAddress, request, actionType) {
5871
- if (!walletAdapter.sendEthTransaction) {
5872
- throw new LiquidiumError(
5873
- LiquidiumErrorCode.VALIDATION_ERROR,
5874
- "ETH wallet adapter does not support transaction sending"
5875
- );
5876
- }
5877
- return await walletAdapter.sendEthTransaction({
5878
- chain: Chain.ETH,
5879
- account: walletAddress,
5880
- actionType,
5881
- transferMode: TransferMode.native,
5882
- transaction: request
5883
- });
5884
- }
5885
- async waitForExpectedAllowance(params) {
5886
- const evmReadClient = this.requireEvmReadClient(
5887
- "Contract-interaction supply requires an EVM RPC URL or public client"
5888
- );
5889
- let lastPollingError;
5890
- for (let pollIndex = 0; pollIndex < ETH_APPROVAL_MAX_POLLS; pollIndex += 1) {
5891
- try {
5892
- const allowance = await readErc20Allowance({
5893
- evmReadClient,
5894
- tokenAddress: params.tokenAddress,
5895
- ownerAddress: params.walletAddress,
5896
- spenderAddress: params.spenderAddress
5897
- });
5898
- const matchesExpectation = params.expectation === "zero" ? allowance === 0n : allowance >= params.amount;
5899
- if (matchesExpectation) {
5900
- return;
5901
- }
5902
- } catch (error) {
5903
- lastPollingError = error;
5904
- }
5905
- await delay(ETH_APPROVAL_POLL_INTERVAL_MS);
5906
- }
5907
- const lastPollingErrorMessage = getUnknownErrorMessage(lastPollingError);
5908
- throw new LiquidiumError(
5909
- LiquidiumErrorCode.SERVICE_UNAVAILABLE,
5910
- lastPollingErrorMessage ? `Timed out waiting for ${params.expectation} ETH allowance update. Last polling error: ${lastPollingErrorMessage}` : `Timed out waiting for ${params.expectation} ETH allowance update`
5911
- );
5912
- }
5913
5869
  };
5914
- async function readErc20Allowance(params) {
5915
- const allowance = await params.evmReadClient.readContract({
5916
- address: params.tokenAddress,
5917
- abi: ERC20_ABI,
5918
- functionName: "allowance",
5919
- args: [
5920
- params.ownerAddress,
5921
- params.spenderAddress
5922
- ]
5923
- });
5924
- return BigInt(allowance);
5925
- }
5926
- async function readErc20Balance(params) {
5927
- const balance = await params.evmReadClient.readContract({
5928
- address: params.tokenAddress,
5929
- abi: ERC20_ABI,
5930
- functionName: "balanceOf",
5931
- args: [params.ownerAddress]
5932
- });
5933
- return BigInt(balance);
5934
- }
5935
- function getApprovalStrategy(params) {
5936
- if (params.allowance >= params.amount) {
5937
- return EvmSupplyApprovalStrategy.none;
5938
- }
5939
- if (params.allowance === 0n) {
5940
- return EvmSupplyApprovalStrategy.approveMax;
5941
- }
5942
- return EvmSupplyApprovalStrategy.resetThenApproveMax;
5943
- }
5944
5870
  function mapExpectedOutflowDetails(details, expectedOutflowType, operation) {
5945
5871
  if (details.outflowType !== expectedOutflowType) {
5946
5872
  throw new LiquidiumError(
@@ -5948,42 +5874,22 @@ function mapExpectedOutflowDetails(details, expectedOutflowType, operation) {
5948
5874
  `${operation} returned unexpected outflow type ${details.outflowType}`
5949
5875
  );
5950
5876
  }
5951
- return details;
5952
- }
5953
- async function delay(timeoutMs) {
5954
- await new Promise((resolve) => setTimeout(resolve, timeoutMs));
5955
- }
5956
- function isRetriableInflowSubmitError(error) {
5957
- if (!(error instanceof LiquidiumError)) {
5958
- return false;
5959
- }
5960
- return error.code === LiquidiumErrorCode.SERVICE_UNAVAILABLE;
5961
- }
5962
- function getUnknownErrorMessage(error) {
5963
- if (error instanceof Error) {
5964
- return error.message;
5965
- }
5966
- if (typeof error === "string") {
5967
- return error;
5968
- }
5969
- return null;
5970
- }
5971
- function getDefaultSubmitInflowRequest(params) {
5972
- if (params.chain !== Chain.BTC && params.chain !== Chain.ETH) {
5973
- return void 0;
5974
- }
5975
5877
  return {
5976
- chain: params.chain,
5977
- type: params.action === SupplyAction.repayment ? InflowSubmitType.REPAY : InflowSubmitType.DEPOSIT
5878
+ ...details,
5879
+ status: createLiquidiumStatus({
5880
+ operation: mapOutflowTypeToStatusOperation(expectedOutflowType),
5881
+ state: details.txid ? "confirming" : "processing"
5882
+ })
5978
5883
  };
5979
5884
  }
5980
- function shouldSubmitInflow(params) {
5981
- if (params.mechanism !== SupplyPlanType.transfer) {
5982
- return true;
5983
- }
5984
- return !isEthStablecoin(params.instruction.asset, params.instruction.chain);
5885
+ function mapOutflowTypeToStatusOperation(outflowType) {
5886
+ return outflowType;
5985
5887
  }
5986
5888
 
5889
+ // src/core/rates.ts
5890
+ var RATE_SCALE2 = 1000000000000000000000000000n;
5891
+ var RATE_DECIMALS = BigInt(RATE_SCALE2.toString().length - 1);
5892
+
5987
5893
  // src/modules/market/mappers.ts
5988
5894
  var DECIMAL_BASE = 10;
5989
5895
  var PAIR_SEPARATOR = "_";
@@ -6055,23 +5961,22 @@ function formatPrice(price, decimals) {
6055
5961
  // src/modules/market/market.ts
6056
5962
  var ZERO_POOL_RATE = [0n, 0n, 0n];
6057
5963
  var MarketModule = class {
6058
- constructor(canisterContext, apiClient) {
5964
+ constructor(canisterContext) {
6059
5965
  this.canisterContext = canisterContext;
6060
- this.apiClient = apiClient;
6061
5966
  }
6062
5967
  canisterContext;
6063
- apiClient;
6064
5968
  /**
6065
- * Lists all pools with their current rates.
5969
+ * Lists SDK-supported pools with their current rates.
6066
5970
  *
6067
- * @returns All configured lending pools enriched with their current rate data.
5971
+ * Unsupported asset or chain variants returned by the canister are omitted.
5972
+ *
5973
+ * @returns Supported lending pools enriched with their current rate data.
6068
5974
  */
6069
5975
  async listPools() {
6070
- void this.apiClient;
6071
5976
  try {
6072
5977
  const flexibleActor = createFlexibleLendingActor(this.canisterContext);
6073
5978
  const rawPools = await flexibleActor.list_pools();
6074
- const decodedPools = rawPools.map(decodeFlexiblePool).filter((pool) => pool !== null);
5979
+ const decodedPools = decodeSupportedFlexiblePools(rawPools);
6075
5980
  return await Promise.all(
6076
5981
  decodedPools.map(async (pool) => {
6077
5982
  const poolRate = await flexibleActor.get_pool_rate(pool.principal);
@@ -6178,6 +6083,17 @@ var MarketModule = class {
6178
6083
  }
6179
6084
  }
6180
6085
  };
6086
+ function decodeSupportedFlexiblePools(rawPools) {
6087
+ const decodedPools = [];
6088
+ for (const rawPool of rawPools) {
6089
+ const decodedPool = decodeFlexiblePool(rawPool);
6090
+ if (!decodedPool) {
6091
+ continue;
6092
+ }
6093
+ decodedPools.push(decodedPool);
6094
+ }
6095
+ return decodedPools;
6096
+ }
6181
6097
 
6182
6098
  // src/modules/positions/mappers.ts
6183
6099
  var USD_VALUE_SCALE_DECIMALS = 27n;
@@ -6498,7 +6414,7 @@ var LiquidiumClient = class {
6498
6414
  this.apiClient,
6499
6415
  this.evmReadClient
6500
6416
  );
6501
- this.market = new MarketModule(this.canisterContext, this.apiClient);
6417
+ this.market = new MarketModule(this.canisterContext);
6502
6418
  this.positions = new PositionsModule(this.canisterContext, this.market);
6503
6419
  this.activities = new ActivitiesModule(
6504
6420
  this.apiClient,
@@ -6508,6 +6424,7 @@ var LiquidiumClient = class {
6508
6424
  this.instantLoans = new InstantLoansModule(
6509
6425
  this.canisterContext,
6510
6426
  this.apiClient,
6427
+ this.activities,
6511
6428
  this.lending,
6512
6429
  this.positions
6513
6430
  );
@@ -6529,6 +6446,6 @@ function resolveEvmReadClient(config) {
6529
6446
  });
6530
6447
  }
6531
6448
 
6532
- export { AccountsModule, ActivitiesModule, ActivityDirection, ActivityFilter, ActivityKind, ActivityStatus, Asset, CK_ETH_DEPOSIT_CONTRACT_ADDRESS, Chain, Environment, EvmSupplyApprovalStrategy, HistoryModule, InflowSubmitType, InstantLoanStatus, InstantLoansModule, LendingModule, LiquidiumClient, LiquidiumError, LiquidiumErrorCode, MIN_BORROW_AMOUNTS_BY_ASSET, MarketModule, OutflowType, PositionsModule, QuoteModule, QuoteValidationErrorCode, QuoteWarningCode, RATE_DECIMALS, RATE_SCALE, SupplyAction, SupplyPlanType, TransferMode, USDC_CONTRACT_ADDRESS, USDT_CONTRACT_ADDRESS, UserHistoryStatus, WalletExecutionKind, createTransferErc20Transaction, executeWith, getMinimumBorrowAmount, intFromPublicId, publicIdFromInt };
6449
+ export { AccountsModule, ActivitiesModule, ActivityFilter, Asset, CK_ETH_DEPOSIT_CONTRACT_ADDRESS, Chain, Environment, EvmSupplyApprovalStrategy, HistoryModule, InstantLoansModule, LendingModule, LiquidiumClient, LiquidiumError, LiquidiumErrorCode, MIN_BORROW_AMOUNTS_BY_ASSET, MIN_WITHDRAW_AMOUNTS_BY_ASSET, MarketModule, OutflowType, PositionsModule, QuoteModule, QuoteValidationErrorCode, QuoteWarningCode, RATE_DECIMALS, RATE_SCALE2 as RATE_SCALE, SupplyAction, SupplyPlanType, TransferMode, USDC_CONTRACT_ADDRESS, USDT_CONTRACT_ADDRESS, WalletActionKind, WalletExecutionKind, createTransferErc20Transaction, executeWith, getMinimumBorrowAmount, getMinimumWithdrawAmount, intFromPublicId, publicIdFromInt };
6533
6450
  //# sourceMappingURL=index.js.map
6534
6451
  //# sourceMappingURL=index.js.map