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