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