@liquidium/client 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -30,11 +30,7 @@ var SupplyAction = {
30
30
  var OutflowType = {
31
31
  borrow: "borrow",
32
32
  feeClaim: "feeClaim",
33
- withdraw: "withdraw"
34
- };
35
- var InflowSubmitType = {
36
- DEPOSIT: "DEPOSIT",
37
- REPAY: "REPAY"
33
+ withdrawal: "withdrawal"
38
34
  };
39
35
 
40
36
  // src/core/config.ts
@@ -260,6 +256,7 @@ var idlFactory = ({ IDL }) => {
260
256
  const Chains = IDL.Variant({
261
257
  "BTC": IDL.Null,
262
258
  "ETH": IDL.Null,
259
+ "ICP": IDL.Null,
263
260
  "SOL": IDL.Null
264
261
  });
265
262
  const WalletType = IDL.Variant({ "Wallet": Chains });
@@ -317,8 +314,14 @@ var idlFactory = ({ IDL }) => {
317
314
  "InsufficientFunds": IDL.Null
318
315
  });
319
316
  const Result = IDL.Variant({ "Ok": IDL.Null, "Err": ProtocolError });
317
+ const IcrcAccount = IDL.Record({
318
+ "owner": IDL.Principal,
319
+ "subaccount": IDL.Opt(IDL.Vec(IDL.Nat8))
320
+ });
320
321
  const AccountType = IDL.Variant({
322
+ "Icrc": IcrcAccount,
321
323
  "Native": IDL.Principal,
324
+ "AccountIdentifier": IDL.Text,
322
325
  "External": IDL.Text
323
326
  });
324
327
  const BorrowAssetRequest = IDL.Record({
@@ -355,6 +358,7 @@ var idlFactory = ({ IDL }) => {
355
358
  });
356
359
  const Assets = IDL.Variant({
357
360
  "BTC": IDL.Null,
361
+ "ICP": IDL.Null,
358
362
  "SOL": IDL.Null,
359
363
  "USDC": IDL.Null,
360
364
  "USDT": IDL.Null
@@ -1360,8 +1364,8 @@ function mapCanisterWalletToWallet(canisterWallet) {
1360
1364
  );
1361
1365
  }
1362
1366
  }
1363
- var KNOWN_ASSET_TAGS = ["BTC", "SOL", "USDC", "USDT"];
1364
- var KNOWN_CHAIN_TAGS = ["BTC", "ETH", "SOL"];
1367
+ var KNOWN_ASSET_TAGS = ["BTC", "USDC", "USDT"];
1368
+ var KNOWN_CHAIN_TAGS = ["BTC", "ETH"];
1365
1369
  function extractVariantTag(variant, knownTags) {
1366
1370
  const [key] = Object.keys(variant);
1367
1371
  if (!key) {
@@ -1385,8 +1389,14 @@ function extractVariantTag(variant, knownTags) {
1385
1389
 
1386
1390
  // src/core/canisters/instant-loans/flexible-actor.ts
1387
1391
  var flexibleInstantLoansIdlFactory = ({ IDL }) => {
1392
+ const IcrcAccount = IDL.Record({
1393
+ owner: IDL.Principal,
1394
+ subaccount: IDL.Opt(IDL.Vec(IDL.Nat8))
1395
+ });
1388
1396
  const AccountType = IDL.Variant({
1397
+ Icrc: IcrcAccount,
1389
1398
  Native: IDL.Principal,
1399
+ AccountIdentifier: IDL.Text,
1390
1400
  External: IDL.Text
1391
1401
  });
1392
1402
  const SignatureVerificationError = IDL.Variant({
@@ -1668,49 +1678,24 @@ function decodeLoanCreatedEvent(payload) {
1668
1678
  }
1669
1679
 
1670
1680
  // src/core/sdk-api-paths.ts
1671
- var SDK_API_VERSION = {
1672
- activities: "v1",
1673
- history: "v1",
1674
- inflow: "v1",
1675
- instantLoans: "v1"
1676
- };
1681
+ var SDK_API_V1_PATH = "/v1";
1682
+ var SDK_API_V2_PATH = "/v2";
1677
1683
  var SdkApiQueryParam = {
1678
1684
  cursor: "cursor",
1679
1685
  from: "from",
1686
+ filter: "filter",
1680
1687
  limit: "limit",
1681
1688
  market: "market",
1689
+ operations: "operations",
1682
1690
  poolId: "poolId",
1683
1691
  profileId: "profileId",
1684
- state: "state",
1685
- statuses: "statuses",
1686
- to: "to",
1687
- types: "types"
1692
+ states: "states",
1693
+ to: "to"
1688
1694
  };
1689
- var ACTIVITIES = `/${SDK_API_VERSION.activities}/activities`;
1690
- var HISTORY_POOL = `/${SDK_API_VERSION.history}/history/pool`;
1691
- var HISTORY_POOL_CONFIG = `/${SDK_API_VERSION.history}/history/pool-config`;
1692
- var HISTORY_RATES = `/${SDK_API_VERSION.history}/history/rates`;
1693
- var HISTORY_USERS = `/${SDK_API_VERSION.history}/history/users`;
1694
- var INFLOW = `/${SDK_API_VERSION.inflow}/inflow`;
1695
- var INSTANT_LOANS = `/${SDK_API_VERSION.instantLoans}/instant-loans`;
1696
- function buildHistoryPoolPath(poolId, query) {
1697
- const base = `${HISTORY_POOL}/${encodeURIComponent(poolId)}`;
1698
- const qs = query.toString();
1699
- return qs ? `${base}?${qs}` : base;
1700
- }
1701
- function buildHistoryPoolConfigPath(poolId, cursor) {
1702
- const base = `${HISTORY_POOL_CONFIG}/${encodeURIComponent(poolId)}`;
1703
- if (!cursor) {
1704
- return base;
1705
- }
1706
- const query = new URLSearchParams({ [SdkApiQueryParam.cursor]: cursor });
1707
- return `${base}?${query.toString()}`;
1708
- }
1709
- function buildHistoryRatesPath(poolId, query) {
1710
- const base = `${HISTORY_RATES}/${encodeURIComponent(poolId)}`;
1711
- const qs = query.toString();
1712
- return qs ? `${base}?${qs}` : base;
1713
- }
1695
+ var ACTIVITIES = `${SDK_API_V2_PATH}/activities`;
1696
+ var HISTORY_USERS = `${SDK_API_V2_PATH}/history/users`;
1697
+ var INFLOW = `${SDK_API_V2_PATH}/inflow`;
1698
+ var INSTANT_LOANS = `${SDK_API_V1_PATH}/instant-loans`;
1714
1699
  function buildHistoryUserTransactionsPath(user, query) {
1715
1700
  const base = `${HISTORY_USERS}/${encodeURIComponent(user)}/transactions`;
1716
1701
  const qs = query.toString();
@@ -1725,8 +1710,8 @@ function buildActivitiesPath(request) {
1725
1710
  const query = new URLSearchParams({
1726
1711
  [SdkApiQueryParam.profileId]: request.profileId
1727
1712
  });
1728
- if (request.state) {
1729
- query.set(SdkApiQueryParam.state, request.state);
1713
+ if (request.filter) {
1714
+ query.set(SdkApiQueryParam.filter, request.filter);
1730
1715
  }
1731
1716
  return `${ACTIVITIES}?${query.toString()}`;
1732
1717
  }
@@ -1784,12 +1769,6 @@ function parseBigInt(value, label) {
1784
1769
  );
1785
1770
  }
1786
1771
  }
1787
- function parseOptionalBigInt(value, label) {
1788
- if (value === void 0) {
1789
- return void 0;
1790
- }
1791
- return parseBigInt(value, label);
1792
- }
1793
1772
 
1794
1773
  // src/modules/instant-loans/ref-code.ts
1795
1774
  var REF_LENGTH = 6;
@@ -1860,28 +1839,8 @@ var ActivityFilter = {
1860
1839
  completed: "completed",
1861
1840
  all: "all"
1862
1841
  };
1863
- var ActivityDirection = {
1864
- inflow: "inflow",
1865
- outflow: "outflow"
1866
- };
1867
- var ActivityKind = {
1868
- deposit: "deposit",
1869
- repayment: "repayment",
1870
- borrow: "borrow",
1871
- withdraw: "withdraw"
1872
- };
1873
- var ActivityStatus = {
1874
- requested: "requested",
1875
- pending: "pending",
1876
- detected: "detected",
1877
- processing: "processing",
1878
- sent: "sent",
1879
- confirmed: "confirmed",
1880
- failed: "failed"
1881
- };
1882
1842
 
1883
1843
  // src/modules/activities/activities.ts
1884
- var PRE_TERMINAL_ETH_ACTIVITY_ID_PREFIX = "pre_terminal_eth_";
1885
1844
  var ActivitiesModule = class {
1886
1845
  constructor(apiClient, canisterContext) {
1887
1846
  this.apiClient = apiClient;
@@ -1890,11 +1849,11 @@ var ActivitiesModule = class {
1890
1849
  apiClient;
1891
1850
  canisterContext;
1892
1851
  /**
1893
- * Lists profile activities. Defaults to all activities.
1852
+ * Lists profile activities. Defaults to active activities.
1894
1853
  *
1895
1854
  * Uses the Liquidium SDK API.
1896
1855
  *
1897
- * @param request - Profile id or instant-loan short reference plus optional state filter.
1856
+ * @param request - Profile id or instant-loan short reference plus optional lifecycle filter.
1898
1857
  * @returns Activities owned by the resolved profile.
1899
1858
  */
1900
1859
  async list(request) {
@@ -1903,7 +1862,7 @@ var ActivitiesModule = class {
1903
1862
  const response = await apiClient.get(
1904
1863
  buildActivitiesPath({
1905
1864
  profileId,
1906
- state: request.filter ?? ActivityFilter.all
1865
+ filter: request.filter ?? ActivityFilter.active
1907
1866
  })
1908
1867
  );
1909
1868
  return response.activities.map(mapActivity);
@@ -1984,76 +1943,48 @@ function mapInstantLoanLookupError(error) {
1984
1943
  return new LiquidiumError(LiquidiumErrorCode.INTERNAL, key);
1985
1944
  }
1986
1945
  function mapActivity(wire) {
1987
- const { amount, stage, status, topUp: topUpWire, ...activity } = wire;
1988
- const effectiveStage = stage ?? deriveActivityStage(wire);
1989
- const topUp = activity.direction === ActivityDirection.inflow && topUpWire ? mapActivityTopUp(topUpWire) : void 0;
1990
- if (activity.direction === ActivityDirection.inflow) {
1991
- return {
1992
- ...activity,
1993
- direction: ActivityDirection.inflow,
1994
- kind: mapInflowActivityKind(activity.kind),
1995
- status: mapInflowActivityStatus(
1996
- mapActivityStatus(status, effectiveStage)
1997
- ),
1998
- ...topUp ? { topUp } : {},
1999
- amount: parseBigInt(amount, "activity amount")
2000
- };
2001
- }
2002
- return {
2003
- ...activity,
2004
- direction: ActivityDirection.outflow,
2005
- kind: mapOutflowActivityKind(activity.kind),
2006
- status: mapOutflowActivityStatus(mapActivityStatus(status, effectiveStage)),
2007
- amount: parseBigInt(amount, "activity amount")
1946
+ const amount = parseBigInt(wire.amount, "activity amount");
1947
+ const activityBase = {
1948
+ id: wire.id,
1949
+ poolId: wire.poolId,
1950
+ asset: wire.asset,
1951
+ chain: wire.chain,
1952
+ amount,
1953
+ timestampMs: wire.timestampMs
2008
1954
  };
2009
- }
2010
- function mapInflowActivityKind(kind) {
2011
- if (kind === "deposit" || kind === "repayment") {
2012
- return kind;
2013
- }
2014
- throw new LiquidiumError(
2015
- LiquidiumErrorCode.INTERNAL,
2016
- `Invalid inflow activity kind: ${kind}`
2017
- );
2018
- }
2019
- function mapOutflowActivityKind(kind) {
2020
- if (kind === "borrow" || kind === "withdraw") {
2021
- return kind;
1955
+ if (isInflowOperation(wire.status.operation)) {
1956
+ const activity = {
1957
+ ...activityBase,
1958
+ status: wire.status
1959
+ };
1960
+ if (wire.txids) {
1961
+ activity.txids = wire.txids;
1962
+ }
1963
+ if (wire.topUp) {
1964
+ activity.topUp = mapActivityTopUp(wire.topUp);
1965
+ }
1966
+ return activity;
2022
1967
  }
2023
- throw new LiquidiumError(
2024
- LiquidiumErrorCode.INTERNAL,
2025
- `Invalid outflow activity kind: ${kind}`
2026
- );
2027
- }
2028
- function mapInflowActivityStatus(status) {
2029
- if (status !== "sent") {
2030
- return status;
1968
+ if (isOutflowOperation(wire.status.operation)) {
1969
+ const activity = {
1970
+ ...activityBase,
1971
+ status: wire.status
1972
+ };
1973
+ if (wire.txids) {
1974
+ activity.txids = wire.txids;
1975
+ }
1976
+ return activity;
2031
1977
  }
2032
1978
  throw new LiquidiumError(
2033
1979
  LiquidiumErrorCode.INTERNAL,
2034
- `Invalid inflow activity status: ${status}`
1980
+ `Invalid activity operation: ${wire.status.operation}`
2035
1981
  );
2036
1982
  }
2037
- function mapOutflowActivityStatus(status) {
2038
- if (status !== "detected" && status !== "processing") {
2039
- return status;
2040
- }
2041
- throw new LiquidiumError(
2042
- LiquidiumErrorCode.INTERNAL,
2043
- `Invalid outflow activity status: ${status}`
2044
- );
1983
+ function isInflowOperation(operation) {
1984
+ return operation === "deposit" || operation === "repayment";
2045
1985
  }
2046
- function mapActivityStatus(status, stage) {
2047
- if (status !== "pending") {
2048
- return status;
2049
- }
2050
- if (stage === "deposited") {
2051
- return "detected";
2052
- }
2053
- if (stage === "confirmed" || stage === "finalising" || stage === "pending") {
2054
- return "processing";
2055
- }
2056
- return "pending";
1986
+ function isOutflowOperation(operation) {
1987
+ return operation === "borrow" || operation === "withdrawal";
2057
1988
  }
2058
1989
  function mapActivityTopUp(wire) {
2059
1990
  return {
@@ -2069,24 +2000,6 @@ function mapActivityTopUp(wire) {
2069
2000
  )
2070
2001
  };
2071
2002
  }
2072
- function deriveActivityStage(wire) {
2073
- if (wire.id.startsWith(PRE_TERMINAL_ETH_ACTIVITY_ID_PREFIX) && wire.direction === ActivityDirection.inflow && wire.chain === Chain.ETH) {
2074
- return "deposited";
2075
- }
2076
- return void 0;
2077
- }
2078
-
2079
- // src/core/rates.ts
2080
- var RATE_SCALE = 1000000000000000000000000000n;
2081
- var RATE_DECIMALS = BigInt(RATE_SCALE.toString().length - 1);
2082
-
2083
- // src/modules/history/types.ts
2084
- var UserHistoryStatus = {
2085
- requested: "requested",
2086
- pending: "pending",
2087
- confirmed: "confirmed",
2088
- failed: "failed"
2089
- };
2090
2003
 
2091
2004
  // src/modules/history/history.ts
2092
2005
  var HistoryModule = class {
@@ -2104,154 +2017,41 @@ var HistoryModule = class {
2104
2017
  return this.apiClient;
2105
2018
  }
2106
2019
  /**
2107
- * Returns paginated rate and utilization history for a pool.
2108
- *
2109
- * @param poolId - The pool principal text.
2110
- * @param window - Optional time window with from/to timestamps and limit.
2111
- * @returns A page of pool rate history entries and the next cursor when more results are available.
2112
- */
2113
- async getPoolHistory(poolId, window = {}) {
2114
- const apiClient = this.requireApi();
2115
- const query = createHistoryWindowQuery(window);
2116
- const requestPath = buildHistoryPoolPath(poolId, query);
2117
- const response = await apiClient.get(requestPath);
2118
- return {
2119
- items: response.items.map((item) => ({
2120
- date: item.date,
2121
- rateDecimals: RATE_DECIMALS,
2122
- avgBorrowRate: parseBigInt(item.avgBorrowRate, "pool borrow rate"),
2123
- avgLendRate: parseBigInt(item.avgLendRate, "pool lend rate"),
2124
- avgUtilizationRate: parseBigInt(
2125
- item.avgUtilizationRate,
2126
- "pool utilization rate"
2127
- )
2128
- })),
2129
- nextCursor: response.nextCursor
2130
- };
2131
- }
2132
- /**
2133
- * Returns paginated configuration change history for a pool.
2134
- *
2135
- * @param poolId - The pool principal text.
2136
- * @param cursor - An optional pagination cursor from a previous response.
2137
- * @returns A page of pool configuration changes and the next cursor when more results are available.
2138
- */
2139
- async getPoolConfigHistory(poolId, cursor) {
2140
- const apiClient = this.requireApi();
2141
- const requestPath = buildHistoryPoolConfigPath(poolId, cursor);
2142
- const response = await apiClient.get(requestPath);
2143
- return {
2144
- items: response.items.map((item) => ({
2145
- type: item.type,
2146
- poolId: item.poolId,
2147
- asset: item.asset,
2148
- chain: item.chain,
2149
- timestamp: item.timestamp,
2150
- totalSupply: parseBigInt(item.totalSupply, "pool history totalSupply"),
2151
- totalDebt: parseBigInt(item.totalDebt, "pool history totalDebt"),
2152
- supplyCap: parseOptionalBigInt(
2153
- item.supplyCap,
2154
- "pool history supplyCap"
2155
- ),
2156
- borrowCap: parseOptionalBigInt(
2157
- item.borrowCap,
2158
- "pool history borrowCap"
2159
- ),
2160
- maxLtv: parseBigInt(item.maxLtv, "pool history maxLtv"),
2161
- liquidationThreshold: parseBigInt(
2162
- item.liquidationThreshold,
2163
- "pool history liquidationThreshold"
2164
- ),
2165
- liquidationBonus: parseBigInt(
2166
- item.liquidationBonus,
2167
- "pool history liquidationBonus"
2168
- ),
2169
- protocolLiquidationFee: parseBigInt(
2170
- item.protocolLiquidationFee,
2171
- "pool history protocolLiquidationFee"
2172
- ),
2173
- reserveFactor: parseBigInt(
2174
- item.reserveFactor,
2175
- "pool history reserveFactor"
2176
- ),
2177
- baseRate: parseBigInt(item.baseRate, "pool history baseRate"),
2178
- optimalUtilizationRate: parseBigInt(
2179
- item.optimalUtilizationRate,
2180
- "pool history optimalUtilizationRate"
2181
- ),
2182
- rateSlopeBefore: parseBigInt(
2183
- item.rateSlopeBefore,
2184
- "pool history rateSlopeBefore"
2185
- ),
2186
- rateSlopeAfter: parseBigInt(
2187
- item.rateSlopeAfter,
2188
- "pool history rateSlopeAfter"
2189
- ),
2190
- lendingIndex: parseBigInt(
2191
- item.lendingIndex,
2192
- "pool history lendingIndex"
2193
- ),
2194
- borrowIndex: parseBigInt(item.borrowIndex, "pool history borrowIndex"),
2195
- sameAssetBorrowing: item.sameAssetBorrowing,
2196
- frozen: item.frozen
2197
- })),
2198
- nextCursor: response.nextCursor
2199
- };
2200
- }
2201
- /**
2202
- * Returns borrow rate history for a pool.
2020
+ * Returns transaction history for a user.
2203
2021
  *
2204
- * @param poolId - The pool principal text.
2205
- * @param window - Optional time window with from/to timestamps and limit.
2206
- * @returns Paginated APY samples.
2022
+ * @param user - The Liquidium profile principal text.
2023
+ * @param filters - Optional pool, operation, state, time range, and pagination filters.
2024
+ * @returns Paginated user history entries.
2207
2025
  */
2208
- async getBorrowRateHistory(poolId, window = {}) {
2209
- const apiClient = this.requireApi();
2210
- const query = createHistoryWindowQuery(window);
2211
- const requestPath = buildHistoryRatesPath(poolId, query);
2212
- const response = await apiClient.get(requestPath);
2213
- return {
2214
- items: response.items.map((item) => ({
2215
- date: item.date,
2216
- rateDecimals: RATE_DECIMALS,
2217
- avgRate: parseBigInt(item.avgRate, "borrow rate")
2218
- })),
2219
- nextCursor: response.nextCursor
2220
- };
2221
- }
2222
- async getUserTransactionHistory(user, marketOrFilters, filters = {}) {
2026
+ async getUserTransactionHistory(user, filters = {}) {
2223
2027
  const apiClient = this.requireApi();
2224
- const normalizedFilters = normalizeTransactionHistoryFilters(
2225
- marketOrFilters,
2226
- filters
2227
- );
2228
2028
  const query = new URLSearchParams();
2229
- if (normalizedFilters.cursor) {
2230
- query.set(SdkApiQueryParam.cursor, normalizedFilters.cursor);
2029
+ if (filters.cursor) {
2030
+ query.set(SdkApiQueryParam.cursor, filters.cursor);
2231
2031
  }
2232
- if (normalizedFilters.market) {
2233
- query.set(SdkApiQueryParam.market, normalizedFilters.market);
2032
+ if (filters.market) {
2033
+ query.set(SdkApiQueryParam.market, filters.market);
2234
2034
  }
2235
- if (normalizedFilters.poolId) {
2236
- query.set(SdkApiQueryParam.poolId, normalizedFilters.poolId);
2035
+ if (filters.poolId) {
2036
+ query.set(SdkApiQueryParam.poolId, filters.poolId);
2237
2037
  }
2238
- if (normalizedFilters.types?.length) {
2239
- query.set(SdkApiQueryParam.types, normalizedFilters.types.join(","));
2038
+ if (filters.operations?.length) {
2039
+ query.set(SdkApiQueryParam.operations, filters.operations.join(","));
2240
2040
  }
2241
- if (normalizedFilters.statuses?.length) {
2041
+ if (filters.states?.length) {
2242
2042
  query.set(
2243
- SdkApiQueryParam.statuses,
2244
- normalizedFilters.statuses.map(mapHistoryStatusToApi).join(",")
2043
+ SdkApiQueryParam.states,
2044
+ createHistoryStateFilterParam(filters.states)
2245
2045
  );
2246
2046
  }
2247
- if (normalizedFilters.from) {
2248
- query.set(SdkApiQueryParam.from, normalizedFilters.from);
2047
+ if (filters.from) {
2048
+ query.set(SdkApiQueryParam.from, filters.from);
2249
2049
  }
2250
- if (normalizedFilters.to) {
2251
- query.set(SdkApiQueryParam.to, normalizedFilters.to);
2050
+ if (filters.to) {
2051
+ query.set(SdkApiQueryParam.to, filters.to);
2252
2052
  }
2253
- if (normalizedFilters.limit !== void 0) {
2254
- query.set(SdkApiQueryParam.limit, String(normalizedFilters.limit));
2053
+ if (filters.limit !== void 0) {
2054
+ query.set(SdkApiQueryParam.limit, String(filters.limit));
2255
2055
  }
2256
2056
  const requestPath = buildHistoryUserTransactionsPath(user, query);
2257
2057
  const response = await apiClient.get(requestPath);
@@ -2260,30 +2060,33 @@ var HistoryModule = class {
2260
2060
  nextCursor: response.nextCursor
2261
2061
  };
2262
2062
  }
2263
- async getLiquidationHistory(user, marketOrFilters, filters = {}) {
2063
+ /**
2064
+ * Returns liquidation history for a user.
2065
+ *
2066
+ * @param user - The Liquidium profile principal text.
2067
+ * @param filters - Optional pool, time range, and pagination filters.
2068
+ * @returns Paginated liquidation history entries.
2069
+ */
2070
+ async getLiquidationHistory(user, filters = {}) {
2264
2071
  const apiClient = this.requireApi();
2265
- const normalizedFilters = normalizeLiquidationHistoryFilters(
2266
- marketOrFilters,
2267
- filters
2268
- );
2269
2072
  const query = new URLSearchParams();
2270
- if (normalizedFilters.cursor) {
2271
- query.set(SdkApiQueryParam.cursor, normalizedFilters.cursor);
2073
+ if (filters.cursor) {
2074
+ query.set(SdkApiQueryParam.cursor, filters.cursor);
2272
2075
  }
2273
- if (normalizedFilters.market) {
2274
- query.set(SdkApiQueryParam.market, normalizedFilters.market);
2076
+ if (filters.market) {
2077
+ query.set(SdkApiQueryParam.market, filters.market);
2275
2078
  }
2276
- if (normalizedFilters.poolId) {
2277
- query.set(SdkApiQueryParam.poolId, normalizedFilters.poolId);
2079
+ if (filters.poolId) {
2080
+ query.set(SdkApiQueryParam.poolId, filters.poolId);
2278
2081
  }
2279
- if (normalizedFilters.from) {
2280
- query.set(SdkApiQueryParam.from, normalizedFilters.from);
2082
+ if (filters.from) {
2083
+ query.set(SdkApiQueryParam.from, filters.from);
2281
2084
  }
2282
- if (normalizedFilters.to) {
2283
- query.set(SdkApiQueryParam.to, normalizedFilters.to);
2085
+ if (filters.to) {
2086
+ query.set(SdkApiQueryParam.to, filters.to);
2284
2087
  }
2285
- if (normalizedFilters.limit !== void 0) {
2286
- query.set(SdkApiQueryParam.limit, String(normalizedFilters.limit));
2088
+ if (filters.limit !== void 0) {
2089
+ query.set(SdkApiQueryParam.limit, String(filters.limit));
2287
2090
  }
2288
2091
  const requestPath = buildHistoryUserLiquidationsPath(user, query);
2289
2092
  const response = await apiClient.get(requestPath);
@@ -2296,77 +2099,59 @@ var HistoryModule = class {
2296
2099
  function mapUserTransactionHistoryEntry(item) {
2297
2100
  return {
2298
2101
  id: item.id,
2299
- type: mapUserTransactionHistoryType(item.type),
2300
2102
  amount: parseBigInt(item.amount, "history user amount"),
2301
2103
  poolId: item.poolId,
2302
2104
  timestamp: item.timestamp,
2303
- status: mapHistoryStatusFromApi(item.status),
2105
+ status: item.status,
2304
2106
  txids: item.txids
2305
2107
  };
2306
2108
  }
2307
2109
  function mapUserLiquidationHistoryEntry(item) {
2308
- if (item.type !== "liquidation") {
2110
+ if (item.status.operation !== "liquidation" || item.status.state !== "completed") {
2309
2111
  throw new LiquidiumError(
2310
2112
  LiquidiumErrorCode.INTERNAL,
2311
- `Invalid liquidation history type: ${item.type}`
2113
+ `Invalid liquidation history status: ${item.status.state}`
2312
2114
  );
2313
2115
  }
2314
2116
  return {
2315
2117
  id: item.id,
2316
- type: "liquidation",
2317
2118
  amount: parseBigInt(item.amount, "history user amount"),
2318
2119
  poolId: item.poolId,
2319
2120
  timestamp: item.timestamp,
2320
- status: mapLiquidationHistoryStatus(item.status),
2121
+ status: item.status,
2321
2122
  txids: item.txids
2322
2123
  };
2323
2124
  }
2324
- function mapUserTransactionHistoryType(type) {
2325
- if (type !== "liquidation") {
2326
- return type;
2327
- }
2328
- throw new LiquidiumError(
2329
- LiquidiumErrorCode.INTERNAL,
2330
- "Transaction history response included a liquidation entry"
2331
- );
2332
- }
2333
- function mapLiquidationHistoryStatus(status) {
2334
- const mappedStatus = mapHistoryStatusFromApi(status);
2335
- if (mappedStatus === UserHistoryStatus.confirmed) {
2336
- return mappedStatus;
2337
- }
2338
- throw new LiquidiumError(
2339
- LiquidiumErrorCode.INTERNAL,
2340
- `Invalid liquidation history status: ${status}`
2341
- );
2342
- }
2343
- function createHistoryWindowQuery(window) {
2344
- const query = new URLSearchParams();
2345
- if (window.cursor) query.set(SdkApiQueryParam.cursor, window.cursor);
2346
- if (window.from) query.set(SdkApiQueryParam.from, window.from);
2347
- if (window.to) query.set(SdkApiQueryParam.to, window.to);
2348
- if (window.limit !== void 0) {
2349
- query.set(SdkApiQueryParam.limit, String(window.limit));
2125
+ function createHistoryStateFilterParam(states) {
2126
+ for (const state of states) {
2127
+ validateHistoryStateFilter(state);
2350
2128
  }
2351
- return query;
2129
+ return [...new Set(states)].join(",");
2352
2130
  }
2353
- function normalizeTransactionHistoryFilters(marketOrFilters, filters) {
2354
- if (typeof marketOrFilters === "string") {
2355
- return { ...filters, market: marketOrFilters };
2356
- }
2357
- return { ...marketOrFilters ?? {}, ...filters };
2358
- }
2359
- function normalizeLiquidationHistoryFilters(marketOrFilters, filters) {
2360
- if (typeof marketOrFilters === "string") {
2361
- return { ...filters, market: marketOrFilters };
2131
+ function validateHistoryStateFilter(state) {
2132
+ switch (state) {
2133
+ case "action_required":
2134
+ case "confirming":
2135
+ case "processing":
2136
+ case "completed":
2137
+ case "failed":
2138
+ return;
2139
+ default:
2140
+ throw new LiquidiumError(
2141
+ LiquidiumErrorCode.VALIDATION_ERROR,
2142
+ `History state filter is not supported: ${state}`
2143
+ );
2362
2144
  }
2363
- return { ...marketOrFilters ?? {}, ...filters };
2364
2145
  }
2365
- function mapHistoryStatusFromApi(status) {
2366
- return status.toLowerCase();
2367
- }
2368
- function mapHistoryStatusToApi(status) {
2369
- return status.toUpperCase();
2146
+
2147
+ // src/core/status.ts
2148
+ function createLiquidiumStatus(params) {
2149
+ return {
2150
+ operation: params.operation,
2151
+ state: params.state,
2152
+ confirmations: params.confirmations ?? null,
2153
+ requiredConfirmations: params.requiredConfirmations ?? null
2154
+ };
2370
2155
  }
2371
2156
 
2372
2157
  // src/core/utils/api-response-parsers.ts
@@ -3035,46 +2820,229 @@ function createDepositAccountsActor(canisterContext) {
3035
2820
  canisterId
3036
2821
  });
3037
2822
  }
3038
-
3039
- // src/core/utils/inflow-subaccount.ts
3040
- var INFLOW_DEPOSIT_PREFIX = 1;
3041
- var INFLOW_REPAY_PREFIX = 2;
3042
- var MAX_PRINCIPAL_BYTES = 29;
3043
- var SUBACCOUNT_LENGTH = 32;
3044
- var PRINCIPAL_LENGTH_OFFSET = 2;
3045
- var PRINCIPAL_START_OFFSET = 3;
3046
- function encodeInflowSubaccount(request) {
3047
- const subaccount = new Uint8Array(SUBACCOUNT_LENGTH);
3048
- const principalBytes = request.principal.toUint8Array();
3049
- if (principalBytes.length > MAX_PRINCIPAL_BYTES) {
2823
+ var flexibleLendingIdlFactory = ({ IDL }) => {
2824
+ const PoolRecord = IDL.Record({
2825
+ principal: IDL.Principal,
2826
+ asset: IDL.Unknown,
2827
+ chain: IDL.Unknown,
2828
+ total_supply_at_last_sync: IDL.Nat,
2829
+ total_debt_at_last_sync: IDL.Nat,
2830
+ supply_cap: IDL.Opt(IDL.Nat),
2831
+ borrow_cap: IDL.Opt(IDL.Nat),
2832
+ max_ltv: IDL.Nat64,
2833
+ liquidation_threshold: IDL.Nat64,
2834
+ liquidation_bonus: IDL.Nat64,
2835
+ protocol_liquidation_fee: IDL.Nat64,
2836
+ reserve_factor: IDL.Nat64,
2837
+ base_rate: IDL.Nat,
2838
+ optimal_utilization_rate: IDL.Nat,
2839
+ rate_slope_before: IDL.Nat,
2840
+ rate_slope_after: IDL.Nat,
2841
+ lending_index: IDL.Nat,
2842
+ borrow_index: IDL.Nat,
2843
+ same_asset_borrowing: IDL.Opt(IDL.Bool),
2844
+ frozen: IDL.Bool,
2845
+ last_updated: IDL.Opt(IDL.Nat64)
2846
+ });
2847
+ const BorrowingPowerRecord = IDL.Record({
2848
+ max_borrowable_usd: IDL.Nat,
2849
+ weighted_max_ltv: IDL.Nat
2850
+ });
2851
+ const PositionRecord = IDL.Record({
2852
+ asset: IDL.Unknown,
2853
+ total_debt_interest: IDL.Nat,
2854
+ borrow_index_snapshot: IDL.Nat,
2855
+ lending_index_snapshot: IDL.Nat,
2856
+ debt_scaled: IDL.Nat,
2857
+ total_earned_interest: IDL.Nat,
2858
+ deposit_scaled: IDL.Nat,
2859
+ pool_id: IDL.Principal,
2860
+ unpaid_debt_interest: IDL.Nat,
2861
+ last_update: IDL.Nat64,
2862
+ user_profile: IDL.Principal
2863
+ });
2864
+ const UserStatsRecord = IDL.Record({
2865
+ debt: IDL.Nat,
2866
+ collateral: IDL.Nat,
2867
+ acumulated_interest: IDL.Nat,
2868
+ borrowing_power: BorrowingPowerRecord,
2869
+ positions: IDL.Vec(PositionRecord),
2870
+ weighted_liquidation_threshold: IDL.Nat
2871
+ });
2872
+ const PositionViewRecord = IDL.Record({
2873
+ lending_index_now: IDL.Nat,
2874
+ interest_since_snapshot: IDL.Nat,
2875
+ asset: IDL.Unknown,
2876
+ total_debt_interest: IDL.Nat,
2877
+ borrow_index_snapshot: IDL.Nat,
2878
+ debt_native_now: IDL.Nat,
2879
+ borrow_index_now: IDL.Nat,
2880
+ lending_index_snapshot: IDL.Nat,
2881
+ debt_scaled: IDL.Nat,
2882
+ total_earned_interest: IDL.Nat,
2883
+ deposit_scaled: IDL.Nat,
2884
+ earned_since_snapshot: IDL.Nat,
2885
+ deposited_native_now: IDL.Nat,
2886
+ pool_id: IDL.Principal,
2887
+ last_update: IDL.Nat64,
2888
+ user_profile: IDL.Principal
2889
+ });
2890
+ return IDL.Service({
2891
+ list_pools: IDL.Func([], [IDL.Vec(PoolRecord)], ["query"]),
2892
+ get_pool_rate: IDL.Func(
2893
+ [IDL.Principal],
2894
+ [IDL.Opt(IDL.Tuple(IDL.Nat, IDL.Nat, IDL.Nat))],
2895
+ ["query"]
2896
+ ),
2897
+ get_health_factor: IDL.Func(
2898
+ [IDL.Principal],
2899
+ [IDL.Nat, UserStatsRecord],
2900
+ ["query"]
2901
+ ),
2902
+ get_profile_stats: IDL.Func([IDL.Principal], [UserStatsRecord], ["query"]),
2903
+ get_position: IDL.Func(
2904
+ [IDL.Principal, IDL.Principal],
2905
+ [IDL.Opt(PositionViewRecord)],
2906
+ ["query"]
2907
+ )
2908
+ });
2909
+ };
2910
+ function createFlexibleLendingActor(canisterContext) {
2911
+ const canisterId = canisterContext.canisterIds.lending;
2912
+ if (!canisterId) {
3050
2913
  throw new LiquidiumError(
3051
- LiquidiumErrorCode.VALIDATION_ERROR,
3052
- "Principal length exceeds inflow subaccount capacity"
2914
+ LiquidiumErrorCode.SERVICE_UNAVAILABLE,
2915
+ "Lending canister ID is not configured"
3053
2916
  );
3054
2917
  }
3055
- subaccount[0] = request.action === SupplyAction.deposit ? INFLOW_DEPOSIT_PREFIX : INFLOW_REPAY_PREFIX;
3056
- subaccount[PRINCIPAL_LENGTH_OFFSET] = principalBytes.length;
3057
- subaccount.set(principalBytes, PRINCIPAL_START_OFFSET);
3058
- return subaccount;
2918
+ return Actor.createActor(flexibleLendingIdlFactory, {
2919
+ agent: canisterContext.agent,
2920
+ canisterId
2921
+ });
3059
2922
  }
3060
-
3061
- // src/modules/lending/types.ts
3062
- var SupplyPlanType = {
3063
- contractInteraction: "contractInteraction",
3064
- transfer: "transfer"
3065
- };
3066
- var EvmSupplyApprovalStrategy = {
3067
- approveMax: "approve-max",
3068
- none: "none",
3069
- resetThenApproveMax: "reset-then-approve-max"
3070
- };
3071
-
3072
- // src/modules/lending/_internal/supply-targets.ts
3073
- async function resolveSupplyTarget(canisterContext, request) {
3074
- const selectedPool = await getPoolById(canisterContext, request.poolId);
3075
- const asset = getVariantKey(selectedPool.asset);
3076
- const chain = getVariantKey(selectedPool.chain);
3077
- const mechanism = resolveSupplyMechanism({
2923
+ function decodeFlexiblePool(pool) {
2924
+ const asset = extractVariantTag(pool.asset, KNOWN_ASSET_TAGS);
2925
+ const chain = extractVariantTag(pool.chain, KNOWN_CHAIN_TAGS);
2926
+ if (!asset || !chain) {
2927
+ return null;
2928
+ }
2929
+ return {
2930
+ principal: pool.principal,
2931
+ asset,
2932
+ chain,
2933
+ total_supply_at_last_sync: pool.total_supply_at_last_sync,
2934
+ total_debt_at_last_sync: pool.total_debt_at_last_sync,
2935
+ supply_cap: pool.supply_cap,
2936
+ borrow_cap: pool.borrow_cap,
2937
+ max_ltv: pool.max_ltv,
2938
+ liquidation_threshold: pool.liquidation_threshold,
2939
+ liquidation_bonus: pool.liquidation_bonus,
2940
+ protocol_liquidation_fee: pool.protocol_liquidation_fee,
2941
+ reserve_factor: pool.reserve_factor,
2942
+ base_rate: pool.base_rate,
2943
+ optimal_utilization_rate: pool.optimal_utilization_rate,
2944
+ rate_slope_before: pool.rate_slope_before,
2945
+ rate_slope_after: pool.rate_slope_after,
2946
+ lending_index: pool.lending_index,
2947
+ borrow_index: pool.borrow_index,
2948
+ same_asset_borrowing: pool.same_asset_borrowing,
2949
+ frozen: pool.frozen,
2950
+ last_updated: pool.last_updated
2951
+ };
2952
+ }
2953
+ function decodeFlexiblePosition(position) {
2954
+ const asset = extractVariantTag(position.asset, KNOWN_ASSET_TAGS);
2955
+ if (!asset) {
2956
+ return null;
2957
+ }
2958
+ return {
2959
+ asset,
2960
+ total_debt_interest: position.total_debt_interest,
2961
+ borrow_index_snapshot: position.borrow_index_snapshot,
2962
+ lending_index_snapshot: position.lending_index_snapshot,
2963
+ debt_scaled: position.debt_scaled,
2964
+ total_earned_interest: position.total_earned_interest,
2965
+ deposit_scaled: position.deposit_scaled,
2966
+ pool_id: position.pool_id,
2967
+ unpaid_debt_interest: position.unpaid_debt_interest,
2968
+ last_update: position.last_update,
2969
+ user_profile: position.user_profile
2970
+ };
2971
+ }
2972
+ function decodeFlexiblePositionView(view) {
2973
+ const asset = extractVariantTag(view.asset, KNOWN_ASSET_TAGS);
2974
+ if (!asset) {
2975
+ return null;
2976
+ }
2977
+ return {
2978
+ lending_index_now: view.lending_index_now,
2979
+ interest_since_snapshot: view.interest_since_snapshot,
2980
+ asset,
2981
+ total_debt_interest: view.total_debt_interest,
2982
+ borrow_index_snapshot: view.borrow_index_snapshot,
2983
+ debt_native_now: view.debt_native_now,
2984
+ borrow_index_now: view.borrow_index_now,
2985
+ lending_index_snapshot: view.lending_index_snapshot,
2986
+ debt_scaled: view.debt_scaled,
2987
+ total_earned_interest: view.total_earned_interest,
2988
+ deposit_scaled: view.deposit_scaled,
2989
+ earned_since_snapshot: view.earned_since_snapshot,
2990
+ deposited_native_now: view.deposited_native_now,
2991
+ pool_id: view.pool_id,
2992
+ last_update: view.last_update,
2993
+ user_profile: view.user_profile
2994
+ };
2995
+ }
2996
+ function decodeFlexibleUserStats(stats) {
2997
+ return {
2998
+ debt: stats.debt,
2999
+ collateral: stats.collateral,
3000
+ acumulated_interest: stats.acumulated_interest,
3001
+ borrowing_power: stats.borrowing_power,
3002
+ positions: stats.positions.map(decodeFlexiblePosition).filter((position) => position !== null),
3003
+ weighted_liquidation_threshold: stats.weighted_liquidation_threshold
3004
+ };
3005
+ }
3006
+
3007
+ // src/core/utils/inflow-subaccount.ts
3008
+ var INFLOW_DEPOSIT_PREFIX = 1;
3009
+ var INFLOW_REPAY_PREFIX = 2;
3010
+ var MAX_PRINCIPAL_BYTES = 29;
3011
+ var SUBACCOUNT_LENGTH = 32;
3012
+ var PRINCIPAL_LENGTH_OFFSET = 2;
3013
+ var PRINCIPAL_START_OFFSET = 3;
3014
+ function encodeInflowSubaccount(request) {
3015
+ const subaccount = new Uint8Array(SUBACCOUNT_LENGTH);
3016
+ const principalBytes = request.principal.toUint8Array();
3017
+ if (principalBytes.length > MAX_PRINCIPAL_BYTES) {
3018
+ throw new LiquidiumError(
3019
+ LiquidiumErrorCode.VALIDATION_ERROR,
3020
+ "Principal length exceeds inflow subaccount capacity"
3021
+ );
3022
+ }
3023
+ subaccount[0] = request.action === SupplyAction.deposit ? INFLOW_DEPOSIT_PREFIX : INFLOW_REPAY_PREFIX;
3024
+ subaccount[PRINCIPAL_LENGTH_OFFSET] = principalBytes.length;
3025
+ subaccount.set(principalBytes, PRINCIPAL_START_OFFSET);
3026
+ return subaccount;
3027
+ }
3028
+
3029
+ // src/modules/lending/types.ts
3030
+ var SupplyPlanType = {
3031
+ contractInteraction: "contractInteraction",
3032
+ transfer: "transfer"
3033
+ };
3034
+ var EvmSupplyApprovalStrategy = {
3035
+ approveMax: "approve-max",
3036
+ none: "none",
3037
+ resetThenApproveMax: "reset-then-approve-max"
3038
+ };
3039
+
3040
+ // src/modules/lending/_internal/supply-targets.ts
3041
+ async function resolveSupplyTarget(canisterContext, request) {
3042
+ const selectedPool = await getPoolById(canisterContext, request.poolId);
3043
+ const asset = selectedPool.asset;
3044
+ const chain = selectedPool.chain;
3045
+ const mechanism = resolveSupplyMechanism({
3078
3046
  asset,
3079
3047
  chain,
3080
3048
  requestedMechanism: request.mechanism
@@ -3179,15 +3147,16 @@ function mapDepositAccountErrorToLiquidiumError(error) {
3179
3147
  );
3180
3148
  }
3181
3149
  async function getPoolById(canisterContext, poolId) {
3182
- const pools = await createLendingActor(canisterContext).list_pools();
3150
+ const pools = await createFlexibleLendingActor(canisterContext).list_pools();
3183
3151
  const selectedPool = pools.find((pool) => pool.principal.toText() === poolId);
3184
- if (!selectedPool) {
3152
+ const decodedPool = selectedPool ? decodeFlexiblePool(selectedPool) : null;
3153
+ if (!decodedPool) {
3185
3154
  throw new LiquidiumError(
3186
3155
  LiquidiumErrorCode.POOL_NOT_FOUND,
3187
3156
  `Pool not found: ${poolId}`
3188
3157
  );
3189
3158
  }
3190
- return selectedPool;
3159
+ return decodedPool;
3191
3160
  }
3192
3161
  async function getNativeAddressSupplyTarget(canisterContext, profileId, request) {
3193
3162
  assertSupportsNativeAddressInflowTarget(request.asset, request.chain);
@@ -3779,20 +3748,11 @@ function getChainForInstantLoanAsset(asset) {
3779
3748
  return asset;
3780
3749
  }
3781
3750
 
3782
- // src/modules/instant-loans/types.ts
3783
- var InstantLoanStatus = {
3784
- awaitingDeposit: "awaiting_deposit",
3785
- depositDetected: "deposit_detected",
3786
- active: "active",
3787
- settling: "settling",
3788
- closed: "closed",
3789
- expired: "expired"
3790
- };
3791
-
3792
3751
  // src/modules/instant-loans/instant-loans.ts
3793
3752
  var REPAYMENT_BUFFER_SECONDS = 86400n;
3794
- var RATE_SCALE2 = 10n ** 27n;
3753
+ var RATE_SCALE = 10n ** 27n;
3795
3754
  var SECONDS_PER_YEAR = 31536000n;
3755
+ var MILLISECONDS_PER_SECOND3 = 1e3;
3796
3756
  var ETH_STABLECOIN_INFLOW_FEE_FALLBACK = 1500000n;
3797
3757
  var INSTANT_LOAN_MIN_SLIPPAGE_BUFFER_BPS = 200n;
3798
3758
  var INSTANT_LOAN_FIND_QUERY_MAX_LENGTH = 256;
@@ -3804,14 +3764,16 @@ var INSTANT_LOAN_ASSETS = [
3804
3764
  Asset.USDT
3805
3765
  ];
3806
3766
  var InstantLoansModule = class {
3807
- constructor(canisterContext, apiClient, lending, positions) {
3767
+ constructor(canisterContext, apiClient, activities, lending, positions) {
3808
3768
  this.canisterContext = canisterContext;
3809
3769
  this.apiClient = apiClient;
3770
+ this.activities = activities;
3810
3771
  this.lending = lending;
3811
3772
  this.positions = positions;
3812
3773
  }
3813
3774
  canisterContext;
3814
3775
  apiClient;
3776
+ activities;
3815
3777
  lending;
3816
3778
  positions;
3817
3779
  /**
@@ -4011,7 +3973,8 @@ var InstantLoansModule = class {
4011
3973
  const response = await apiClient.get(
4012
3974
  buildInstantLoanFindPath({ query })
4013
3975
  );
4014
- return (response.candidates ?? response.loans ?? []).map(mapCandidateWire);
3976
+ const candidates = "candidates" in response ? response.candidates : response.loans;
3977
+ return candidates.map(mapCandidateWire);
4015
3978
  }
4016
3979
  async getLoanRecord(loanId) {
4017
3980
  try {
@@ -4050,6 +4013,7 @@ var InstantLoansModule = class {
4050
4013
  borrowAmount: record.borrow_amount,
4051
4014
  borrowDestination: accountFromCanister(record.borrow_destination),
4052
4015
  refundDestination: accountFromCanister(record.refund_destination),
4016
+ started: record.started,
4053
4017
  depositDetectedTimestamp: record.deposit_detected_ts[0] ?? null,
4054
4018
  expiryTimestamp: record.expires_at[0] ?? deriveDepositExpiryTimestamp({
4055
4019
  depositDetectedTimestamp: record.deposit_detected_ts[0] ?? null,
@@ -4078,7 +4042,8 @@ var InstantLoansModule = class {
4078
4042
  repayTarget,
4079
4043
  collateralPosition,
4080
4044
  borrowPosition,
4081
- borrowPoolRate
4045
+ borrowPoolRate,
4046
+ activeActivities
4082
4047
  ] = await Promise.all([
4083
4048
  resolveSupplyTarget(this.canisterContext, {
4084
4049
  profileId,
@@ -4094,7 +4059,8 @@ var InstantLoansModule = class {
4094
4059
  }),
4095
4060
  this.positions.getPosition(profileId, collateralPoolId),
4096
4061
  this.positions.getPosition(profileId, borrowPoolId),
4097
- this.positions.market.getPoolRate(borrowPoolId)
4062
+ this.positions.market.getPoolRate(borrowPoolId),
4063
+ this.activities.list({ profileId, filter: ActivityFilter.active })
4098
4064
  ]);
4099
4065
  const totalDebtAmount = calculateTotalDebtAmount(borrowPosition);
4100
4066
  const interestBufferAmount = calculateInterestBufferAmount(
@@ -4111,8 +4077,12 @@ var InstantLoansModule = class {
4111
4077
  const borrowedDecimals = borrowPosition?.borrowedDecimals ?? getAssetNativeDecimals(borrowAsset);
4112
4078
  const debtInterestAmount = borrowPosition?.debtInterest ?? 0n;
4113
4079
  const status = deriveInstantLoanStatus({
4080
+ started: input.started,
4081
+ depositDetectedTimestamp: input.depositDetectedTimestamp,
4082
+ expiryTimestamp: input.expiryTimestamp,
4114
4083
  collateralAmount: currentCollateralAmount,
4115
- totalDebtAmount
4084
+ totalDebtAmount,
4085
+ activeActivities
4116
4086
  });
4117
4087
  const initialDeposit = await this.createInitialDepositQuote({
4118
4088
  collateralAmount,
@@ -4264,7 +4234,7 @@ function calculateInterestBufferAmount(borrowPosition, annualBorrowRate) {
4264
4234
  if (totalDebtAmount <= 0n || annualBorrowRate <= 0n) {
4265
4235
  return 0n;
4266
4236
  }
4267
- const interestBuffer = totalDebtAmount * annualBorrowRate * REPAYMENT_BUFFER_SECONDS / RATE_SCALE2 / SECONDS_PER_YEAR;
4237
+ const interestBuffer = totalDebtAmount * annualBorrowRate * REPAYMENT_BUFFER_SECONDS / RATE_SCALE / SECONDS_PER_YEAR;
4268
4238
  return interestBuffer > 1n ? interestBuffer : 1n;
4269
4239
  }
4270
4240
  function calculateTotalDebtAmount(borrowPosition) {
@@ -4274,13 +4244,75 @@ function calculateTotalDebtAmount(borrowPosition) {
4274
4244
  return borrowPosition.borrowed + borrowPosition.debtInterest;
4275
4245
  }
4276
4246
  function deriveInstantLoanStatus(input) {
4247
+ const activeActivityStatus = deriveActiveInstantLoanActivityStatus(input);
4248
+ if (activeActivityStatus) {
4249
+ return activeActivityStatus;
4250
+ }
4277
4251
  if (input.totalDebtAmount > 0n) {
4278
- return InstantLoanStatus.active;
4252
+ return createLiquidiumStatus({
4253
+ operation: "repayment",
4254
+ state: "active"
4255
+ });
4256
+ }
4257
+ if (input.started) {
4258
+ return createLiquidiumStatus({
4259
+ operation: "repayment",
4260
+ state: "completed"
4261
+ });
4262
+ }
4263
+ if (isInstantLoanDepositExpired(input)) {
4264
+ return createLiquidiumStatus({
4265
+ operation: "deposit",
4266
+ state: "expired"
4267
+ });
4279
4268
  }
4280
4269
  if (input.collateralAmount > 0n) {
4281
- return InstantLoanStatus.depositDetected;
4270
+ return createLiquidiumStatus({
4271
+ operation: "deposit",
4272
+ state: "processing"
4273
+ });
4274
+ }
4275
+ if (input.depositDetectedTimestamp !== null) {
4276
+ return createLiquidiumStatus({
4277
+ operation: "deposit",
4278
+ state: "confirming"
4279
+ });
4280
+ }
4281
+ return createLiquidiumStatus({
4282
+ operation: "deposit",
4283
+ state: "action_required"
4284
+ });
4285
+ }
4286
+ function deriveActiveInstantLoanActivityStatus(input) {
4287
+ if (!input.started) {
4288
+ return findActiveActivityStatus(input.activeActivities, "deposit");
4289
+ }
4290
+ const borrowStatus = findActiveActivityStatus(
4291
+ input.activeActivities,
4292
+ "borrow"
4293
+ );
4294
+ if (borrowStatus) {
4295
+ return borrowStatus;
4296
+ }
4297
+ return findActiveActivityStatus(input.activeActivities, "repayment");
4298
+ }
4299
+ function findActiveActivityStatus(activities, operation) {
4300
+ const activity = activities.find(
4301
+ (candidate) => candidate.status.operation === operation
4302
+ );
4303
+ return activity?.status ?? null;
4304
+ }
4305
+ function isInstantLoanDepositExpired(input) {
4306
+ if (input.started || input.depositDetectedTimestamp === null) {
4307
+ return false;
4282
4308
  }
4283
- return InstantLoanStatus.awaitingDeposit;
4309
+ if (input.expiryTimestamp === null) {
4310
+ return false;
4311
+ }
4312
+ return input.expiryTimestamp <= getCurrentUnixTimestampSeconds();
4313
+ }
4314
+ function getCurrentUnixTimestampSeconds() {
4315
+ return BigInt(Math.floor(Date.now() / MILLISECONDS_PER_SECOND3));
4284
4316
  }
4285
4317
  function deriveDepositExpiryTimestamp(input) {
4286
4318
  if (input.depositDetectedTimestamp === null) {
@@ -4334,8 +4366,32 @@ function accountFromCanister(account) {
4334
4366
  if ("Native" in account) {
4335
4367
  return { type: "Native", principal: account.Native.toText() };
4336
4368
  }
4369
+ if ("AccountIdentifier" in account) {
4370
+ return {
4371
+ type: "AccountIdentifier",
4372
+ address: account.AccountIdentifier
4373
+ };
4374
+ }
4375
+ if ("Icrc" in account) {
4376
+ const subaccount = normalizeOptionalSubaccount(account.Icrc.subaccount[0]);
4377
+ return {
4378
+ type: "Icrc",
4379
+ owner: account.Icrc.owner.toText(),
4380
+ subaccount,
4381
+ address: encodeIcrcAccount({
4382
+ owner: account.Icrc.owner,
4383
+ subaccount
4384
+ })
4385
+ };
4386
+ }
4337
4387
  return { type: "External", address: account.External };
4338
4388
  }
4389
+ function normalizeOptionalSubaccount(subaccount) {
4390
+ if (!subaccount) {
4391
+ return void 0;
4392
+ }
4393
+ return subaccount instanceof Uint8Array ? subaccount : Uint8Array.from(subaccount);
4394
+ }
4339
4395
  function mapInstantLoanEvent(event) {
4340
4396
  return {
4341
4397
  id: event.id,
@@ -4705,6 +4761,28 @@ function accountTypeToString(accountType) {
4705
4761
  }
4706
4762
  }
4707
4763
 
4764
+ // src/modules/lending/_internal/inflow-fee-rounding.ts
4765
+ var ETH_STABLECOIN_INFLOW_FEE_ROUND_UP_TO_NEAREST_10_CENTS = 100000n;
4766
+ var BTC_INFLOW_FEE_ROUND_UP_TO_NEAREST_SATS = 500n;
4767
+ function roundInflowFeeEstimate(request, totalFee) {
4768
+ if (totalFee <= 0n) {
4769
+ return 0n;
4770
+ }
4771
+ if (isEthStablecoin(request.asset, request.chain)) {
4772
+ return roundUpToNearest(
4773
+ totalFee,
4774
+ ETH_STABLECOIN_INFLOW_FEE_ROUND_UP_TO_NEAREST_10_CENTS
4775
+ );
4776
+ }
4777
+ if (request.asset === Asset.BTC && request.chain === Chain.BTC) {
4778
+ return roundUpToNearest(totalFee, BTC_INFLOW_FEE_ROUND_UP_TO_NEAREST_SATS);
4779
+ }
4780
+ return totalFee;
4781
+ }
4782
+ function roundUpToNearest(amount, roundingUnit) {
4783
+ return ceilDivBigint(amount, roundingUnit) * roundingUnit;
4784
+ }
4785
+
4708
4786
  // src/core/utils/retry.ts
4709
4787
  var MIN_ATTEMPTS = 1;
4710
4788
  var MIN_INITIAL_RETRY_DELAY_MS = 0;
@@ -4756,813 +4834,966 @@ function assertValidRetryOptions(options) {
4756
4834
  }
4757
4835
  }
4758
4836
 
4759
- // src/modules/lending/_internal/inflow-fee-rounding.ts
4760
- var ETH_STABLECOIN_INFLOW_FEE_ROUND_UP_TO_NEAREST_10_CENTS = 100000n;
4761
- var BTC_INFLOW_FEE_ROUND_UP_TO_NEAREST_SATS = 500n;
4762
- function roundInflowFeeEstimate(request, totalFee) {
4763
- if (totalFee <= 0n) {
4764
- return 0n;
4765
- }
4766
- if (isEthStablecoin(request.asset, request.chain)) {
4767
- return roundUpToNearest(
4768
- totalFee,
4769
- ETH_STABLECOIN_INFLOW_FEE_ROUND_UP_TO_NEAREST_10_CENTS
4770
- );
4771
- }
4772
- if (request.asset === Asset.BTC && request.chain === Chain.BTC) {
4773
- return roundUpToNearest(totalFee, BTC_INFLOW_FEE_ROUND_UP_TO_NEAREST_SATS);
4774
- }
4775
- return totalFee;
4776
- }
4777
- function roundUpToNearest(amount, roundingUnit) {
4778
- return ceilDivBigint(amount, roundingUnit) * roundingUnit;
4779
- }
4780
-
4781
- // src/modules/lending/mappers.ts
4782
- function mapCanisterOutflowDetails(outflow) {
4783
- const rawOutflowType = getVariantKey(outflow.outflow_type);
4784
- return {
4785
- id: outflow.id,
4786
- outflowType: normalizeOutflowType(rawOutflowType),
4787
- outflowRef: outflow.outflow_ref[0],
4788
- txid: outflow.txid[0],
4789
- amount: outflow.amount,
4790
- receiver: mapCanisterAccountType(outflow.receiver)
4791
- };
4792
- }
4793
- function mapCanisterAccountType(receiver) {
4794
- if ("Native" in receiver) {
4795
- return {
4796
- type: "Native",
4797
- account: receiver.Native.toText()
4798
- };
4799
- }
4800
- return {
4801
- type: "External",
4802
- account: receiver.External
4803
- };
4804
- }
4805
- function normalizeOutflowType(rawOutflowType) {
4806
- switch (rawOutflowType) {
4807
- case "Withdraw":
4808
- return OutflowType.withdraw;
4809
- case "Borrow":
4810
- return OutflowType.borrow;
4811
- case "FeeClaim":
4812
- return OutflowType.feeClaim;
4813
- default:
4814
- throw new LiquidiumError(
4815
- LiquidiumErrorCode.INTERNAL,
4816
- `Unsupported outflow type: ${rawOutflowType}`
4817
- );
4818
- }
4819
- }
4820
- function mapWalletChainToLendingChain(chain) {
4821
- switch (chain) {
4822
- case Chain.BTC:
4823
- return { BTC: null };
4824
- case Chain.ETH:
4825
- return { ETH: null };
4826
- }
4827
- }
4828
-
4829
- // src/modules/lending/lending.ts
4837
+ // src/modules/lending/_internal/supply-flow.ts
4830
4838
  var SUBMIT_INFLOW_MAX_ATTEMPTS = 4;
4831
4839
  var SUBMIT_INFLOW_INITIAL_RETRY_DELAY_MS = 1500;
4832
4840
  var SUBMIT_INFLOW_RETRY_BACKOFF_MULTIPLIER = 2;
4833
4841
  var ETH_APPROVAL_POLL_INTERVAL_MS = 2e3;
4834
4842
  var ETH_APPROVAL_MAX_POLLS = 30;
4835
- var LendingModule = class {
4836
- constructor(canisterContext, apiClient, evmReadClient) {
4837
- this.canisterContext = canisterContext;
4838
- this.apiClient = apiClient;
4839
- this.evmReadClient = evmReadClient;
4843
+ var SupplyFlowExecutor = class {
4844
+ constructor(params) {
4845
+ this.params = params;
4840
4846
  }
4841
- canisterContext;
4842
- apiClient;
4843
- evmReadClient;
4844
- /**
4845
- * Prepares a withdraw action that can be signed and submitted later.
4846
- *
4847
- * Use this when you need explicit control over signing and submission.
4848
- *
4849
- * @param request - Profile, pool, amount (pool asset base units), outflow address, and signer wallet.
4850
- * @returns A signable {@link WithdrawAction} with `submit` wired to the canister.
4851
- */
4852
- async prepareWithdraw(request) {
4853
- const destinationAccount = request.receiverAddress.trim();
4854
- const signerAccount = normalizeEvmAddress(
4855
- request.signerWalletAddress.trim()
4847
+ params;
4848
+ async create(request) {
4849
+ const target = await resolveSupplyTarget(
4850
+ this.params.canisterContext,
4851
+ request
4856
4852
  );
4857
- if (!destinationAccount) {
4853
+ const instruction = {
4854
+ poolId: request.poolId,
4855
+ asset: target.asset,
4856
+ chain: target.chain,
4857
+ action: request.action,
4858
+ target
4859
+ };
4860
+ const mechanism = resolveSupplyMechanism({
4861
+ asset: instruction.asset,
4862
+ chain: instruction.chain,
4863
+ requestedMechanism: request.mechanism
4864
+ });
4865
+ const defaultSubmitInflowRequest = getDefaultSubmitInflowRequest({
4866
+ action: request.action,
4867
+ chain: mechanism === SupplyPlanType.contractInteraction ? Chain.ETH : instruction.chain
4868
+ });
4869
+ let txid;
4870
+ switch (mechanism) {
4871
+ case SupplyPlanType.transfer:
4872
+ if (request.walletAdapter) {
4873
+ txid = await this.sendAndSubmitNativeSupplyInflow({
4874
+ request,
4875
+ instruction,
4876
+ defaultSubmitInflowRequest
4877
+ });
4878
+ }
4879
+ break;
4880
+ case SupplyPlanType.contractInteraction:
4881
+ txid = await this.executeContractSupply({
4882
+ request,
4883
+ instruction,
4884
+ defaultSubmitInflowRequest
4885
+ });
4886
+ break;
4887
+ }
4888
+ return {
4889
+ type: mechanism,
4890
+ target: instruction.target,
4891
+ txid,
4892
+ status: createLiquidiumStatus({
4893
+ operation: mapSupplyActionToStatusOperation(request.action),
4894
+ state: txid ? "confirming" : "action_required"
4895
+ }),
4896
+ submit: async (submitRequest) => {
4897
+ return await this.submitSupplyFlowInflow({
4898
+ instruction,
4899
+ mechanism,
4900
+ defaultSubmitInflowRequest,
4901
+ submitRequest
4902
+ });
4903
+ }
4904
+ };
4905
+ }
4906
+ async getEvmSupplyContext(request) {
4907
+ const selectedPool = await this.params.getPoolById(request.poolId);
4908
+ return await this.getEvmSupplyContextForPool({
4909
+ request,
4910
+ asset: selectedPool.asset,
4911
+ chain: selectedPool.chain
4912
+ });
4913
+ }
4914
+ async getEvmSupplyContextForPool(params) {
4915
+ const { request, asset, chain } = params;
4916
+ const evmReadClient = this.requireEvmReadClient(
4917
+ "EVM supply context requires an EVM RPC URL or public client"
4918
+ );
4919
+ const walletAddress = normalizeAndValidateEvmAddress(
4920
+ request.walletAddress,
4921
+ "Invalid EVM wallet address"
4922
+ );
4923
+ if (request.amount <= 0n) {
4858
4924
  throw new LiquidiumError(
4859
4925
  LiquidiumErrorCode.VALIDATION_ERROR,
4860
- "Withdraw requires a custom outflow account"
4926
+ "EVM supply context requires a positive amount"
4861
4927
  );
4862
4928
  }
4863
- if (!signerAccount) {
4929
+ if (!isEthStablecoin(asset, chain)) {
4864
4930
  throw new LiquidiumError(
4865
4931
  LiquidiumErrorCode.VALIDATION_ERROR,
4866
- "Withdraw requires a signer account"
4932
+ `EVM supply context only supports ETH stablecoin pools, received ${asset} on ${chain}`
4867
4933
  );
4868
4934
  }
4869
- const receiverAddress = await this.normalizeOutflowReceiverAddress({
4870
- poolId: request.poolId,
4871
- receiverAddress: destinationAccount
4935
+ const tokenAddress = getEthStablecoinContractAddress(asset);
4936
+ const spenderAddress = CK_ETH_DEPOSIT_CONTRACT_ADDRESS;
4937
+ const [allowance, balance] = await Promise.all([
4938
+ readErc20Allowance({
4939
+ evmReadClient,
4940
+ tokenAddress,
4941
+ ownerAddress: walletAddress,
4942
+ spenderAddress
4943
+ }),
4944
+ readErc20Balance({
4945
+ evmReadClient,
4946
+ tokenAddress,
4947
+ ownerAddress: walletAddress
4948
+ })
4949
+ ]);
4950
+ const approvalStrategy = getApprovalStrategy({
4951
+ allowance,
4952
+ amount: request.amount
4872
4953
  });
4873
- const lendingActor = createLendingActor(this.canisterContext);
4874
- try {
4875
- const expiryTimestamp = computeExpiryTimestampFromNow();
4876
- const nonce = await lendingActor.get_nonce(signerAccount);
4877
- const withdrawRequestData = {
4878
- profileId: request.profileId,
4879
- poolId: request.poolId,
4880
- amount: request.amount,
4881
- receiverAddress,
4882
- signerWalletAddress: signerAccount,
4883
- expiryTimestamp
4884
- };
4885
- return {
4886
- kind: WalletActionKind.createWithdraw,
4887
- executionKind: WalletExecutionKind.signMessage,
4888
- actionType: WalletActionKind.createWithdraw,
4889
- transferMode: TransferMode.native,
4890
- account: signerAccount,
4891
- message: createWithdrawAssetMessage(
4892
- {
4893
- pool_id: request.poolId,
4894
- amount: request.amount.toString(),
4895
- account: { type: "External", data: receiverAddress },
4896
- expiry_timestamp: expiryTimestamp
4897
- },
4898
- nonce
4899
- ),
4900
- data: withdrawRequestData,
4901
- submit: async (signatureInfo) => {
4902
- return await this.submitWithdraw(withdrawRequestData, signatureInfo);
4903
- }
4904
- };
4905
- } catch (error) {
4906
- if (error instanceof LiquidiumError) {
4907
- throw error;
4908
- }
4909
- throw mapCanisterCallErrorToLiquidiumError("get_nonce", error);
4910
- }
4911
- }
4912
- async submitWithdraw(request, signatureInfo) {
4913
- try {
4914
- const result = await createLendingActor(this.canisterContext).withdraw(
4915
- Principal.fromText(request.profileId),
4916
- {
4917
- data: {
4918
- expiry_timestamp: request.expiryTimestamp,
4919
- account: { External: request.receiverAddress },
4920
- pool_id: Principal.fromText(request.poolId),
4921
- amount: request.amount
4922
- },
4923
- signature_info: {
4924
- Wallet: {
4925
- signature: normalizeWalletSignature(
4926
- signatureInfo.signature,
4927
- signatureInfo.chain
4928
- ),
4929
- chain: mapWalletChainToLendingChain(signatureInfo.chain),
4930
- account: request.signerWalletAddress
4931
- }
4932
- }
4933
- }
4934
- );
4935
- if ("Err" in result) {
4936
- throw mapLendingProtocolErrorToLiquidiumError(result.Err);
4937
- }
4938
- return mapExpectedOutflowDetails(
4939
- mapCanisterOutflowDetails(result.Ok),
4940
- OutflowType.withdraw,
4941
- "withdraw"
4942
- );
4943
- } catch (error) {
4944
- if (error instanceof LiquidiumError) {
4945
- throw error;
4946
- }
4947
- throw mapCanisterCallErrorToLiquidiumError("withdraw", error);
4948
- }
4949
- }
4950
- /**
4951
- * Creates a withdraw outflow using the provided wallet adapter.
4952
- *
4953
- * This is the convenience form of `prepareWithdraw(...)` plus execution.
4954
- *
4955
- * @param params - Withdraw request fields plus `signerChain` and `signerWalletAdapter`.
4956
- * @returns The canister {@link OutflowDetails} for the completed withdraw.
4957
- */
4958
- async withdraw(params) {
4959
- const action = await this.prepareWithdraw(params);
4960
- return await executeWith({
4961
- walletAdapter: params.signerWalletAdapter,
4962
- chain: params.signerChain,
4963
- account: action.account
4964
- })(action);
4954
+ return {
4955
+ profileId: request.profileId,
4956
+ poolId: request.poolId,
4957
+ walletAddress,
4958
+ action: request.action,
4959
+ asset,
4960
+ chain: Chain.ETH,
4961
+ amount: request.amount.toString(),
4962
+ tokenAddress,
4963
+ spenderAddress,
4964
+ depositContractAddress: CK_ETH_DEPOSIT_CONTRACT_ADDRESS,
4965
+ balance: balance.toString(),
4966
+ allowance: allowance.toString(),
4967
+ requiresApproval: approvalStrategy !== EvmSupplyApprovalStrategy.none,
4968
+ approvalStrategy
4969
+ };
4965
4970
  }
4966
- /**
4967
- * Prepares a borrow action that can be signed and submitted later.
4968
- *
4969
- * Use this when you need explicit control over signing and submission.
4970
- *
4971
- * @param request - Profile, pool, amount (borrow asset base units), outflow address, and signer wallet.
4972
- * @returns A signable {@link BorrowAction} with `submit` wired to the canister.
4973
- */
4974
- async prepareBorrow(request) {
4975
- const destinationAccount = request.receiverAddress.trim();
4976
- const signerAccount = normalizeEvmAddress(
4977
- request.signerWalletAddress.trim()
4978
- );
4979
- if (!destinationAccount) {
4971
+ async sendAndSubmitNativeSupplyInflow(params) {
4972
+ const { request, instruction, defaultSubmitInflowRequest } = params;
4973
+ if (instruction.target.type !== "nativeAddress") {
4980
4974
  throw new LiquidiumError(
4981
4975
  LiquidiumErrorCode.VALIDATION_ERROR,
4982
- "Borrow requires a custom outflow account"
4976
+ "Wallet-executed supply requires a native-address target"
4983
4977
  );
4984
4978
  }
4985
- if (!signerAccount) {
4979
+ if (!request.walletAdapter) {
4986
4980
  throw new LiquidiumError(
4987
4981
  LiquidiumErrorCode.VALIDATION_ERROR,
4988
- "Borrow requires a signer account"
4982
+ "Wallet-executed supply requires a wallet adapter"
4989
4983
  );
4990
4984
  }
4991
- if (request.amount <= 0n) {
4985
+ const account = request.account?.trim();
4986
+ if (!account) {
4992
4987
  throw new LiquidiumError(
4993
4988
  LiquidiumErrorCode.VALIDATION_ERROR,
4994
- "Borrow amount must be greater than 0"
4989
+ "Wallet-executed transfer supply requires an account"
4995
4990
  );
4996
4991
  }
4997
- const selectedPool = await this.getPoolById(request.poolId);
4998
- const selectedAsset = getVariantKey(selectedPool.asset);
4999
- const minimumBorrowAmountError = getBorrowAmountMinimumValidationError({
5000
- amount: request.amount,
5001
- asset: selectedAsset
5002
- });
5003
- if (minimumBorrowAmountError) {
4992
+ if (!request.amount || request.amount <= 0n) {
5004
4993
  throw new LiquidiumError(
5005
4994
  LiquidiumErrorCode.VALIDATION_ERROR,
5006
- minimumBorrowAmountError.message
4995
+ "Wallet-executed supply requires a positive amount"
5007
4996
  );
5008
4997
  }
5009
- const receiverAddress = normalizeExternalAddress({
5010
- address: destinationAccount,
5011
- asset: selectedAsset,
5012
- chain: getVariantKey(selectedPool.chain)
4998
+ const txid = await this.sendNativeSupplyTransaction({
4999
+ walletAdapter: request.walletAdapter,
5000
+ chain: instruction.chain,
5001
+ toAddress: instruction.target.address,
5002
+ amount: request.amount,
5003
+ senderAccount: account,
5004
+ asset: instruction.asset,
5005
+ action: request.action
5013
5006
  });
5014
- const lendingActor = createLendingActor(this.canisterContext);
5015
- try {
5016
- const expiryTimestamp = computeExpiryTimestampFromNow();
5017
- const nonce = await lendingActor.get_nonce(signerAccount);
5018
- const borrowRequestData = {
5019
- profileId: request.profileId,
5020
- poolId: request.poolId,
5021
- amount: request.amount,
5022
- receiverAddress,
5023
- signerWalletAddress: signerAccount,
5024
- expiryTimestamp
5025
- };
5007
+ if (shouldSubmitInflow({ instruction, mechanism: SupplyPlanType.transfer })) {
5008
+ try {
5009
+ await this.submitInflowWithRetry(txid, defaultSubmitInflowRequest);
5010
+ } catch {
5011
+ }
5012
+ }
5013
+ return txid;
5014
+ }
5015
+ async submitSupplyFlowInflow(params) {
5016
+ if (!shouldSubmitInflow(params)) {
5026
5017
  return {
5027
- kind: WalletActionKind.createBorrow,
5028
- executionKind: WalletExecutionKind.signMessage,
5029
- actionType: WalletActionKind.createBorrow,
5030
- transferMode: TransferMode.native,
5031
- account: signerAccount,
5032
- message: createBorrowAssetMessage(
5033
- {
5034
- pool_id: request.poolId,
5035
- amount: request.amount.toString(),
5036
- account: { type: "External", data: receiverAddress },
5037
- expiry_timestamp: expiryTimestamp
5038
- },
5039
- nonce
5040
- ),
5041
- data: borrowRequestData,
5042
- submit: async (signatureInfo) => {
5043
- return await this.submitBorrow(borrowRequestData, signatureInfo);
5044
- }
5018
+ txid: params.submitRequest.txid
5045
5019
  };
5046
- } catch (error) {
5047
- if (error instanceof LiquidiumError) {
5048
- throw error;
5049
- }
5050
- throw mapCanisterCallErrorToLiquidiumError("get_nonce", error);
5051
5020
  }
5021
+ const defaultSubmitInflowRequest = params.defaultSubmitInflowRequest;
5022
+ if (!defaultSubmitInflowRequest) {
5023
+ throw new LiquidiumError(
5024
+ LiquidiumErrorCode.VALIDATION_ERROR,
5025
+ "Supply flow submit requires an inflow operation"
5026
+ );
5027
+ }
5028
+ return await this.params.submitInflow({
5029
+ ...defaultSubmitInflowRequest,
5030
+ ...params.submitRequest
5031
+ });
5052
5032
  }
5053
- async submitBorrow(request, signatureInfo) {
5033
+ async executeContractSupply(params) {
5034
+ const { request, instruction, defaultSubmitInflowRequest } = params;
5035
+ if (instruction.target.type !== "icrcAccount" || instruction.chain !== Chain.ETH) {
5036
+ throw new LiquidiumError(
5037
+ LiquidiumErrorCode.VALIDATION_ERROR,
5038
+ "Contract-interaction supply is only supported for ETH ICRC-account targets"
5039
+ );
5040
+ }
5041
+ const walletAddressInput = request.account?.trim();
5042
+ if (!walletAddressInput) {
5043
+ throw new LiquidiumError(
5044
+ LiquidiumErrorCode.VALIDATION_ERROR,
5045
+ "Contract-interaction supply requires an account"
5046
+ );
5047
+ }
5048
+ const walletAddress = normalizeAndValidateEvmAddress(
5049
+ walletAddressInput,
5050
+ "Invalid EVM wallet address"
5051
+ );
5052
+ if (!request.amount || request.amount <= 0n) {
5053
+ throw new LiquidiumError(
5054
+ LiquidiumErrorCode.VALIDATION_ERROR,
5055
+ "Contract-interaction supply requires a positive amount"
5056
+ );
5057
+ }
5058
+ const walletAdapter = request.walletAdapter;
5059
+ if (!walletAdapter?.sendEthTransaction) {
5060
+ throw new LiquidiumError(
5061
+ LiquidiumErrorCode.VALIDATION_ERROR,
5062
+ "Contract-interaction supply requires an ETH wallet adapter"
5063
+ );
5064
+ }
5065
+ this.params.requireApi();
5066
+ this.requireEvmReadClient(
5067
+ "Contract-interaction supply requires an EVM RPC URL or public client"
5068
+ );
5069
+ const evmSupplyContext = await this.getEvmSupplyContextForPool({
5070
+ request: {
5071
+ profileId: request.profileId,
5072
+ poolId: request.poolId,
5073
+ walletAddress,
5074
+ amount: request.amount,
5075
+ action: request.action
5076
+ },
5077
+ asset: instruction.asset,
5078
+ chain: instruction.chain
5079
+ });
5080
+ const supplyAmount = BigInt(evmSupplyContext.amount);
5081
+ if (BigInt(evmSupplyContext.balance) < supplyAmount) {
5082
+ throw new LiquidiumError(
5083
+ LiquidiumErrorCode.INSUFFICIENT_FUNDS,
5084
+ `Insufficient ${evmSupplyContext.asset} balance for ${request.action}`
5085
+ );
5086
+ }
5087
+ if (evmSupplyContext.approvalStrategy === EvmSupplyApprovalStrategy.resetThenApproveMax) {
5088
+ await this.sendEthContractTransaction(
5089
+ walletAdapter,
5090
+ walletAddress,
5091
+ createApproveTransaction({
5092
+ tokenAddress: evmSupplyContext.tokenAddress,
5093
+ spenderAddress: evmSupplyContext.spenderAddress,
5094
+ amount: 0n
5095
+ }),
5096
+ `supply-${request.action}-approve-reset`
5097
+ );
5098
+ await this.waitForExpectedAllowance({
5099
+ walletAddress,
5100
+ tokenAddress: evmSupplyContext.tokenAddress,
5101
+ spenderAddress: evmSupplyContext.spenderAddress,
5102
+ amount: supplyAmount,
5103
+ expectation: "zero"
5104
+ });
5105
+ }
5106
+ if (evmSupplyContext.approvalStrategy !== EvmSupplyApprovalStrategy.none) {
5107
+ await this.sendEthContractTransaction(
5108
+ walletAdapter,
5109
+ walletAddress,
5110
+ createApproveTransaction({
5111
+ tokenAddress: evmSupplyContext.tokenAddress,
5112
+ spenderAddress: evmSupplyContext.spenderAddress,
5113
+ amount: MAX_UINT256
5114
+ }),
5115
+ `supply-${request.action}-approve-max`
5116
+ );
5117
+ await this.waitForExpectedAllowance({
5118
+ walletAddress,
5119
+ tokenAddress: evmSupplyContext.tokenAddress,
5120
+ spenderAddress: evmSupplyContext.spenderAddress,
5121
+ amount: supplyAmount,
5122
+ expectation: "sufficient"
5123
+ });
5124
+ }
5125
+ const depositTxid = await this.sendEthContractTransaction(
5126
+ walletAdapter,
5127
+ walletAddress,
5128
+ createDepositErc20Transaction({
5129
+ depositContractAddress: evmSupplyContext.depositContractAddress,
5130
+ tokenAddress: evmSupplyContext.tokenAddress,
5131
+ amount: supplyAmount,
5132
+ poolId: request.poolId,
5133
+ profileId: request.profileId,
5134
+ destinationAccount: instruction.target.account,
5135
+ action: request.action
5136
+ }),
5137
+ `supply-${request.action}-deposit-erc20`
5138
+ );
5054
5139
  try {
5055
- const result = await createLendingActor(
5056
- this.canisterContext
5057
- ).borrow_assets(Principal.fromText(request.profileId), {
5058
- data: {
5059
- expiry_timestamp: request.expiryTimestamp,
5060
- account: { External: request.receiverAddress },
5061
- pool_id: Principal.fromText(request.poolId),
5062
- amount: request.amount
5063
- },
5064
- signature_info: {
5065
- Wallet: {
5066
- signature: normalizeWalletSignature(
5067
- signatureInfo.signature,
5068
- signatureInfo.chain
5069
- ),
5070
- chain: mapWalletChainToLendingChain(signatureInfo.chain),
5071
- account: request.signerWalletAddress
5072
- }
5140
+ await this.submitInflowWithRetry(depositTxid, defaultSubmitInflowRequest);
5141
+ } catch {
5142
+ }
5143
+ return depositTxid;
5144
+ }
5145
+ async sendNativeSupplyTransaction(params) {
5146
+ switch (params.chain) {
5147
+ case Chain.BTC: {
5148
+ if (!params.walletAdapter.sendBtcTransaction) {
5149
+ throw new LiquidiumError(
5150
+ LiquidiumErrorCode.VALIDATION_ERROR,
5151
+ "BTC wallet adapter does not support transaction sending"
5152
+ );
5073
5153
  }
5074
- });
5075
- if ("Err" in result) {
5076
- throw mapLendingProtocolErrorToLiquidiumError(result.Err);
5154
+ return await params.walletAdapter.sendBtcTransaction({
5155
+ chain: Chain.BTC,
5156
+ toAddress: params.toAddress,
5157
+ amountSats: params.amount,
5158
+ account: params.senderAccount,
5159
+ actionType: `supply-${params.action}`,
5160
+ transferMode: TransferMode.native
5161
+ });
5077
5162
  }
5078
- return mapExpectedOutflowDetails(
5079
- mapCanisterOutflowDetails(result.Ok),
5080
- OutflowType.borrow,
5081
- "borrow_assets"
5082
- );
5083
- } catch (error) {
5084
- if (error instanceof LiquidiumError) {
5085
- throw error;
5163
+ case Chain.ETH: {
5164
+ if (!params.walletAdapter.sendEthTransaction) {
5165
+ throw new LiquidiumError(
5166
+ LiquidiumErrorCode.VALIDATION_ERROR,
5167
+ "ETH wallet adapter does not support transaction sending"
5168
+ );
5169
+ }
5170
+ if (isEthStablecoin(params.asset, params.chain)) {
5171
+ return await params.walletAdapter.sendEthTransaction({
5172
+ chain: Chain.ETH,
5173
+ account: params.senderAccount,
5174
+ actionType: `supply-${params.action}`,
5175
+ transferMode: TransferMode.native,
5176
+ transaction: createTransferErc20Transaction({
5177
+ tokenAddress: getEthStablecoinContractAddress(params.asset),
5178
+ recipientAddress: params.toAddress,
5179
+ amount: params.amount
5180
+ })
5181
+ });
5182
+ }
5183
+ return await params.walletAdapter.sendEthTransaction({
5184
+ chain: Chain.ETH,
5185
+ account: params.senderAccount,
5186
+ actionType: `supply-${params.action}`,
5187
+ transferMode: TransferMode.native,
5188
+ transaction: {
5189
+ to: params.toAddress,
5190
+ value: params.amount.toString()
5191
+ }
5192
+ });
5086
5193
  }
5087
- throw mapCanisterCallErrorToLiquidiumError("borrow_assets", error);
5194
+ default:
5195
+ throw new LiquidiumError(
5196
+ LiquidiumErrorCode.VALIDATION_ERROR,
5197
+ `Native-address wallet execution is not supported for ${params.chain}`
5198
+ );
5088
5199
  }
5089
5200
  }
5090
- /**
5091
- * Creates a borrow outflow using the provided wallet adapter.
5092
- *
5093
- * This is the convenience form of `prepareBorrow(...)` plus execution.
5094
- *
5095
- * @param params - Borrow request fields plus `signerChain` and `signerWalletAdapter`.
5096
- * @returns The lending canister receipt as {@link OutflowDetails}.
5097
- *
5098
- * @remarks
5099
- * `id` is always present. `txid` may be missing on the first response; the SDK does not
5100
- * poll for it. Use history or app-level polling if you need the chain transaction id.
5101
- */
5102
- async borrow(params) {
5103
- const action = await this.prepareBorrow(params);
5104
- return await executeWith({
5105
- walletAdapter: params.signerWalletAdapter,
5106
- chain: params.signerChain,
5107
- account: action.account
5108
- })(action);
5109
- }
5110
- /**
5111
- * Resolves a supply target for a deposit or repayment and optionally broadcasts it.
5112
- *
5113
- * Transfer mode can return manual broadcast instructions when wallet fields are
5114
- * omitted. Contract-interaction mode always requires `walletAdapter`, `account`,
5115
- * and `amount` because it must prepare and submit approval/deposit calls.
5116
- *
5117
- * The SDK does not poll for inflow status. When a `txid` is returned, it is the
5118
- * caller's responsibility to track confirmation state using their own polling.
5119
- *
5120
- * @returns A {@link SupplyFlow} receipt with `type`, `target`, `submit`, and
5121
- * an optional `txid` present when the SDK broadcast for you.
5122
- */
5123
- async supply(request) {
5124
- const target = await resolveSupplyTarget(this.canisterContext, request);
5125
- const instruction = {
5126
- poolId: request.poolId,
5127
- asset: target.asset,
5128
- chain: target.chain,
5129
- action: request.action,
5130
- target
5131
- };
5132
- const mechanism = resolveSupplyMechanism({
5133
- asset: instruction.asset,
5134
- chain: instruction.chain,
5135
- requestedMechanism: request.mechanism
5201
+ async submitInflowWithRetry(txid, extraRequest) {
5202
+ if (!extraRequest) {
5203
+ throw new LiquidiumError(
5204
+ LiquidiumErrorCode.VALIDATION_ERROR,
5205
+ "Inflow submission requires an operation"
5206
+ );
5207
+ }
5208
+ await retryWithBackoff({
5209
+ execute: async () => {
5210
+ await this.params.submitInflow({ txid, ...extraRequest });
5211
+ },
5212
+ maxAttempts: SUBMIT_INFLOW_MAX_ATTEMPTS,
5213
+ initialRetryDelayMs: SUBMIT_INFLOW_INITIAL_RETRY_DELAY_MS,
5214
+ backoffMultiplier: SUBMIT_INFLOW_RETRY_BACKOFF_MULTIPLIER,
5215
+ shouldRetryError: isRetriableInflowSubmitError
5136
5216
  });
5137
- const defaultSubmitInflowRequest = getDefaultSubmitInflowRequest({
5138
- action: request.action,
5139
- chain: mechanism === SupplyPlanType.contractInteraction ? Chain.ETH : instruction.chain
5217
+ }
5218
+ async sendEthContractTransaction(walletAdapter, walletAddress, request, actionType) {
5219
+ if (!walletAdapter.sendEthTransaction) {
5220
+ throw new LiquidiumError(
5221
+ LiquidiumErrorCode.VALIDATION_ERROR,
5222
+ "ETH wallet adapter does not support transaction sending"
5223
+ );
5224
+ }
5225
+ return await walletAdapter.sendEthTransaction({
5226
+ chain: Chain.ETH,
5227
+ account: walletAddress,
5228
+ actionType,
5229
+ transferMode: TransferMode.native,
5230
+ transaction: request
5140
5231
  });
5141
- let txid;
5142
- switch (mechanism) {
5143
- case SupplyPlanType.transfer:
5144
- if (request.walletAdapter) {
5145
- txid = await this.sendAndSubmitNativeSupplyInflow({
5146
- request,
5147
- instruction,
5148
- defaultSubmitInflowRequest
5149
- });
5150
- }
5151
- break;
5152
- case SupplyPlanType.contractInteraction:
5153
- txid = await this.executeContractSupply({
5154
- request,
5155
- instruction,
5156
- defaultSubmitInflowRequest
5232
+ }
5233
+ async waitForExpectedAllowance(params) {
5234
+ const evmReadClient = this.requireEvmReadClient(
5235
+ "Contract-interaction supply requires an EVM RPC URL or public client"
5236
+ );
5237
+ let lastPollingError;
5238
+ for (let pollIndex = 0; pollIndex < ETH_APPROVAL_MAX_POLLS; pollIndex += 1) {
5239
+ try {
5240
+ const allowance = await readErc20Allowance({
5241
+ evmReadClient,
5242
+ tokenAddress: params.tokenAddress,
5243
+ ownerAddress: params.walletAddress,
5244
+ spenderAddress: params.spenderAddress
5157
5245
  });
5158
- break;
5246
+ const matchesExpectation = params.expectation === "zero" ? allowance === 0n : allowance >= params.amount;
5247
+ if (matchesExpectation) {
5248
+ return;
5249
+ }
5250
+ } catch (error) {
5251
+ lastPollingError = error;
5252
+ }
5253
+ await delay(ETH_APPROVAL_POLL_INTERVAL_MS);
5159
5254
  }
5255
+ const lastPollingErrorMessage = getUnknownErrorMessage(lastPollingError);
5256
+ throw new LiquidiumError(
5257
+ LiquidiumErrorCode.SERVICE_UNAVAILABLE,
5258
+ lastPollingErrorMessage ? `Timed out waiting for ${params.expectation} ETH allowance update. Last polling error: ${lastPollingErrorMessage}` : `Timed out waiting for ${params.expectation} ETH allowance update`
5259
+ );
5260
+ }
5261
+ requireEvmReadClient(message) {
5262
+ if (!this.params.evmReadClient) {
5263
+ throw new LiquidiumError(LiquidiumErrorCode.VALIDATION_ERROR, message);
5264
+ }
5265
+ return this.params.evmReadClient;
5266
+ }
5267
+ };
5268
+ async function readErc20Allowance(params) {
5269
+ const allowance = await params.evmReadClient.readContract({
5270
+ address: params.tokenAddress,
5271
+ abi: ERC20_ABI,
5272
+ functionName: "allowance",
5273
+ args: [
5274
+ params.ownerAddress,
5275
+ params.spenderAddress
5276
+ ]
5277
+ });
5278
+ return BigInt(allowance);
5279
+ }
5280
+ async function readErc20Balance(params) {
5281
+ const balance = await params.evmReadClient.readContract({
5282
+ address: params.tokenAddress,
5283
+ abi: ERC20_ABI,
5284
+ functionName: "balanceOf",
5285
+ args: [params.ownerAddress]
5286
+ });
5287
+ return BigInt(balance);
5288
+ }
5289
+ function getApprovalStrategy(params) {
5290
+ if (params.allowance >= params.amount) {
5291
+ return EvmSupplyApprovalStrategy.none;
5292
+ }
5293
+ if (params.allowance === 0n) {
5294
+ return EvmSupplyApprovalStrategy.approveMax;
5295
+ }
5296
+ return EvmSupplyApprovalStrategy.resetThenApproveMax;
5297
+ }
5298
+ async function delay(timeoutMs) {
5299
+ await new Promise((resolve) => setTimeout(resolve, timeoutMs));
5300
+ }
5301
+ function isRetriableInflowSubmitError(error) {
5302
+ if (!(error instanceof LiquidiumError)) {
5303
+ return false;
5304
+ }
5305
+ return error.code === LiquidiumErrorCode.SERVICE_UNAVAILABLE;
5306
+ }
5307
+ function getUnknownErrorMessage(error) {
5308
+ if (error instanceof Error) {
5309
+ return error.message;
5310
+ }
5311
+ if (typeof error === "string") {
5312
+ return error;
5313
+ }
5314
+ return null;
5315
+ }
5316
+ function getDefaultSubmitInflowRequest(params) {
5317
+ if (params.chain !== Chain.BTC && params.chain !== Chain.ETH) {
5318
+ return void 0;
5319
+ }
5320
+ return {
5321
+ chain: params.chain,
5322
+ operation: mapSupplyActionToStatusOperation(params.action)
5323
+ };
5324
+ }
5325
+ function mapSupplyActionToStatusOperation(action) {
5326
+ return action === SupplyAction.repayment ? "repayment" : "deposit";
5327
+ }
5328
+ function shouldSubmitInflow(params) {
5329
+ if (params.mechanism !== SupplyPlanType.transfer) {
5330
+ return true;
5331
+ }
5332
+ return !isEthStablecoin(params.instruction.asset, params.instruction.chain);
5333
+ }
5334
+ function mapCanisterOutflowDetails(outflow) {
5335
+ const rawOutflowType = getVariantKey(outflow.outflow_type);
5336
+ return {
5337
+ id: outflow.id,
5338
+ outflowType: normalizeOutflowType(rawOutflowType),
5339
+ outflowRef: outflow.outflow_ref[0],
5340
+ txid: outflow.txid[0],
5341
+ amount: outflow.amount,
5342
+ receiver: mapCanisterAccountType(outflow.receiver)
5343
+ };
5344
+ }
5345
+ function mapCanisterAccountType(receiver) {
5346
+ if ("Native" in receiver) {
5160
5347
  return {
5161
- type: mechanism,
5162
- target: instruction.target,
5163
- txid,
5164
- submit: async (submitRequest) => {
5165
- return await this.submitSupplyFlowInflow({
5166
- instruction,
5167
- mechanism,
5168
- defaultSubmitInflowRequest,
5169
- submitRequest
5170
- });
5171
- }
5348
+ type: "Native",
5349
+ account: receiver.Native.toText()
5350
+ };
5351
+ }
5352
+ if ("AccountIdentifier" in receiver) {
5353
+ return {
5354
+ type: "AccountIdentifier",
5355
+ account: receiver.AccountIdentifier
5172
5356
  };
5173
5357
  }
5358
+ if ("Icrc" in receiver) {
5359
+ const subaccount = normalizeOptionalSubaccount2(receiver.Icrc.subaccount[0]);
5360
+ return {
5361
+ type: "Icrc",
5362
+ owner: receiver.Icrc.owner.toText(),
5363
+ subaccount,
5364
+ account: encodeIcrcAccount({
5365
+ owner: receiver.Icrc.owner,
5366
+ subaccount
5367
+ })
5368
+ };
5369
+ }
5370
+ return {
5371
+ type: "External",
5372
+ account: receiver.External
5373
+ };
5374
+ }
5375
+ function normalizeOptionalSubaccount2(subaccount) {
5376
+ if (!subaccount) {
5377
+ return void 0;
5378
+ }
5379
+ return subaccount instanceof Uint8Array ? subaccount : Uint8Array.from(subaccount);
5380
+ }
5381
+ function normalizeOutflowType(rawOutflowType) {
5382
+ switch (rawOutflowType) {
5383
+ case "Withdraw":
5384
+ return OutflowType.withdrawal;
5385
+ case "Borrow":
5386
+ return OutflowType.borrow;
5387
+ case "FeeClaim":
5388
+ return OutflowType.feeClaim;
5389
+ default:
5390
+ throw new LiquidiumError(
5391
+ LiquidiumErrorCode.INTERNAL,
5392
+ `Unsupported outflow type: ${rawOutflowType}`
5393
+ );
5394
+ }
5395
+ }
5396
+ function mapWalletChainToLendingChain(chain) {
5397
+ switch (chain) {
5398
+ case Chain.BTC:
5399
+ return { BTC: null };
5400
+ case Chain.ETH:
5401
+ return { ETH: null };
5402
+ }
5403
+ }
5404
+
5405
+ // src/modules/lending/lending.ts
5406
+ var LendingModule = class {
5407
+ constructor(canisterContext, apiClient, evmReadClient) {
5408
+ this.canisterContext = canisterContext;
5409
+ this.apiClient = apiClient;
5410
+ this.evmReadClient = evmReadClient;
5411
+ }
5412
+ canisterContext;
5413
+ apiClient;
5414
+ evmReadClient;
5174
5415
  /**
5175
- * Fetches ERC-20 supply planning data with the configured EVM read client.
5416
+ * Prepares a withdraw action that can be signed and submitted later.
5176
5417
  *
5177
- * Requires `evmRpcUrl` or `evmPublicClient` on the client. Used internally by
5178
- * contract-interaction `supply`.
5418
+ * Use this when you need explicit control over signing and submission.
5179
5419
  *
5180
- * @param request - Profile, pool, wallet, amount (token base units), and action.
5181
- * @returns Locally computed {@link EvmSupplyContext} for approvals and deposit.
5420
+ * @param request - Profile, pool, amount (pool asset base units), outflow address, and signer wallet.
5421
+ * @returns A signable {@link WithdrawAction} with `submit` wired to the canister.
5182
5422
  */
5183
- async getEvmSupplyContext(request) {
5184
- const selectedPool = await this.getPoolById(request.poolId);
5185
- return await this.getEvmSupplyContextForPool({
5186
- request,
5187
- asset: getVariantKey(selectedPool.asset),
5188
- chain: getVariantKey(selectedPool.chain)
5189
- });
5190
- }
5191
- async getEvmSupplyContextForPool(params) {
5192
- const { request, asset, chain } = params;
5193
- const evmReadClient = this.requireEvmReadClient(
5194
- "EVM supply context requires an EVM RPC URL or public client"
5195
- );
5196
- const walletAddress = normalizeAndValidateEvmAddress(
5197
- request.walletAddress,
5198
- "Invalid EVM wallet address"
5423
+ async prepareWithdraw(request) {
5424
+ const destinationAccount = request.receiverAddress.trim();
5425
+ const signerAccount = normalizeEvmAddress(
5426
+ request.signerWalletAddress.trim()
5199
5427
  );
5200
- if (request.amount <= 0n) {
5428
+ if (!destinationAccount) {
5201
5429
  throw new LiquidiumError(
5202
5430
  LiquidiumErrorCode.VALIDATION_ERROR,
5203
- "EVM supply context requires a positive amount"
5431
+ "Withdraw requires a custom outflow account"
5204
5432
  );
5205
5433
  }
5206
- if (!isEthStablecoin(asset, chain)) {
5434
+ if (!signerAccount) {
5207
5435
  throw new LiquidiumError(
5208
5436
  LiquidiumErrorCode.VALIDATION_ERROR,
5209
- `EVM supply context only supports ETH stablecoin pools, received ${asset} on ${chain}`
5437
+ "Withdraw requires a signer account"
5210
5438
  );
5211
5439
  }
5212
- const tokenAddress = getEthStablecoinContractAddress(asset);
5213
- const spenderAddress = CK_ETH_DEPOSIT_CONTRACT_ADDRESS;
5214
- const [allowance, balance] = await Promise.all([
5215
- readErc20Allowance({
5216
- evmReadClient,
5217
- tokenAddress,
5218
- ownerAddress: walletAddress,
5219
- spenderAddress
5220
- }),
5221
- readErc20Balance({
5222
- evmReadClient,
5223
- tokenAddress,
5224
- ownerAddress: walletAddress
5225
- })
5226
- ]);
5227
- const approvalStrategy = getApprovalStrategy({
5228
- allowance,
5229
- amount: request.amount
5230
- });
5231
- return {
5232
- success: true,
5233
- profileId: request.profileId,
5440
+ const receiverAddress = await this.normalizeOutflowReceiverAddress({
5234
5441
  poolId: request.poolId,
5235
- walletAddress,
5236
- action: request.action,
5237
- asset,
5238
- chain: Chain.ETH,
5239
- amount: request.amount.toString(),
5240
- tokenAddress,
5241
- spenderAddress,
5242
- depositContractAddress: CK_ETH_DEPOSIT_CONTRACT_ADDRESS,
5243
- balance: balance.toString(),
5244
- allowance: allowance.toString(),
5245
- requiresApproval: approvalStrategy !== EvmSupplyApprovalStrategy.none,
5246
- approvalStrategy
5247
- };
5248
- }
5249
- /**
5250
- * Returns the read-only deposit address for an ETH stablecoin inflow target.
5251
- *
5252
- * This is a query call that does not create or mutate state. Use it when you
5253
- * need the deposit address without hitting the authorization-gated update path.
5254
- *
5255
- * @param request - Profile, pool, asset, and supply action.
5256
- * @returns The EVM deposit address for the derived account.
5257
- */
5258
- async getDepositAddress(request) {
5259
- if (!isEthStablecoin(request.asset, Chain.ETH)) {
5260
- throw new LiquidiumError(
5261
- LiquidiumErrorCode.VALIDATION_ERROR,
5262
- "getDepositAddress is only supported for ETH stablecoins"
5263
- );
5264
- }
5265
- const tokenAddress = getEthStablecoinContractAddress(request.asset);
5266
- const subaccount = encodeInflowSubaccount({
5267
- action: request.action,
5268
- principal: Principal.fromText(request.profileId)
5442
+ receiverAddress: destinationAccount
5269
5443
  });
5270
- const result = await createDepositAccountsActor(
5271
- this.canisterContext
5272
- ).get_deposit_address(
5273
- {
5274
- owner: Principal.fromText(request.poolId),
5275
- subaccount: [subaccount]
5276
- },
5277
- [tokenAddress]
5278
- );
5279
- if ("Err" in result) {
5280
- throw mapDepositAccountErrorToLiquidiumError(result.Err);
5281
- }
5282
- return result.Ok;
5283
- }
5284
- /**
5285
- * Estimates the network/deposit fee for an inflow target.
5286
- *
5287
- * ETH stablecoin deposit-address estimates are served by the deposit-address
5288
- * canister. BTC estimates include the ckBTC minter deposit fee and ledger fee.
5289
- *
5290
- * @param request - Asset and chain pair to estimate for.
5291
- * @returns Total fee estimate rounded up in the asset's base units.
5292
- */
5293
- async estimateInflowFee(request) {
5294
- if (isEthStablecoin(request.asset, request.chain)) {
5295
- const result = await createDepositAccountsActor(
5296
- this.canisterContext
5297
- ).estimate_deposit_fee([getEthStablecoinContractAddress(request.asset)]);
5298
- if ("Err" in result) {
5299
- throw mapDepositAccountErrorToLiquidiumError(result.Err);
5300
- }
5301
- return {
5302
- totalFee: roundInflowFeeEstimate(request, result.Ok)
5444
+ const lendingActor = createLendingActor(this.canisterContext);
5445
+ try {
5446
+ const expiryTimestamp = computeExpiryTimestampFromNow();
5447
+ const nonce = await lendingActor.get_nonce(signerAccount);
5448
+ const withdrawRequestData = {
5449
+ profileId: request.profileId,
5450
+ poolId: request.poolId,
5451
+ amount: request.amount,
5452
+ receiverAddress,
5453
+ signerWalletAddress: signerAccount,
5454
+ expiryTimestamp
5303
5455
  };
5304
- }
5305
- if (request.asset === Asset.BTC && request.chain === Chain.BTC) {
5306
- const estimate = await this.estimateBtcInflowFee();
5307
5456
  return {
5308
- totalFee: roundInflowFeeEstimate(request, estimate.totalFee)
5457
+ kind: WalletActionKind.createWithdraw,
5458
+ executionKind: WalletExecutionKind.signMessage,
5459
+ actionType: WalletActionKind.createWithdraw,
5460
+ transferMode: TransferMode.native,
5461
+ account: signerAccount,
5462
+ message: createWithdrawAssetMessage(
5463
+ {
5464
+ pool_id: request.poolId,
5465
+ amount: request.amount.toString(),
5466
+ account: { type: "External", data: receiverAddress },
5467
+ expiry_timestamp: expiryTimestamp
5468
+ },
5469
+ nonce
5470
+ ),
5471
+ data: withdrawRequestData,
5472
+ submit: async (signatureInfo) => {
5473
+ return await this.submitWithdraw(withdrawRequestData, signatureInfo);
5474
+ }
5309
5475
  };
5476
+ } catch (error) {
5477
+ if (error instanceof LiquidiumError) {
5478
+ throw error;
5479
+ }
5480
+ throw mapCanisterCallErrorToLiquidiumError("get_nonce", error);
5310
5481
  }
5311
- throw new LiquidiumError(
5312
- LiquidiumErrorCode.VALIDATION_ERROR,
5313
- `Inflow fee estimates are not supported for ${request.asset} on ${request.chain}`
5314
- );
5315
- }
5316
- async estimateBtcInflowFee() {
5317
- const [minterFee, ledgerFee] = await Promise.all([
5318
- createCkBtcMinterActor(this.canisterContext).get_deposit_fee(),
5319
- createCkBtcLedgerActor(this.canisterContext).icrc1_fee()
5320
- ]);
5321
- return { totalFee: minterFee + ledgerFee };
5322
5482
  }
5323
- async sendAndSubmitNativeSupplyInflow(params) {
5324
- const { request, instruction, defaultSubmitInflowRequest } = params;
5325
- if (instruction.target.type !== "nativeAddress") {
5326
- throw new LiquidiumError(
5327
- LiquidiumErrorCode.VALIDATION_ERROR,
5328
- "Wallet-executed supply requires a native-address target"
5329
- );
5330
- }
5331
- if (!request.walletAdapter) {
5332
- throw new LiquidiumError(
5333
- LiquidiumErrorCode.VALIDATION_ERROR,
5334
- "Wallet-executed supply requires a wallet adapter"
5335
- );
5336
- }
5337
- const account = request.account?.trim();
5338
- if (!account) {
5339
- throw new LiquidiumError(
5340
- LiquidiumErrorCode.VALIDATION_ERROR,
5341
- "Wallet-executed transfer supply requires an account"
5483
+ async submitWithdraw(request, signatureInfo) {
5484
+ try {
5485
+ const result = await createLendingActor(this.canisterContext).withdraw(
5486
+ Principal.fromText(request.profileId),
5487
+ {
5488
+ data: {
5489
+ expiry_timestamp: request.expiryTimestamp,
5490
+ account: { External: request.receiverAddress },
5491
+ pool_id: Principal.fromText(request.poolId),
5492
+ amount: request.amount
5493
+ },
5494
+ signature_info: {
5495
+ Wallet: {
5496
+ signature: normalizeWalletSignature(
5497
+ signatureInfo.signature,
5498
+ signatureInfo.chain
5499
+ ),
5500
+ chain: mapWalletChainToLendingChain(signatureInfo.chain),
5501
+ account: request.signerWalletAddress
5502
+ }
5503
+ }
5504
+ }
5342
5505
  );
5343
- }
5344
- if (!request.amount || request.amount <= 0n) {
5345
- throw new LiquidiumError(
5346
- LiquidiumErrorCode.VALIDATION_ERROR,
5347
- "Wallet-executed supply requires a positive amount"
5506
+ if ("Err" in result) {
5507
+ throw mapLendingProtocolErrorToLiquidiumError(result.Err);
5508
+ }
5509
+ return mapExpectedOutflowDetails(
5510
+ mapCanisterOutflowDetails(result.Ok),
5511
+ OutflowType.withdrawal,
5512
+ "withdraw"
5348
5513
  );
5514
+ } catch (error) {
5515
+ if (error instanceof LiquidiumError) {
5516
+ throw error;
5517
+ }
5518
+ throw mapCanisterCallErrorToLiquidiumError("withdraw", error);
5349
5519
  }
5350
- const txid = await this.sendNativeSupplyTransaction({
5351
- walletAdapter: request.walletAdapter,
5352
- chain: instruction.chain,
5353
- toAddress: instruction.target.address,
5354
- amount: request.amount,
5355
- senderAccount: account,
5356
- asset: instruction.asset,
5357
- action: request.action
5358
- });
5359
- if (shouldSubmitInflow({ instruction, mechanism: SupplyPlanType.transfer })) {
5360
- await this.submitInflowWithRetry(txid, defaultSubmitInflowRequest);
5361
- }
5362
- return txid;
5363
5520
  }
5364
- async submitSupplyFlowInflow(params) {
5365
- if (!shouldSubmitInflow(params)) {
5366
- return { success: true, txid: params.submitRequest.txid };
5367
- }
5368
- return await this.submitInflow({
5369
- ...params.defaultSubmitInflowRequest,
5370
- ...params.submitRequest
5371
- });
5521
+ /**
5522
+ * Creates a withdraw outflow using the provided wallet adapter.
5523
+ *
5524
+ * This is the convenience form of `prepareWithdraw(...)` plus execution.
5525
+ *
5526
+ * @param params - Withdraw request fields plus `signerChain` and `signerWalletAdapter`.
5527
+ * @returns The canister {@link OutflowDetails} for the completed withdraw.
5528
+ */
5529
+ async withdraw(params) {
5530
+ const action = await this.prepareWithdraw(params);
5531
+ return await executeWith({
5532
+ walletAdapter: params.signerWalletAdapter,
5533
+ chain: params.signerChain,
5534
+ account: action.account
5535
+ })(action);
5372
5536
  }
5373
- async executeContractSupply(params) {
5374
- const { request, instruction, defaultSubmitInflowRequest } = params;
5375
- if (instruction.target.type !== "icrcAccount" || instruction.chain !== Chain.ETH) {
5537
+ /**
5538
+ * Prepares a borrow action that can be signed and submitted later.
5539
+ *
5540
+ * Use this when you need explicit control over signing and submission.
5541
+ *
5542
+ * @param request - Profile, pool, amount (borrow asset base units), outflow address, and signer wallet.
5543
+ * @returns A signable {@link BorrowAction} with `submit` wired to the canister.
5544
+ */
5545
+ async prepareBorrow(request) {
5546
+ const destinationAccount = request.receiverAddress.trim();
5547
+ const signerAccount = normalizeEvmAddress(
5548
+ request.signerWalletAddress.trim()
5549
+ );
5550
+ if (!destinationAccount) {
5376
5551
  throw new LiquidiumError(
5377
5552
  LiquidiumErrorCode.VALIDATION_ERROR,
5378
- "Contract-interaction supply is only supported for ETH ICRC-account targets"
5553
+ "Borrow requires a custom outflow account"
5379
5554
  );
5380
5555
  }
5381
- const walletAddressInput = request.account?.trim();
5382
- if (!walletAddressInput) {
5556
+ if (!signerAccount) {
5383
5557
  throw new LiquidiumError(
5384
5558
  LiquidiumErrorCode.VALIDATION_ERROR,
5385
- "Contract-interaction supply requires an account"
5559
+ "Borrow requires a signer account"
5386
5560
  );
5387
5561
  }
5388
- const walletAddress = normalizeAndValidateEvmAddress(
5389
- walletAddressInput,
5390
- "Invalid EVM wallet address"
5391
- );
5392
- if (!request.amount || request.amount <= 0n) {
5562
+ if (request.amount <= 0n) {
5393
5563
  throw new LiquidiumError(
5394
5564
  LiquidiumErrorCode.VALIDATION_ERROR,
5395
- "Contract-interaction supply requires a positive amount"
5565
+ "Borrow amount must be greater than 0"
5396
5566
  );
5397
5567
  }
5398
- const walletAdapter = request.walletAdapter;
5399
- if (!walletAdapter?.sendEthTransaction) {
5568
+ const selectedPool = await this.getPoolById(request.poolId);
5569
+ const selectedAsset = selectedPool.asset;
5570
+ const minimumBorrowAmountError = getBorrowAmountMinimumValidationError({
5571
+ amount: request.amount,
5572
+ asset: selectedAsset
5573
+ });
5574
+ if (minimumBorrowAmountError) {
5400
5575
  throw new LiquidiumError(
5401
5576
  LiquidiumErrorCode.VALIDATION_ERROR,
5402
- "Contract-interaction supply requires an ETH wallet adapter"
5577
+ minimumBorrowAmountError.message
5403
5578
  );
5404
5579
  }
5405
- this.requireApi();
5406
- this.requireEvmReadClient(
5407
- "Contract-interaction supply requires an EVM RPC URL or public client"
5408
- );
5409
- const evmSupplyContext = await this.getEvmSupplyContextForPool({
5410
- request: {
5580
+ const receiverAddress = normalizeExternalAddress({
5581
+ address: destinationAccount,
5582
+ asset: selectedAsset,
5583
+ chain: selectedPool.chain
5584
+ });
5585
+ const lendingActor = createLendingActor(this.canisterContext);
5586
+ try {
5587
+ const expiryTimestamp = computeExpiryTimestampFromNow();
5588
+ const nonce = await lendingActor.get_nonce(signerAccount);
5589
+ const borrowRequestData = {
5411
5590
  profileId: request.profileId,
5412
5591
  poolId: request.poolId,
5413
- walletAddress,
5414
5592
  amount: request.amount,
5415
- action: request.action
5416
- },
5417
- asset: instruction.asset,
5418
- chain: instruction.chain
5419
- });
5420
- const supplyAmount = BigInt(evmSupplyContext.amount);
5421
- if (BigInt(evmSupplyContext.balance) < supplyAmount) {
5422
- throw new LiquidiumError(
5423
- LiquidiumErrorCode.INSUFFICIENT_FUNDS,
5424
- `Insufficient ${evmSupplyContext.asset} balance for ${request.action}`
5425
- );
5593
+ receiverAddress,
5594
+ signerWalletAddress: signerAccount,
5595
+ expiryTimestamp
5596
+ };
5597
+ return {
5598
+ kind: WalletActionKind.createBorrow,
5599
+ executionKind: WalletExecutionKind.signMessage,
5600
+ actionType: WalletActionKind.createBorrow,
5601
+ transferMode: TransferMode.native,
5602
+ account: signerAccount,
5603
+ message: createBorrowAssetMessage(
5604
+ {
5605
+ pool_id: request.poolId,
5606
+ amount: request.amount.toString(),
5607
+ account: { type: "External", data: receiverAddress },
5608
+ expiry_timestamp: expiryTimestamp
5609
+ },
5610
+ nonce
5611
+ ),
5612
+ data: borrowRequestData,
5613
+ submit: async (signatureInfo) => {
5614
+ return await this.submitBorrow(borrowRequestData, signatureInfo);
5615
+ }
5616
+ };
5617
+ } catch (error) {
5618
+ if (error instanceof LiquidiumError) {
5619
+ throw error;
5620
+ }
5621
+ throw mapCanisterCallErrorToLiquidiumError("get_nonce", error);
5426
5622
  }
5427
- if (evmSupplyContext.approvalStrategy === EvmSupplyApprovalStrategy.resetThenApproveMax) {
5428
- await this.sendEthContractTransaction(
5429
- walletAdapter,
5430
- walletAddress,
5431
- createApproveTransaction({
5432
- tokenAddress: evmSupplyContext.tokenAddress,
5433
- spenderAddress: evmSupplyContext.spenderAddress,
5434
- amount: 0n
5435
- }),
5436
- `supply-${request.action}-approve-reset`
5437
- );
5438
- await this.waitForExpectedAllowance({
5439
- walletAddress,
5440
- tokenAddress: evmSupplyContext.tokenAddress,
5441
- spenderAddress: evmSupplyContext.spenderAddress,
5442
- amount: supplyAmount,
5443
- expectation: "zero"
5623
+ }
5624
+ async submitBorrow(request, signatureInfo) {
5625
+ try {
5626
+ const result = await createLendingActor(
5627
+ this.canisterContext
5628
+ ).borrow_assets(Principal.fromText(request.profileId), {
5629
+ data: {
5630
+ expiry_timestamp: request.expiryTimestamp,
5631
+ account: { External: request.receiverAddress },
5632
+ pool_id: Principal.fromText(request.poolId),
5633
+ amount: request.amount
5634
+ },
5635
+ signature_info: {
5636
+ Wallet: {
5637
+ signature: normalizeWalletSignature(
5638
+ signatureInfo.signature,
5639
+ signatureInfo.chain
5640
+ ),
5641
+ chain: mapWalletChainToLendingChain(signatureInfo.chain),
5642
+ account: request.signerWalletAddress
5643
+ }
5644
+ }
5444
5645
  });
5646
+ if ("Err" in result) {
5647
+ throw mapLendingProtocolErrorToLiquidiumError(result.Err);
5648
+ }
5649
+ return mapExpectedOutflowDetails(
5650
+ mapCanisterOutflowDetails(result.Ok),
5651
+ OutflowType.borrow,
5652
+ "borrow_assets"
5653
+ );
5654
+ } catch (error) {
5655
+ if (error instanceof LiquidiumError) {
5656
+ throw error;
5657
+ }
5658
+ throw mapCanisterCallErrorToLiquidiumError("borrow_assets", error);
5445
5659
  }
5446
- if (evmSupplyContext.approvalStrategy !== EvmSupplyApprovalStrategy.none) {
5447
- await this.sendEthContractTransaction(
5448
- walletAdapter,
5449
- walletAddress,
5450
- createApproveTransaction({
5451
- tokenAddress: evmSupplyContext.tokenAddress,
5452
- spenderAddress: evmSupplyContext.spenderAddress,
5453
- amount: MAX_UINT256
5454
- }),
5455
- `supply-${request.action}-approve-max`
5660
+ }
5661
+ /**
5662
+ * Creates a borrow outflow using the provided wallet adapter.
5663
+ *
5664
+ * This is the convenience form of `prepareBorrow(...)` plus execution.
5665
+ *
5666
+ * @param params - Borrow request fields plus `signerChain` and `signerWalletAdapter`.
5667
+ * @returns The lending canister receipt as {@link OutflowDetails}.
5668
+ *
5669
+ * @remarks
5670
+ * `id` is always present. `txid` may be missing on the first response; the SDK does not
5671
+ * poll for it. Use history or app-level polling if you need the chain transaction id.
5672
+ */
5673
+ async borrow(params) {
5674
+ const action = await this.prepareBorrow(params);
5675
+ return await executeWith({
5676
+ walletAdapter: params.signerWalletAdapter,
5677
+ chain: params.signerChain,
5678
+ account: action.account
5679
+ })(action);
5680
+ }
5681
+ /**
5682
+ * Resolves a supply target for a deposit or repayment and optionally broadcasts it.
5683
+ *
5684
+ * Transfer mode can return manual broadcast instructions when wallet fields are
5685
+ * omitted. Contract-interaction mode always requires `walletAdapter`, `account`,
5686
+ * and `amount` because it must prepare and submit approval/deposit calls.
5687
+ *
5688
+ * The SDK does not poll for inflow status. When a `txid` is returned, it is the
5689
+ * caller's responsibility to track confirmation state using their own polling.
5690
+ *
5691
+ * @returns A {@link SupplyFlow} receipt with `type`, `target`, `submit`, and
5692
+ * an optional `txid` present when the SDK broadcast for you.
5693
+ */
5694
+ async supply(request) {
5695
+ return await this.createSupplyFlowExecutor().create(request);
5696
+ }
5697
+ /**
5698
+ * Fetches ERC-20 supply planning data with the configured EVM read client.
5699
+ *
5700
+ * Requires `evmRpcUrl` or `evmPublicClient` on the client. Used internally by
5701
+ * contract-interaction `supply`.
5702
+ *
5703
+ * @param request - Profile, pool, wallet, amount (token base units), and action.
5704
+ * @returns Locally computed {@link EvmSupplyContext} for approvals and deposit.
5705
+ */
5706
+ async getEvmSupplyContext(request) {
5707
+ return await this.createSupplyFlowExecutor().getEvmSupplyContext(request);
5708
+ }
5709
+ /**
5710
+ * Returns the read-only deposit address for an ETH stablecoin inflow target.
5711
+ *
5712
+ * This is a query call that does not create or mutate state. Use it when you
5713
+ * need the deposit address without hitting the authorization-gated update path.
5714
+ *
5715
+ * @param request - Profile, pool, asset, and supply action.
5716
+ * @returns The EVM deposit address for the derived account.
5717
+ */
5718
+ async getDepositAddress(request) {
5719
+ if (!isEthStablecoin(request.asset, Chain.ETH)) {
5720
+ throw new LiquidiumError(
5721
+ LiquidiumErrorCode.VALIDATION_ERROR,
5722
+ "getDepositAddress is only supported for ETH stablecoins"
5456
5723
  );
5457
- await this.waitForExpectedAllowance({
5458
- walletAddress,
5459
- tokenAddress: evmSupplyContext.tokenAddress,
5460
- spenderAddress: evmSupplyContext.spenderAddress,
5461
- amount: supplyAmount,
5462
- expectation: "sufficient"
5463
- });
5464
5724
  }
5465
- const depositTxid = await this.sendEthContractTransaction(
5466
- walletAdapter,
5467
- walletAddress,
5468
- createDepositErc20Transaction({
5469
- depositContractAddress: evmSupplyContext.depositContractAddress,
5470
- tokenAddress: evmSupplyContext.tokenAddress,
5471
- amount: supplyAmount,
5472
- poolId: request.poolId,
5473
- profileId: request.profileId,
5474
- destinationAccount: instruction.target.account,
5475
- action: request.action
5476
- }),
5477
- `supply-${request.action}-deposit-erc20`
5725
+ const tokenAddress = getEthStablecoinContractAddress(request.asset);
5726
+ const subaccount = encodeInflowSubaccount({
5727
+ action: request.action,
5728
+ principal: Principal.fromText(request.profileId)
5729
+ });
5730
+ const result = await createDepositAccountsActor(
5731
+ this.canisterContext
5732
+ ).get_deposit_address(
5733
+ {
5734
+ owner: Principal.fromText(request.poolId),
5735
+ subaccount: [subaccount]
5736
+ },
5737
+ [tokenAddress]
5478
5738
  );
5479
- try {
5480
- await this.submitInflowWithRetry(depositTxid, defaultSubmitInflowRequest);
5481
- } catch {
5739
+ if ("Err" in result) {
5740
+ throw mapDepositAccountErrorToLiquidiumError(result.Err);
5482
5741
  }
5483
- return depositTxid;
5742
+ return result.Ok;
5484
5743
  }
5485
- async sendNativeSupplyTransaction(params) {
5486
- switch (params.chain) {
5487
- case Chain.BTC: {
5488
- if (!params.walletAdapter.sendBtcTransaction) {
5489
- throw new LiquidiumError(
5490
- LiquidiumErrorCode.VALIDATION_ERROR,
5491
- "BTC wallet adapter does not support transaction sending"
5492
- );
5493
- }
5494
- return await params.walletAdapter.sendBtcTransaction({
5495
- chain: Chain.BTC,
5496
- toAddress: params.toAddress,
5497
- amountSats: params.amount,
5498
- account: params.senderAccount,
5499
- actionType: `supply-${params.action}`,
5500
- transferMode: TransferMode.native
5501
- });
5502
- }
5503
- case Chain.ETH: {
5504
- if (!params.walletAdapter.sendEthTransaction) {
5505
- throw new LiquidiumError(
5506
- LiquidiumErrorCode.VALIDATION_ERROR,
5507
- "ETH wallet adapter does not support transaction sending"
5508
- );
5509
- }
5510
- if (isEthStablecoin(params.asset, params.chain)) {
5511
- return await params.walletAdapter.sendEthTransaction({
5512
- chain: Chain.ETH,
5513
- account: params.senderAccount,
5514
- actionType: `supply-${params.action}`,
5515
- transferMode: TransferMode.native,
5516
- transaction: createTransferErc20Transaction({
5517
- tokenAddress: getEthStablecoinContractAddress(params.asset),
5518
- recipientAddress: params.toAddress,
5519
- amount: params.amount
5520
- })
5521
- });
5522
- }
5523
- return await params.walletAdapter.sendEthTransaction({
5524
- chain: Chain.ETH,
5525
- account: params.senderAccount,
5526
- actionType: `supply-${params.action}`,
5527
- transferMode: TransferMode.native,
5528
- transaction: {
5529
- to: params.toAddress,
5530
- value: params.amount.toString()
5531
- }
5532
- });
5744
+ /**
5745
+ * Estimates the network/deposit fee for an inflow target.
5746
+ *
5747
+ * ETH stablecoin deposit-address estimates are served by the deposit-address
5748
+ * canister. BTC estimates include the ckBTC minter deposit fee and ledger fee.
5749
+ *
5750
+ * @param request - Asset and chain pair to estimate for.
5751
+ * @returns Total fee estimate rounded up in the asset's base units.
5752
+ */
5753
+ async estimateInflowFee(request) {
5754
+ if (isEthStablecoin(request.asset, request.chain)) {
5755
+ const result = await createDepositAccountsActor(
5756
+ this.canisterContext
5757
+ ).estimate_deposit_fee([getEthStablecoinContractAddress(request.asset)]);
5758
+ if ("Err" in result) {
5759
+ throw mapDepositAccountErrorToLiquidiumError(result.Err);
5533
5760
  }
5534
- default:
5535
- throw new LiquidiumError(
5536
- LiquidiumErrorCode.VALIDATION_ERROR,
5537
- `Native-address wallet execution is not supported for ${params.chain}`
5538
- );
5761
+ return {
5762
+ totalFee: roundInflowFeeEstimate(request, result.Ok)
5763
+ };
5539
5764
  }
5765
+ if (request.asset === Asset.BTC && request.chain === Chain.BTC) {
5766
+ const estimate = await this.estimateBtcInflowFee();
5767
+ return {
5768
+ totalFee: roundInflowFeeEstimate(request, estimate.totalFee)
5769
+ };
5770
+ }
5771
+ throw new LiquidiumError(
5772
+ LiquidiumErrorCode.VALIDATION_ERROR,
5773
+ `Inflow fee estimates are not supported for ${request.asset} on ${request.chain}`
5774
+ );
5540
5775
  }
5541
- async submitInflowWithRetry(txid, extraRequest) {
5542
- await retryWithBackoff({
5543
- execute: async () => {
5544
- await this.submitInflow({ txid, ...extraRequest });
5545
- },
5546
- maxAttempts: SUBMIT_INFLOW_MAX_ATTEMPTS,
5547
- initialRetryDelayMs: SUBMIT_INFLOW_INITIAL_RETRY_DELAY_MS,
5548
- backoffMultiplier: SUBMIT_INFLOW_RETRY_BACKOFF_MULTIPLIER,
5549
- shouldRetryError: isRetriableInflowSubmitError
5550
- });
5776
+ async estimateBtcInflowFee() {
5777
+ const [minterFee, ledgerFee] = await Promise.all([
5778
+ createCkBtcMinterActor(this.canisterContext).get_deposit_fee(),
5779
+ createCkBtcLedgerActor(this.canisterContext).icrc1_fee()
5780
+ ]);
5781
+ return { totalFee: minterFee + ledgerFee };
5551
5782
  }
5552
5783
  /**
5553
5784
  * Submits an inflow transaction id for faster indexing.
5554
5785
  *
5555
5786
  * Uses the Liquidium SDK API.
5556
5787
  *
5557
- * @param request - Broadcast `txid` plus optional `chain` and inflow `type`.
5788
+ * @param request - Broadcast `txid` plus inflow `operation` and optional `chain`.
5558
5789
  * @returns Acknowledgement including the submitted `txid`.
5559
5790
  */
5560
5791
  async submitInflow(request) {
5561
5792
  const apiClient = this.requireApi();
5562
- return await apiClient.post(
5563
- SdkApiPath.inflow,
5564
- request
5565
- );
5793
+ const response = await apiClient.post(SdkApiPath.inflow, request);
5794
+ return {
5795
+ txid: response.txid
5796
+ };
5566
5797
  }
5567
5798
  /**
5568
5799
  * Returns whether borrowing is currently disabled by the protocol.
@@ -5582,350 +5813,76 @@ var LendingModule = class {
5582
5813
  "get_borrowing_disabled",
5583
5814
  error
5584
5815
  );
5585
- }
5586
- }
5587
- requireApi() {
5588
- if (!this.apiClient) {
5589
- throw new LiquidiumError(
5590
- LiquidiumErrorCode.VALIDATION_ERROR,
5591
- "Lending API actions require an API client"
5592
- );
5593
- }
5594
- return this.apiClient;
5595
- }
5596
- requireEvmReadClient(message) {
5597
- if (!this.evmReadClient) {
5598
- throw new LiquidiumError(LiquidiumErrorCode.VALIDATION_ERROR, message);
5599
- }
5600
- return this.evmReadClient;
5601
- }
5602
- async getPoolById(poolId) {
5603
- const pools = await createLendingActor(this.canisterContext).list_pools();
5604
- const selectedPool = pools.find(
5605
- (pool) => pool.principal.toText() === poolId
5606
- );
5607
- if (!selectedPool) {
5608
- throw new LiquidiumError(
5609
- LiquidiumErrorCode.POOL_NOT_FOUND,
5610
- `Pool not found: ${poolId}`
5611
- );
5612
- }
5613
- return selectedPool;
5614
- }
5615
- async normalizeOutflowReceiverAddress(params) {
5616
- const selectedPool = await this.getPoolById(params.poolId);
5617
- return normalizeExternalAddress({
5618
- address: params.receiverAddress,
5619
- asset: getVariantKey(selectedPool.asset),
5620
- chain: getVariantKey(selectedPool.chain)
5621
- });
5622
- }
5623
- async sendEthContractTransaction(walletAdapter, walletAddress, request, actionType) {
5624
- if (!walletAdapter.sendEthTransaction) {
5625
- throw new LiquidiumError(
5626
- LiquidiumErrorCode.VALIDATION_ERROR,
5627
- "ETH wallet adapter does not support transaction sending"
5628
- );
5629
- }
5630
- return await walletAdapter.sendEthTransaction({
5631
- chain: Chain.ETH,
5632
- account: walletAddress,
5633
- actionType,
5634
- transferMode: TransferMode.native,
5635
- transaction: request
5636
- });
5637
- }
5638
- async waitForExpectedAllowance(params) {
5639
- const evmReadClient = this.requireEvmReadClient(
5640
- "Contract-interaction supply requires an EVM RPC URL or public client"
5641
- );
5642
- let lastPollingError;
5643
- for (let pollIndex = 0; pollIndex < ETH_APPROVAL_MAX_POLLS; pollIndex += 1) {
5644
- try {
5645
- const allowance = await readErc20Allowance({
5646
- evmReadClient,
5647
- tokenAddress: params.tokenAddress,
5648
- ownerAddress: params.walletAddress,
5649
- spenderAddress: params.spenderAddress
5650
- });
5651
- const matchesExpectation = params.expectation === "zero" ? allowance === 0n : allowance >= params.amount;
5652
- if (matchesExpectation) {
5653
- return;
5654
- }
5655
- } catch (error) {
5656
- lastPollingError = error;
5657
- }
5658
- await delay(ETH_APPROVAL_POLL_INTERVAL_MS);
5659
- }
5660
- const lastPollingErrorMessage = getUnknownErrorMessage(lastPollingError);
5661
- throw new LiquidiumError(
5662
- LiquidiumErrorCode.SERVICE_UNAVAILABLE,
5663
- lastPollingErrorMessage ? `Timed out waiting for ${params.expectation} ETH allowance update. Last polling error: ${lastPollingErrorMessage}` : `Timed out waiting for ${params.expectation} ETH allowance update`
5664
- );
5665
- }
5666
- };
5667
- async function readErc20Allowance(params) {
5668
- const allowance = await params.evmReadClient.readContract({
5669
- address: params.tokenAddress,
5670
- abi: ERC20_ABI,
5671
- functionName: "allowance",
5672
- args: [
5673
- params.ownerAddress,
5674
- params.spenderAddress
5675
- ]
5676
- });
5677
- return BigInt(allowance);
5678
- }
5679
- async function readErc20Balance(params) {
5680
- const balance = await params.evmReadClient.readContract({
5681
- address: params.tokenAddress,
5682
- abi: ERC20_ABI,
5683
- functionName: "balanceOf",
5684
- args: [params.ownerAddress]
5685
- });
5686
- return BigInt(balance);
5687
- }
5688
- function getApprovalStrategy(params) {
5689
- if (params.allowance >= params.amount) {
5690
- return EvmSupplyApprovalStrategy.none;
5816
+ }
5691
5817
  }
5692
- if (params.allowance === 0n) {
5693
- return EvmSupplyApprovalStrategy.approveMax;
5818
+ requireApi() {
5819
+ if (!this.apiClient) {
5820
+ throw new LiquidiumError(
5821
+ LiquidiumErrorCode.VALIDATION_ERROR,
5822
+ "Lending API actions require an API client"
5823
+ );
5824
+ }
5825
+ return this.apiClient;
5694
5826
  }
5695
- return EvmSupplyApprovalStrategy.resetThenApproveMax;
5696
- }
5697
- function mapExpectedOutflowDetails(details, expectedOutflowType, operation) {
5698
- if (details.outflowType !== expectedOutflowType) {
5699
- throw new LiquidiumError(
5700
- LiquidiumErrorCode.INTERNAL,
5701
- `${operation} returned unexpected outflow type ${details.outflowType}`
5702
- );
5827
+ createSupplyFlowExecutor() {
5828
+ return new SupplyFlowExecutor({
5829
+ canisterContext: this.canisterContext,
5830
+ evmReadClient: this.evmReadClient,
5831
+ getPoolById: async (poolId) => await this.getPoolById(poolId),
5832
+ requireApi: () => {
5833
+ this.requireApi();
5834
+ },
5835
+ submitInflow: async (request) => await this.submitInflow(request)
5836
+ });
5703
5837
  }
5704
- if (details.receiver.type !== "External") {
5705
- throw new LiquidiumError(
5706
- LiquidiumErrorCode.INTERNAL,
5707
- `${operation} returned unexpected receiver type ${details.receiver.type}`
5838
+ async getPoolById(poolId) {
5839
+ const pools = await createFlexibleLendingActor(
5840
+ this.canisterContext
5841
+ ).list_pools();
5842
+ const selectedPool = pools.find(
5843
+ (pool) => pool.principal.toText() === poolId
5708
5844
  );
5845
+ const decodedPool = selectedPool ? decodeFlexiblePool(selectedPool) : null;
5846
+ if (!decodedPool) {
5847
+ throw new LiquidiumError(
5848
+ LiquidiumErrorCode.POOL_NOT_FOUND,
5849
+ `Pool not found: ${poolId}`
5850
+ );
5851
+ }
5852
+ return decodedPool;
5709
5853
  }
5710
- return details;
5711
- }
5712
- async function delay(timeoutMs) {
5713
- await new Promise((resolve) => setTimeout(resolve, timeoutMs));
5714
- }
5715
- function isRetriableInflowSubmitError(error) {
5716
- if (!(error instanceof LiquidiumError)) {
5717
- return false;
5718
- }
5719
- return error.code === LiquidiumErrorCode.SERVICE_UNAVAILABLE;
5720
- }
5721
- function getUnknownErrorMessage(error) {
5722
- if (error instanceof Error) {
5723
- return error.message;
5724
- }
5725
- if (typeof error === "string") {
5726
- return error;
5727
- }
5728
- return null;
5729
- }
5730
- function getDefaultSubmitInflowRequest(params) {
5731
- if (params.chain !== Chain.BTC && params.chain !== Chain.ETH) {
5732
- return void 0;
5733
- }
5734
- return {
5735
- chain: params.chain,
5736
- type: params.action === SupplyAction.repayment ? InflowSubmitType.REPAY : InflowSubmitType.DEPOSIT
5737
- };
5738
- }
5739
- function shouldSubmitInflow(params) {
5740
- if (params.mechanism !== SupplyPlanType.transfer) {
5741
- return true;
5854
+ async normalizeOutflowReceiverAddress(params) {
5855
+ const selectedPool = await this.getPoolById(params.poolId);
5856
+ return normalizeExternalAddress({
5857
+ address: params.receiverAddress,
5858
+ asset: selectedPool.asset,
5859
+ chain: selectedPool.chain
5860
+ });
5742
5861
  }
5743
- return !isEthStablecoin(params.instruction.asset, params.instruction.chain);
5744
- }
5745
- var flexibleLendingIdlFactory = ({ IDL }) => {
5746
- const PoolRecord = IDL.Record({
5747
- principal: IDL.Principal,
5748
- asset: IDL.Unknown,
5749
- chain: IDL.Unknown,
5750
- total_supply_at_last_sync: IDL.Nat,
5751
- total_debt_at_last_sync: IDL.Nat,
5752
- supply_cap: IDL.Opt(IDL.Nat),
5753
- borrow_cap: IDL.Opt(IDL.Nat),
5754
- max_ltv: IDL.Nat64,
5755
- liquidation_threshold: IDL.Nat64,
5756
- liquidation_bonus: IDL.Nat64,
5757
- protocol_liquidation_fee: IDL.Nat64,
5758
- reserve_factor: IDL.Nat64,
5759
- base_rate: IDL.Nat,
5760
- optimal_utilization_rate: IDL.Nat,
5761
- rate_slope_before: IDL.Nat,
5762
- rate_slope_after: IDL.Nat,
5763
- lending_index: IDL.Nat,
5764
- borrow_index: IDL.Nat,
5765
- same_asset_borrowing: IDL.Opt(IDL.Bool),
5766
- frozen: IDL.Bool,
5767
- last_updated: IDL.Opt(IDL.Nat64)
5768
- });
5769
- const BorrowingPowerRecord = IDL.Record({
5770
- max_borrowable_usd: IDL.Nat,
5771
- weighted_max_ltv: IDL.Nat
5772
- });
5773
- const PositionRecord = IDL.Record({
5774
- asset: IDL.Unknown,
5775
- total_debt_interest: IDL.Nat,
5776
- borrow_index_snapshot: IDL.Nat,
5777
- lending_index_snapshot: IDL.Nat,
5778
- debt_scaled: IDL.Nat,
5779
- total_earned_interest: IDL.Nat,
5780
- deposit_scaled: IDL.Nat,
5781
- pool_id: IDL.Principal,
5782
- unpaid_debt_interest: IDL.Nat,
5783
- last_update: IDL.Nat64,
5784
- user_profile: IDL.Principal
5785
- });
5786
- const UserStatsRecord = IDL.Record({
5787
- debt: IDL.Nat,
5788
- collateral: IDL.Nat,
5789
- acumulated_interest: IDL.Nat,
5790
- borrowing_power: BorrowingPowerRecord,
5791
- positions: IDL.Vec(PositionRecord),
5792
- weighted_liquidation_threshold: IDL.Nat
5793
- });
5794
- const PositionViewRecord = IDL.Record({
5795
- lending_index_now: IDL.Nat,
5796
- interest_since_snapshot: IDL.Nat,
5797
- asset: IDL.Unknown,
5798
- total_debt_interest: IDL.Nat,
5799
- borrow_index_snapshot: IDL.Nat,
5800
- debt_native_now: IDL.Nat,
5801
- borrow_index_now: IDL.Nat,
5802
- lending_index_snapshot: IDL.Nat,
5803
- debt_scaled: IDL.Nat,
5804
- total_earned_interest: IDL.Nat,
5805
- deposit_scaled: IDL.Nat,
5806
- earned_since_snapshot: IDL.Nat,
5807
- deposited_native_now: IDL.Nat,
5808
- pool_id: IDL.Principal,
5809
- last_update: IDL.Nat64,
5810
- user_profile: IDL.Principal
5811
- });
5812
- return IDL.Service({
5813
- list_pools: IDL.Func([], [IDL.Vec(PoolRecord)], ["query"]),
5814
- get_pool_rate: IDL.Func(
5815
- [IDL.Principal],
5816
- [IDL.Opt(IDL.Tuple(IDL.Nat, IDL.Nat, IDL.Nat))],
5817
- ["query"]
5818
- ),
5819
- get_health_factor: IDL.Func(
5820
- [IDL.Principal],
5821
- [IDL.Nat, UserStatsRecord],
5822
- ["query"]
5823
- ),
5824
- get_profile_stats: IDL.Func([IDL.Principal], [UserStatsRecord], ["query"]),
5825
- get_position: IDL.Func(
5826
- [IDL.Principal, IDL.Principal],
5827
- [IDL.Opt(PositionViewRecord)],
5828
- ["query"]
5829
- )
5830
- });
5831
5862
  };
5832
- function createFlexibleLendingActor(canisterContext) {
5833
- const canisterId = canisterContext.canisterIds.lending;
5834
- if (!canisterId) {
5863
+ function mapExpectedOutflowDetails(details, expectedOutflowType, operation) {
5864
+ if (details.outflowType !== expectedOutflowType) {
5835
5865
  throw new LiquidiumError(
5836
- LiquidiumErrorCode.SERVICE_UNAVAILABLE,
5837
- "Lending canister ID is not configured"
5866
+ LiquidiumErrorCode.INTERNAL,
5867
+ `${operation} returned unexpected outflow type ${details.outflowType}`
5838
5868
  );
5839
5869
  }
5840
- return Actor.createActor(flexibleLendingIdlFactory, {
5841
- agent: canisterContext.agent,
5842
- canisterId
5843
- });
5844
- }
5845
- function decodeFlexiblePool(pool) {
5846
- const asset = extractVariantTag(pool.asset, KNOWN_ASSET_TAGS);
5847
- const chain = extractVariantTag(pool.chain, KNOWN_CHAIN_TAGS);
5848
- if (!asset || !chain) {
5849
- return null;
5850
- }
5851
- return {
5852
- principal: pool.principal,
5853
- asset,
5854
- chain,
5855
- total_supply_at_last_sync: pool.total_supply_at_last_sync,
5856
- total_debt_at_last_sync: pool.total_debt_at_last_sync,
5857
- supply_cap: pool.supply_cap,
5858
- borrow_cap: pool.borrow_cap,
5859
- max_ltv: pool.max_ltv,
5860
- liquidation_threshold: pool.liquidation_threshold,
5861
- liquidation_bonus: pool.liquidation_bonus,
5862
- protocol_liquidation_fee: pool.protocol_liquidation_fee,
5863
- reserve_factor: pool.reserve_factor,
5864
- base_rate: pool.base_rate,
5865
- optimal_utilization_rate: pool.optimal_utilization_rate,
5866
- rate_slope_before: pool.rate_slope_before,
5867
- rate_slope_after: pool.rate_slope_after,
5868
- lending_index: pool.lending_index,
5869
- borrow_index: pool.borrow_index,
5870
- same_asset_borrowing: pool.same_asset_borrowing,
5871
- frozen: pool.frozen,
5872
- last_updated: pool.last_updated
5873
- };
5874
- }
5875
- function decodeFlexiblePosition(position) {
5876
- const asset = extractVariantTag(position.asset, KNOWN_ASSET_TAGS);
5877
- if (!asset) {
5878
- return null;
5879
- }
5880
- return {
5881
- asset,
5882
- total_debt_interest: position.total_debt_interest,
5883
- borrow_index_snapshot: position.borrow_index_snapshot,
5884
- lending_index_snapshot: position.lending_index_snapshot,
5885
- debt_scaled: position.debt_scaled,
5886
- total_earned_interest: position.total_earned_interest,
5887
- deposit_scaled: position.deposit_scaled,
5888
- pool_id: position.pool_id,
5889
- unpaid_debt_interest: position.unpaid_debt_interest,
5890
- last_update: position.last_update,
5891
- user_profile: position.user_profile
5892
- };
5893
- }
5894
- function decodeFlexiblePositionView(view) {
5895
- const asset = extractVariantTag(view.asset, KNOWN_ASSET_TAGS);
5896
- if (!asset) {
5897
- return null;
5898
- }
5899
5870
  return {
5900
- lending_index_now: view.lending_index_now,
5901
- interest_since_snapshot: view.interest_since_snapshot,
5902
- asset,
5903
- total_debt_interest: view.total_debt_interest,
5904
- borrow_index_snapshot: view.borrow_index_snapshot,
5905
- debt_native_now: view.debt_native_now,
5906
- borrow_index_now: view.borrow_index_now,
5907
- lending_index_snapshot: view.lending_index_snapshot,
5908
- debt_scaled: view.debt_scaled,
5909
- total_earned_interest: view.total_earned_interest,
5910
- deposit_scaled: view.deposit_scaled,
5911
- earned_since_snapshot: view.earned_since_snapshot,
5912
- deposited_native_now: view.deposited_native_now,
5913
- pool_id: view.pool_id,
5914
- last_update: view.last_update,
5915
- user_profile: view.user_profile
5871
+ ...details,
5872
+ status: createLiquidiumStatus({
5873
+ operation: mapOutflowTypeToStatusOperation(expectedOutflowType),
5874
+ state: details.txid ? "confirming" : "processing"
5875
+ })
5916
5876
  };
5917
5877
  }
5918
- function decodeFlexibleUserStats(stats) {
5919
- return {
5920
- debt: stats.debt,
5921
- collateral: stats.collateral,
5922
- acumulated_interest: stats.acumulated_interest,
5923
- borrowing_power: stats.borrowing_power,
5924
- positions: stats.positions.map(decodeFlexiblePosition).filter((position) => position !== null),
5925
- weighted_liquidation_threshold: stats.weighted_liquidation_threshold
5926
- };
5878
+ function mapOutflowTypeToStatusOperation(outflowType) {
5879
+ return outflowType;
5927
5880
  }
5928
5881
 
5882
+ // src/core/rates.ts
5883
+ var RATE_SCALE2 = 1000000000000000000000000000n;
5884
+ var RATE_DECIMALS = BigInt(RATE_SCALE2.toString().length - 1);
5885
+
5929
5886
  // src/modules/market/mappers.ts
5930
5887
  var DECIMAL_BASE = 10;
5931
5888
  var PAIR_SEPARATOR = "_";
@@ -6004,16 +5961,18 @@ var MarketModule = class {
6004
5961
  canisterContext;
6005
5962
  apiClient;
6006
5963
  /**
6007
- * Lists all pools with their current rates.
5964
+ * Lists SDK-supported pools with their current rates.
5965
+ *
5966
+ * Unsupported asset or chain variants returned by the canister are omitted.
6008
5967
  *
6009
- * @returns All configured lending pools enriched with their current rate data.
5968
+ * @returns Supported lending pools enriched with their current rate data.
6010
5969
  */
6011
5970
  async listPools() {
6012
5971
  void this.apiClient;
6013
5972
  try {
6014
5973
  const flexibleActor = createFlexibleLendingActor(this.canisterContext);
6015
5974
  const rawPools = await flexibleActor.list_pools();
6016
- const decodedPools = rawPools.map(decodeFlexiblePool).filter((pool) => pool !== null);
5975
+ const decodedPools = decodeSupportedFlexiblePools(rawPools);
6017
5976
  return await Promise.all(
6018
5977
  decodedPools.map(async (pool) => {
6019
5978
  const poolRate = await flexibleActor.get_pool_rate(pool.principal);
@@ -6120,6 +6079,17 @@ var MarketModule = class {
6120
6079
  }
6121
6080
  }
6122
6081
  };
6082
+ function decodeSupportedFlexiblePools(rawPools) {
6083
+ const decodedPools = [];
6084
+ for (const rawPool of rawPools) {
6085
+ const decodedPool = decodeFlexiblePool(rawPool);
6086
+ if (!decodedPool) {
6087
+ continue;
6088
+ }
6089
+ decodedPools.push(decodedPool);
6090
+ }
6091
+ return decodedPools;
6092
+ }
6123
6093
 
6124
6094
  // src/modules/positions/mappers.ts
6125
6095
  var USD_VALUE_SCALE_DECIMALS = 27n;
@@ -6450,6 +6420,7 @@ var LiquidiumClient = class {
6450
6420
  this.instantLoans = new InstantLoansModule(
6451
6421
  this.canisterContext,
6452
6422
  this.apiClient,
6423
+ this.activities,
6453
6424
  this.lending,
6454
6425
  this.positions
6455
6426
  );
@@ -6471,6 +6442,6 @@ function resolveEvmReadClient(config) {
6471
6442
  });
6472
6443
  }
6473
6444
 
6474
- export { AccountsModule, ActivitiesModule, ActivityDirection, ActivityFilter, ActivityKind, ActivityStatus, Asset, CK_ETH_DEPOSIT_CONTRACT_ADDRESS, Chain, Environment, EvmSupplyApprovalStrategy, HistoryModule, InflowSubmitType, InstantLoanStatus, InstantLoansModule, LendingModule, LiquidiumClient, LiquidiumError, LiquidiumErrorCode, MIN_BORROW_AMOUNTS_BY_ASSET, MarketModule, OutflowType, PositionsModule, QuoteModule, QuoteValidationErrorCode, QuoteWarningCode, RATE_DECIMALS, RATE_SCALE, SupplyAction, SupplyPlanType, TransferMode, USDC_CONTRACT_ADDRESS, USDT_CONTRACT_ADDRESS, UserHistoryStatus, WalletExecutionKind, createTransferErc20Transaction, executeWith, getMinimumBorrowAmount, intFromPublicId, publicIdFromInt };
6445
+ export { AccountsModule, ActivitiesModule, ActivityFilter, Asset, CK_ETH_DEPOSIT_CONTRACT_ADDRESS, Chain, Environment, EvmSupplyApprovalStrategy, HistoryModule, InstantLoansModule, LendingModule, LiquidiumClient, LiquidiumError, LiquidiumErrorCode, MIN_BORROW_AMOUNTS_BY_ASSET, MarketModule, OutflowType, PositionsModule, QuoteModule, QuoteValidationErrorCode, QuoteWarningCode, RATE_DECIMALS, RATE_SCALE2 as RATE_SCALE, SupplyAction, SupplyPlanType, TransferMode, USDC_CONTRACT_ADDRESS, USDT_CONTRACT_ADDRESS, WalletActionKind, WalletExecutionKind, createTransferErc20Transaction, executeWith, getMinimumBorrowAmount, intFromPublicId, publicIdFromInt };
6475
6446
  //# sourceMappingURL=index.js.map
6476
6447
  //# sourceMappingURL=index.js.map