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