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