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