@liquidium/client 0.3.4 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -3
- package/dist/index.cjs +1114 -1206
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +92 -277
- package/dist/index.d.ts +92 -277
- package/dist/index.js +1113 -1200
- 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
|
|
@@ -1370,8 +1366,8 @@ function mapCanisterWalletToWallet(canisterWallet) {
|
|
|
1370
1366
|
);
|
|
1371
1367
|
}
|
|
1372
1368
|
}
|
|
1373
|
-
var KNOWN_ASSET_TAGS = ["BTC", "
|
|
1374
|
-
var KNOWN_CHAIN_TAGS = ["BTC", "ETH"
|
|
1369
|
+
var KNOWN_ASSET_TAGS = ["BTC", "USDC", "USDT"];
|
|
1370
|
+
var KNOWN_CHAIN_TAGS = ["BTC", "ETH"];
|
|
1375
1371
|
function extractVariantTag(variant, knownTags) {
|
|
1376
1372
|
const [key] = Object.keys(variant);
|
|
1377
1373
|
if (!key) {
|
|
@@ -1684,49 +1680,24 @@ function decodeLoanCreatedEvent(payload) {
|
|
|
1684
1680
|
}
|
|
1685
1681
|
|
|
1686
1682
|
// src/core/sdk-api-paths.ts
|
|
1687
|
-
var
|
|
1688
|
-
|
|
1689
|
-
history: "v1",
|
|
1690
|
-
inflow: "v1",
|
|
1691
|
-
instantLoans: "v1"
|
|
1692
|
-
};
|
|
1683
|
+
var SDK_API_V1_PATH = "/v1";
|
|
1684
|
+
var SDK_API_V2_PATH = "/v2";
|
|
1693
1685
|
var SdkApiQueryParam = {
|
|
1694
1686
|
cursor: "cursor",
|
|
1695
1687
|
from: "from",
|
|
1688
|
+
filter: "filter",
|
|
1696
1689
|
limit: "limit",
|
|
1697
1690
|
market: "market",
|
|
1691
|
+
operations: "operations",
|
|
1698
1692
|
poolId: "poolId",
|
|
1699
1693
|
profileId: "profileId",
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
to: "to",
|
|
1703
|
-
types: "types"
|
|
1694
|
+
states: "states",
|
|
1695
|
+
to: "to"
|
|
1704
1696
|
};
|
|
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
|
-
}
|
|
1697
|
+
var ACTIVITIES = `${SDK_API_V2_PATH}/activities`;
|
|
1698
|
+
var HISTORY_USERS = `${SDK_API_V2_PATH}/history/users`;
|
|
1699
|
+
var INFLOW = `${SDK_API_V2_PATH}/inflow`;
|
|
1700
|
+
var INSTANT_LOANS = `${SDK_API_V1_PATH}/instant-loans`;
|
|
1730
1701
|
function buildHistoryUserTransactionsPath(user, query) {
|
|
1731
1702
|
const base = `${HISTORY_USERS}/${encodeURIComponent(user)}/transactions`;
|
|
1732
1703
|
const qs = query.toString();
|
|
@@ -1741,8 +1712,8 @@ function buildActivitiesPath(request) {
|
|
|
1741
1712
|
const query = new URLSearchParams({
|
|
1742
1713
|
[SdkApiQueryParam.profileId]: request.profileId
|
|
1743
1714
|
});
|
|
1744
|
-
if (request.
|
|
1745
|
-
query.set(SdkApiQueryParam.
|
|
1715
|
+
if (request.filter) {
|
|
1716
|
+
query.set(SdkApiQueryParam.filter, request.filter);
|
|
1746
1717
|
}
|
|
1747
1718
|
return `${ACTIVITIES}?${query.toString()}`;
|
|
1748
1719
|
}
|
|
@@ -1800,12 +1771,6 @@ function parseBigInt(value, label) {
|
|
|
1800
1771
|
);
|
|
1801
1772
|
}
|
|
1802
1773
|
}
|
|
1803
|
-
function parseOptionalBigInt(value, label) {
|
|
1804
|
-
if (value === void 0) {
|
|
1805
|
-
return void 0;
|
|
1806
|
-
}
|
|
1807
|
-
return parseBigInt(value, label);
|
|
1808
|
-
}
|
|
1809
1774
|
|
|
1810
1775
|
// src/modules/instant-loans/ref-code.ts
|
|
1811
1776
|
var REF_LENGTH = 6;
|
|
@@ -1876,28 +1841,8 @@ var ActivityFilter = {
|
|
|
1876
1841
|
completed: "completed",
|
|
1877
1842
|
all: "all"
|
|
1878
1843
|
};
|
|
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
1844
|
|
|
1899
1845
|
// src/modules/activities/activities.ts
|
|
1900
|
-
var PRE_TERMINAL_ETH_ACTIVITY_ID_PREFIX = "pre_terminal_eth_";
|
|
1901
1846
|
var ActivitiesModule = class {
|
|
1902
1847
|
constructor(apiClient, canisterContext) {
|
|
1903
1848
|
this.apiClient = apiClient;
|
|
@@ -1906,11 +1851,11 @@ var ActivitiesModule = class {
|
|
|
1906
1851
|
apiClient;
|
|
1907
1852
|
canisterContext;
|
|
1908
1853
|
/**
|
|
1909
|
-
* Lists profile activities. Defaults to
|
|
1854
|
+
* Lists profile activities. Defaults to active activities.
|
|
1910
1855
|
*
|
|
1911
1856
|
* Uses the Liquidium SDK API.
|
|
1912
1857
|
*
|
|
1913
|
-
* @param request - Profile id or instant-loan short reference plus optional
|
|
1858
|
+
* @param request - Profile id or instant-loan short reference plus optional lifecycle filter.
|
|
1914
1859
|
* @returns Activities owned by the resolved profile.
|
|
1915
1860
|
*/
|
|
1916
1861
|
async list(request) {
|
|
@@ -1919,7 +1864,7 @@ var ActivitiesModule = class {
|
|
|
1919
1864
|
const response = await apiClient.get(
|
|
1920
1865
|
buildActivitiesPath({
|
|
1921
1866
|
profileId,
|
|
1922
|
-
|
|
1867
|
+
filter: request.filter ?? ActivityFilter.active
|
|
1923
1868
|
})
|
|
1924
1869
|
);
|
|
1925
1870
|
return response.activities.map(mapActivity);
|
|
@@ -2000,76 +1945,48 @@ function mapInstantLoanLookupError(error) {
|
|
|
2000
1945
|
return new LiquidiumError(LiquidiumErrorCode.INTERNAL, key);
|
|
2001
1946
|
}
|
|
2002
1947
|
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")
|
|
1948
|
+
const amount = parseBigInt(wire.amount, "activity amount");
|
|
1949
|
+
const activityBase = {
|
|
1950
|
+
id: wire.id,
|
|
1951
|
+
poolId: wire.poolId,
|
|
1952
|
+
asset: wire.asset,
|
|
1953
|
+
chain: wire.chain,
|
|
1954
|
+
amount,
|
|
1955
|
+
timestampMs: wire.timestampMs
|
|
2024
1956
|
};
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
return kind;
|
|
1957
|
+
if (isInflowOperation(wire.status.operation)) {
|
|
1958
|
+
const activity = {
|
|
1959
|
+
...activityBase,
|
|
1960
|
+
status: wire.status
|
|
1961
|
+
};
|
|
1962
|
+
if (wire.txids) {
|
|
1963
|
+
activity.txids = wire.txids;
|
|
1964
|
+
}
|
|
1965
|
+
if (wire.topUp) {
|
|
1966
|
+
activity.topUp = mapActivityTopUp(wire.topUp);
|
|
1967
|
+
}
|
|
1968
|
+
return activity;
|
|
2038
1969
|
}
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
}
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
1970
|
+
if (isOutflowOperation(wire.status.operation)) {
|
|
1971
|
+
const activity = {
|
|
1972
|
+
...activityBase,
|
|
1973
|
+
status: wire.status
|
|
1974
|
+
};
|
|
1975
|
+
if (wire.txids) {
|
|
1976
|
+
activity.txids = wire.txids;
|
|
1977
|
+
}
|
|
1978
|
+
return activity;
|
|
2047
1979
|
}
|
|
2048
1980
|
throw new LiquidiumError(
|
|
2049
1981
|
LiquidiumErrorCode.INTERNAL,
|
|
2050
|
-
`Invalid
|
|
1982
|
+
`Invalid activity operation: ${wire.status.operation}`
|
|
2051
1983
|
);
|
|
2052
1984
|
}
|
|
2053
|
-
function
|
|
2054
|
-
|
|
2055
|
-
return status;
|
|
2056
|
-
}
|
|
2057
|
-
throw new LiquidiumError(
|
|
2058
|
-
LiquidiumErrorCode.INTERNAL,
|
|
2059
|
-
`Invalid outflow activity status: ${status}`
|
|
2060
|
-
);
|
|
1985
|
+
function isInflowOperation(operation) {
|
|
1986
|
+
return operation === "deposit" || operation === "repayment";
|
|
2061
1987
|
}
|
|
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";
|
|
1988
|
+
function isOutflowOperation(operation) {
|
|
1989
|
+
return operation === "borrow" || operation === "withdrawal";
|
|
2073
1990
|
}
|
|
2074
1991
|
function mapActivityTopUp(wire) {
|
|
2075
1992
|
return {
|
|
@@ -2085,24 +2002,6 @@ function mapActivityTopUp(wire) {
|
|
|
2085
2002
|
)
|
|
2086
2003
|
};
|
|
2087
2004
|
}
|
|
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
2005
|
|
|
2107
2006
|
// src/modules/history/history.ts
|
|
2108
2007
|
var HistoryModule = class {
|
|
@@ -2120,154 +2019,41 @@ var HistoryModule = class {
|
|
|
2120
2019
|
return this.apiClient;
|
|
2121
2020
|
}
|
|
2122
2021
|
/**
|
|
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.
|
|
2022
|
+
* Returns transaction history for a user.
|
|
2219
2023
|
*
|
|
2220
|
-
* @param
|
|
2221
|
-
* @param
|
|
2222
|
-
* @returns Paginated
|
|
2024
|
+
* @param user - The Liquidium profile principal text.
|
|
2025
|
+
* @param filters - Optional pool, operation, state, time range, and pagination filters.
|
|
2026
|
+
* @returns Paginated user history entries.
|
|
2223
2027
|
*/
|
|
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 = {}) {
|
|
2028
|
+
async getUserTransactionHistory(user, filters = {}) {
|
|
2239
2029
|
const apiClient = this.requireApi();
|
|
2240
|
-
const normalizedFilters = normalizeTransactionHistoryFilters(
|
|
2241
|
-
marketOrFilters,
|
|
2242
|
-
filters
|
|
2243
|
-
);
|
|
2244
2030
|
const query = new URLSearchParams();
|
|
2245
|
-
if (
|
|
2246
|
-
query.set(SdkApiQueryParam.cursor,
|
|
2031
|
+
if (filters.cursor) {
|
|
2032
|
+
query.set(SdkApiQueryParam.cursor, filters.cursor);
|
|
2247
2033
|
}
|
|
2248
|
-
if (
|
|
2249
|
-
query.set(SdkApiQueryParam.market,
|
|
2034
|
+
if (filters.market) {
|
|
2035
|
+
query.set(SdkApiQueryParam.market, filters.market);
|
|
2250
2036
|
}
|
|
2251
|
-
if (
|
|
2252
|
-
query.set(SdkApiQueryParam.poolId,
|
|
2037
|
+
if (filters.poolId) {
|
|
2038
|
+
query.set(SdkApiQueryParam.poolId, filters.poolId);
|
|
2253
2039
|
}
|
|
2254
|
-
if (
|
|
2255
|
-
query.set(SdkApiQueryParam.
|
|
2040
|
+
if (filters.operations?.length) {
|
|
2041
|
+
query.set(SdkApiQueryParam.operations, filters.operations.join(","));
|
|
2256
2042
|
}
|
|
2257
|
-
if (
|
|
2043
|
+
if (filters.states?.length) {
|
|
2258
2044
|
query.set(
|
|
2259
|
-
SdkApiQueryParam.
|
|
2260
|
-
|
|
2045
|
+
SdkApiQueryParam.states,
|
|
2046
|
+
createHistoryStateFilterParam(filters.states)
|
|
2261
2047
|
);
|
|
2262
2048
|
}
|
|
2263
|
-
if (
|
|
2264
|
-
query.set(SdkApiQueryParam.from,
|
|
2049
|
+
if (filters.from) {
|
|
2050
|
+
query.set(SdkApiQueryParam.from, filters.from);
|
|
2265
2051
|
}
|
|
2266
|
-
if (
|
|
2267
|
-
query.set(SdkApiQueryParam.to,
|
|
2052
|
+
if (filters.to) {
|
|
2053
|
+
query.set(SdkApiQueryParam.to, filters.to);
|
|
2268
2054
|
}
|
|
2269
|
-
if (
|
|
2270
|
-
query.set(SdkApiQueryParam.limit, String(
|
|
2055
|
+
if (filters.limit !== void 0) {
|
|
2056
|
+
query.set(SdkApiQueryParam.limit, String(filters.limit));
|
|
2271
2057
|
}
|
|
2272
2058
|
const requestPath = buildHistoryUserTransactionsPath(user, query);
|
|
2273
2059
|
const response = await apiClient.get(requestPath);
|
|
@@ -2276,30 +2062,33 @@ var HistoryModule = class {
|
|
|
2276
2062
|
nextCursor: response.nextCursor
|
|
2277
2063
|
};
|
|
2278
2064
|
}
|
|
2279
|
-
|
|
2065
|
+
/**
|
|
2066
|
+
* Returns liquidation history for a user.
|
|
2067
|
+
*
|
|
2068
|
+
* @param user - The Liquidium profile principal text.
|
|
2069
|
+
* @param filters - Optional pool, time range, and pagination filters.
|
|
2070
|
+
* @returns Paginated liquidation history entries.
|
|
2071
|
+
*/
|
|
2072
|
+
async getLiquidationHistory(user, filters = {}) {
|
|
2280
2073
|
const apiClient = this.requireApi();
|
|
2281
|
-
const normalizedFilters = normalizeLiquidationHistoryFilters(
|
|
2282
|
-
marketOrFilters,
|
|
2283
|
-
filters
|
|
2284
|
-
);
|
|
2285
2074
|
const query = new URLSearchParams();
|
|
2286
|
-
if (
|
|
2287
|
-
query.set(SdkApiQueryParam.cursor,
|
|
2075
|
+
if (filters.cursor) {
|
|
2076
|
+
query.set(SdkApiQueryParam.cursor, filters.cursor);
|
|
2288
2077
|
}
|
|
2289
|
-
if (
|
|
2290
|
-
query.set(SdkApiQueryParam.market,
|
|
2078
|
+
if (filters.market) {
|
|
2079
|
+
query.set(SdkApiQueryParam.market, filters.market);
|
|
2291
2080
|
}
|
|
2292
|
-
if (
|
|
2293
|
-
query.set(SdkApiQueryParam.poolId,
|
|
2081
|
+
if (filters.poolId) {
|
|
2082
|
+
query.set(SdkApiQueryParam.poolId, filters.poolId);
|
|
2294
2083
|
}
|
|
2295
|
-
if (
|
|
2296
|
-
query.set(SdkApiQueryParam.from,
|
|
2084
|
+
if (filters.from) {
|
|
2085
|
+
query.set(SdkApiQueryParam.from, filters.from);
|
|
2297
2086
|
}
|
|
2298
|
-
if (
|
|
2299
|
-
query.set(SdkApiQueryParam.to,
|
|
2087
|
+
if (filters.to) {
|
|
2088
|
+
query.set(SdkApiQueryParam.to, filters.to);
|
|
2300
2089
|
}
|
|
2301
|
-
if (
|
|
2302
|
-
query.set(SdkApiQueryParam.limit, String(
|
|
2090
|
+
if (filters.limit !== void 0) {
|
|
2091
|
+
query.set(SdkApiQueryParam.limit, String(filters.limit));
|
|
2303
2092
|
}
|
|
2304
2093
|
const requestPath = buildHistoryUserLiquidationsPath(user, query);
|
|
2305
2094
|
const response = await apiClient.get(requestPath);
|
|
@@ -2312,77 +2101,59 @@ var HistoryModule = class {
|
|
|
2312
2101
|
function mapUserTransactionHistoryEntry(item) {
|
|
2313
2102
|
return {
|
|
2314
2103
|
id: item.id,
|
|
2315
|
-
type: mapUserTransactionHistoryType(item.type),
|
|
2316
2104
|
amount: parseBigInt(item.amount, "history user amount"),
|
|
2317
2105
|
poolId: item.poolId,
|
|
2318
2106
|
timestamp: item.timestamp,
|
|
2319
|
-
status:
|
|
2107
|
+
status: item.status,
|
|
2320
2108
|
txids: item.txids
|
|
2321
2109
|
};
|
|
2322
2110
|
}
|
|
2323
2111
|
function mapUserLiquidationHistoryEntry(item) {
|
|
2324
|
-
if (item.
|
|
2112
|
+
if (item.status.operation !== "liquidation" || item.status.state !== "completed") {
|
|
2325
2113
|
throw new LiquidiumError(
|
|
2326
2114
|
LiquidiumErrorCode.INTERNAL,
|
|
2327
|
-
`Invalid liquidation history
|
|
2115
|
+
`Invalid liquidation history status: ${item.status.state}`
|
|
2328
2116
|
);
|
|
2329
2117
|
}
|
|
2330
2118
|
return {
|
|
2331
2119
|
id: item.id,
|
|
2332
|
-
type: "liquidation",
|
|
2333
2120
|
amount: parseBigInt(item.amount, "history user amount"),
|
|
2334
2121
|
poolId: item.poolId,
|
|
2335
2122
|
timestamp: item.timestamp,
|
|
2336
|
-
status:
|
|
2123
|
+
status: item.status,
|
|
2337
2124
|
txids: item.txids
|
|
2338
2125
|
};
|
|
2339
2126
|
}
|
|
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));
|
|
2127
|
+
function createHistoryStateFilterParam(states) {
|
|
2128
|
+
for (const state of states) {
|
|
2129
|
+
validateHistoryStateFilter(state);
|
|
2366
2130
|
}
|
|
2367
|
-
return
|
|
2131
|
+
return [...new Set(states)].join(",");
|
|
2368
2132
|
}
|
|
2369
|
-
function
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2133
|
+
function validateHistoryStateFilter(state) {
|
|
2134
|
+
switch (state) {
|
|
2135
|
+
case "action_required":
|
|
2136
|
+
case "confirming":
|
|
2137
|
+
case "processing":
|
|
2138
|
+
case "completed":
|
|
2139
|
+
case "failed":
|
|
2140
|
+
return;
|
|
2141
|
+
default:
|
|
2142
|
+
throw new LiquidiumError(
|
|
2143
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
2144
|
+
`History state filter is not supported: ${state}`
|
|
2145
|
+
);
|
|
2378
2146
|
}
|
|
2379
|
-
return { ...marketOrFilters ?? {}, ...filters };
|
|
2380
2147
|
}
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2148
|
+
|
|
2149
|
+
// src/core/status.ts
|
|
2150
|
+
function createLiquidiumStatus(params) {
|
|
2151
|
+
return {
|
|
2152
|
+
operation: params.operation,
|
|
2153
|
+
state: params.state,
|
|
2154
|
+
confirmations: params.confirmations ?? null,
|
|
2155
|
+
requiredConfirmations: params.requiredConfirmations ?? null
|
|
2156
|
+
};
|
|
2386
2157
|
}
|
|
2387
2158
|
|
|
2388
2159
|
// src/core/utils/api-response-parsers.ts
|
|
@@ -3979,20 +3750,11 @@ function getChainForInstantLoanAsset(asset) {
|
|
|
3979
3750
|
return asset;
|
|
3980
3751
|
}
|
|
3981
3752
|
|
|
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
3753
|
// src/modules/instant-loans/instant-loans.ts
|
|
3993
3754
|
var REPAYMENT_BUFFER_SECONDS = 86400n;
|
|
3994
|
-
var
|
|
3755
|
+
var RATE_SCALE = 10n ** 27n;
|
|
3995
3756
|
var SECONDS_PER_YEAR = 31536000n;
|
|
3757
|
+
var MILLISECONDS_PER_SECOND3 = 1e3;
|
|
3996
3758
|
var ETH_STABLECOIN_INFLOW_FEE_FALLBACK = 1500000n;
|
|
3997
3759
|
var INSTANT_LOAN_MIN_SLIPPAGE_BUFFER_BPS = 200n;
|
|
3998
3760
|
var INSTANT_LOAN_FIND_QUERY_MAX_LENGTH = 256;
|
|
@@ -4004,14 +3766,16 @@ var INSTANT_LOAN_ASSETS = [
|
|
|
4004
3766
|
Asset.USDT
|
|
4005
3767
|
];
|
|
4006
3768
|
var InstantLoansModule = class {
|
|
4007
|
-
constructor(canisterContext, apiClient, lending, positions) {
|
|
3769
|
+
constructor(canisterContext, apiClient, activities, lending, positions) {
|
|
4008
3770
|
this.canisterContext = canisterContext;
|
|
4009
3771
|
this.apiClient = apiClient;
|
|
3772
|
+
this.activities = activities;
|
|
4010
3773
|
this.lending = lending;
|
|
4011
3774
|
this.positions = positions;
|
|
4012
3775
|
}
|
|
4013
3776
|
canisterContext;
|
|
4014
3777
|
apiClient;
|
|
3778
|
+
activities;
|
|
4015
3779
|
lending;
|
|
4016
3780
|
positions;
|
|
4017
3781
|
/**
|
|
@@ -4211,7 +3975,8 @@ var InstantLoansModule = class {
|
|
|
4211
3975
|
const response = await apiClient.get(
|
|
4212
3976
|
buildInstantLoanFindPath({ query })
|
|
4213
3977
|
);
|
|
4214
|
-
|
|
3978
|
+
const candidates = "candidates" in response ? response.candidates : response.loans;
|
|
3979
|
+
return candidates.map(mapCandidateWire);
|
|
4215
3980
|
}
|
|
4216
3981
|
async getLoanRecord(loanId) {
|
|
4217
3982
|
try {
|
|
@@ -4250,6 +4015,7 @@ var InstantLoansModule = class {
|
|
|
4250
4015
|
borrowAmount: record.borrow_amount,
|
|
4251
4016
|
borrowDestination: accountFromCanister(record.borrow_destination),
|
|
4252
4017
|
refundDestination: accountFromCanister(record.refund_destination),
|
|
4018
|
+
started: record.started,
|
|
4253
4019
|
depositDetectedTimestamp: record.deposit_detected_ts[0] ?? null,
|
|
4254
4020
|
expiryTimestamp: record.expires_at[0] ?? deriveDepositExpiryTimestamp({
|
|
4255
4021
|
depositDetectedTimestamp: record.deposit_detected_ts[0] ?? null,
|
|
@@ -4278,7 +4044,8 @@ var InstantLoansModule = class {
|
|
|
4278
4044
|
repayTarget,
|
|
4279
4045
|
collateralPosition,
|
|
4280
4046
|
borrowPosition,
|
|
4281
|
-
borrowPoolRate
|
|
4047
|
+
borrowPoolRate,
|
|
4048
|
+
activeActivities
|
|
4282
4049
|
] = await Promise.all([
|
|
4283
4050
|
resolveSupplyTarget(this.canisterContext, {
|
|
4284
4051
|
profileId,
|
|
@@ -4294,7 +4061,8 @@ var InstantLoansModule = class {
|
|
|
4294
4061
|
}),
|
|
4295
4062
|
this.positions.getPosition(profileId, collateralPoolId),
|
|
4296
4063
|
this.positions.getPosition(profileId, borrowPoolId),
|
|
4297
|
-
this.positions.market.getPoolRate(borrowPoolId)
|
|
4064
|
+
this.positions.market.getPoolRate(borrowPoolId),
|
|
4065
|
+
this.activities.list({ profileId, filter: ActivityFilter.active })
|
|
4298
4066
|
]);
|
|
4299
4067
|
const totalDebtAmount = calculateTotalDebtAmount(borrowPosition);
|
|
4300
4068
|
const interestBufferAmount = calculateInterestBufferAmount(
|
|
@@ -4311,8 +4079,12 @@ var InstantLoansModule = class {
|
|
|
4311
4079
|
const borrowedDecimals = borrowPosition?.borrowedDecimals ?? getAssetNativeDecimals(borrowAsset);
|
|
4312
4080
|
const debtInterestAmount = borrowPosition?.debtInterest ?? 0n;
|
|
4313
4081
|
const status = deriveInstantLoanStatus({
|
|
4082
|
+
started: input.started,
|
|
4083
|
+
depositDetectedTimestamp: input.depositDetectedTimestamp,
|
|
4084
|
+
expiryTimestamp: input.expiryTimestamp,
|
|
4314
4085
|
collateralAmount: currentCollateralAmount,
|
|
4315
|
-
totalDebtAmount
|
|
4086
|
+
totalDebtAmount,
|
|
4087
|
+
activeActivities
|
|
4316
4088
|
});
|
|
4317
4089
|
const initialDeposit = await this.createInitialDepositQuote({
|
|
4318
4090
|
collateralAmount,
|
|
@@ -4464,7 +4236,7 @@ function calculateInterestBufferAmount(borrowPosition, annualBorrowRate) {
|
|
|
4464
4236
|
if (totalDebtAmount <= 0n || annualBorrowRate <= 0n) {
|
|
4465
4237
|
return 0n;
|
|
4466
4238
|
}
|
|
4467
|
-
const interestBuffer = totalDebtAmount * annualBorrowRate * REPAYMENT_BUFFER_SECONDS /
|
|
4239
|
+
const interestBuffer = totalDebtAmount * annualBorrowRate * REPAYMENT_BUFFER_SECONDS / RATE_SCALE / SECONDS_PER_YEAR;
|
|
4468
4240
|
return interestBuffer > 1n ? interestBuffer : 1n;
|
|
4469
4241
|
}
|
|
4470
4242
|
function calculateTotalDebtAmount(borrowPosition) {
|
|
@@ -4474,13 +4246,75 @@ function calculateTotalDebtAmount(borrowPosition) {
|
|
|
4474
4246
|
return borrowPosition.borrowed + borrowPosition.debtInterest;
|
|
4475
4247
|
}
|
|
4476
4248
|
function deriveInstantLoanStatus(input) {
|
|
4249
|
+
const activeActivityStatus = deriveActiveInstantLoanActivityStatus(input);
|
|
4250
|
+
if (activeActivityStatus) {
|
|
4251
|
+
return activeActivityStatus;
|
|
4252
|
+
}
|
|
4477
4253
|
if (input.totalDebtAmount > 0n) {
|
|
4478
|
-
return
|
|
4254
|
+
return createLiquidiumStatus({
|
|
4255
|
+
operation: "repayment",
|
|
4256
|
+
state: "active"
|
|
4257
|
+
});
|
|
4258
|
+
}
|
|
4259
|
+
if (input.started) {
|
|
4260
|
+
return createLiquidiumStatus({
|
|
4261
|
+
operation: "repayment",
|
|
4262
|
+
state: "completed"
|
|
4263
|
+
});
|
|
4264
|
+
}
|
|
4265
|
+
if (isInstantLoanDepositExpired(input)) {
|
|
4266
|
+
return createLiquidiumStatus({
|
|
4267
|
+
operation: "deposit",
|
|
4268
|
+
state: "expired"
|
|
4269
|
+
});
|
|
4479
4270
|
}
|
|
4480
4271
|
if (input.collateralAmount > 0n) {
|
|
4481
|
-
return
|
|
4272
|
+
return createLiquidiumStatus({
|
|
4273
|
+
operation: "deposit",
|
|
4274
|
+
state: "processing"
|
|
4275
|
+
});
|
|
4276
|
+
}
|
|
4277
|
+
if (input.depositDetectedTimestamp !== null) {
|
|
4278
|
+
return createLiquidiumStatus({
|
|
4279
|
+
operation: "deposit",
|
|
4280
|
+
state: "confirming"
|
|
4281
|
+
});
|
|
4282
|
+
}
|
|
4283
|
+
return createLiquidiumStatus({
|
|
4284
|
+
operation: "deposit",
|
|
4285
|
+
state: "action_required"
|
|
4286
|
+
});
|
|
4287
|
+
}
|
|
4288
|
+
function deriveActiveInstantLoanActivityStatus(input) {
|
|
4289
|
+
if (!input.started) {
|
|
4290
|
+
return findActiveActivityStatus(input.activeActivities, "deposit");
|
|
4291
|
+
}
|
|
4292
|
+
const borrowStatus = findActiveActivityStatus(
|
|
4293
|
+
input.activeActivities,
|
|
4294
|
+
"borrow"
|
|
4295
|
+
);
|
|
4296
|
+
if (borrowStatus) {
|
|
4297
|
+
return borrowStatus;
|
|
4298
|
+
}
|
|
4299
|
+
return findActiveActivityStatus(input.activeActivities, "repayment");
|
|
4300
|
+
}
|
|
4301
|
+
function findActiveActivityStatus(activities, operation) {
|
|
4302
|
+
const activity = activities.find(
|
|
4303
|
+
(candidate) => candidate.status.operation === operation
|
|
4304
|
+
);
|
|
4305
|
+
return activity?.status ?? null;
|
|
4306
|
+
}
|
|
4307
|
+
function isInstantLoanDepositExpired(input) {
|
|
4308
|
+
if (input.started || input.depositDetectedTimestamp === null) {
|
|
4309
|
+
return false;
|
|
4310
|
+
}
|
|
4311
|
+
if (input.expiryTimestamp === null) {
|
|
4312
|
+
return false;
|
|
4482
4313
|
}
|
|
4483
|
-
return
|
|
4314
|
+
return input.expiryTimestamp <= getCurrentUnixTimestampSeconds();
|
|
4315
|
+
}
|
|
4316
|
+
function getCurrentUnixTimestampSeconds() {
|
|
4317
|
+
return BigInt(Math.floor(Date.now() / MILLISECONDS_PER_SECOND3));
|
|
4484
4318
|
}
|
|
4485
4319
|
function deriveDepositExpiryTimestamp(input) {
|
|
4486
4320
|
if (input.depositDetectedTimestamp === null) {
|
|
@@ -4929,6 +4763,28 @@ function accountTypeToString(accountType) {
|
|
|
4929
4763
|
}
|
|
4930
4764
|
}
|
|
4931
4765
|
|
|
4766
|
+
// src/modules/lending/_internal/inflow-fee-rounding.ts
|
|
4767
|
+
var ETH_STABLECOIN_INFLOW_FEE_ROUND_UP_TO_NEAREST_10_CENTS = 100000n;
|
|
4768
|
+
var BTC_INFLOW_FEE_ROUND_UP_TO_NEAREST_SATS = 500n;
|
|
4769
|
+
function roundInflowFeeEstimate(request, totalFee) {
|
|
4770
|
+
if (totalFee <= 0n) {
|
|
4771
|
+
return 0n;
|
|
4772
|
+
}
|
|
4773
|
+
if (isEthStablecoin(request.asset, request.chain)) {
|
|
4774
|
+
return roundUpToNearest(
|
|
4775
|
+
totalFee,
|
|
4776
|
+
ETH_STABLECOIN_INFLOW_FEE_ROUND_UP_TO_NEAREST_10_CENTS
|
|
4777
|
+
);
|
|
4778
|
+
}
|
|
4779
|
+
if (request.asset === Asset.BTC && request.chain === Chain.BTC) {
|
|
4780
|
+
return roundUpToNearest(totalFee, BTC_INFLOW_FEE_ROUND_UP_TO_NEAREST_SATS);
|
|
4781
|
+
}
|
|
4782
|
+
return totalFee;
|
|
4783
|
+
}
|
|
4784
|
+
function roundUpToNearest(amount, roundingUnit) {
|
|
4785
|
+
return ceilDivBigint(amount, roundingUnit) * roundingUnit;
|
|
4786
|
+
}
|
|
4787
|
+
|
|
4932
4788
|
// src/core/utils/retry.ts
|
|
4933
4789
|
var MIN_ATTEMPTS = 1;
|
|
4934
4790
|
var MIN_INITIAL_RETRY_DELAY_MS = 0;
|
|
@@ -4980,835 +4836,966 @@ function assertValidRetryOptions(options) {
|
|
|
4980
4836
|
}
|
|
4981
4837
|
}
|
|
4982
4838
|
|
|
4983
|
-
// src/modules/lending/_internal/
|
|
4984
|
-
var
|
|
4985
|
-
var
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4839
|
+
// src/modules/lending/_internal/supply-flow.ts
|
|
4840
|
+
var SUBMIT_INFLOW_MAX_ATTEMPTS = 4;
|
|
4841
|
+
var SUBMIT_INFLOW_INITIAL_RETRY_DELAY_MS = 1500;
|
|
4842
|
+
var SUBMIT_INFLOW_RETRY_BACKOFF_MULTIPLIER = 2;
|
|
4843
|
+
var ETH_APPROVAL_POLL_INTERVAL_MS = 2e3;
|
|
4844
|
+
var ETH_APPROVAL_MAX_POLLS = 30;
|
|
4845
|
+
var SupplyFlowExecutor = class {
|
|
4846
|
+
constructor(params) {
|
|
4847
|
+
this.params = params;
|
|
4989
4848
|
}
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4849
|
+
params;
|
|
4850
|
+
async create(request) {
|
|
4851
|
+
const target = await resolveSupplyTarget(
|
|
4852
|
+
this.params.canisterContext,
|
|
4853
|
+
request
|
|
4994
4854
|
);
|
|
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()
|
|
4855
|
+
const instruction = {
|
|
4856
|
+
poolId: request.poolId,
|
|
4857
|
+
asset: target.asset,
|
|
4858
|
+
chain: target.chain,
|
|
4859
|
+
action: request.action,
|
|
4860
|
+
target
|
|
5020
4861
|
};
|
|
5021
|
-
|
|
5022
|
-
|
|
4862
|
+
const mechanism = resolveSupplyMechanism({
|
|
4863
|
+
asset: instruction.asset,
|
|
4864
|
+
chain: instruction.chain,
|
|
4865
|
+
requestedMechanism: request.mechanism
|
|
4866
|
+
});
|
|
4867
|
+
const defaultSubmitInflowRequest = getDefaultSubmitInflowRequest({
|
|
4868
|
+
action: request.action,
|
|
4869
|
+
chain: mechanism === SupplyPlanType.contractInteraction ? Chain.ETH : instruction.chain
|
|
4870
|
+
});
|
|
4871
|
+
let txid;
|
|
4872
|
+
switch (mechanism) {
|
|
4873
|
+
case SupplyPlanType.transfer:
|
|
4874
|
+
if (request.walletAdapter) {
|
|
4875
|
+
txid = await this.sendAndSubmitNativeSupplyInflow({
|
|
4876
|
+
request,
|
|
4877
|
+
instruction,
|
|
4878
|
+
defaultSubmitInflowRequest
|
|
4879
|
+
});
|
|
4880
|
+
}
|
|
4881
|
+
break;
|
|
4882
|
+
case SupplyPlanType.contractInteraction:
|
|
4883
|
+
txid = await this.executeContractSupply({
|
|
4884
|
+
request,
|
|
4885
|
+
instruction,
|
|
4886
|
+
defaultSubmitInflowRequest
|
|
4887
|
+
});
|
|
4888
|
+
break;
|
|
4889
|
+
}
|
|
5023
4890
|
return {
|
|
5024
|
-
type:
|
|
5025
|
-
|
|
4891
|
+
type: mechanism,
|
|
4892
|
+
target: instruction.target,
|
|
4893
|
+
txid,
|
|
4894
|
+
status: createLiquidiumStatus({
|
|
4895
|
+
operation: mapSupplyActionToStatusOperation(request.action),
|
|
4896
|
+
state: txid ? "confirming" : "action_required"
|
|
4897
|
+
}),
|
|
4898
|
+
submit: async (submitRequest) => {
|
|
4899
|
+
return await this.submitSupplyFlowInflow({
|
|
4900
|
+
instruction,
|
|
4901
|
+
mechanism,
|
|
4902
|
+
defaultSubmitInflowRequest,
|
|
4903
|
+
submitRequest
|
|
4904
|
+
});
|
|
4905
|
+
}
|
|
5026
4906
|
};
|
|
5027
4907
|
}
|
|
5028
|
-
|
|
5029
|
-
const
|
|
5030
|
-
return {
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
owner: receiver.Icrc.owner,
|
|
5036
|
-
subaccount
|
|
5037
|
-
})
|
|
5038
|
-
};
|
|
5039
|
-
}
|
|
5040
|
-
return {
|
|
5041
|
-
type: "External",
|
|
5042
|
-
account: receiver.External
|
|
5043
|
-
};
|
|
5044
|
-
}
|
|
5045
|
-
function normalizeOptionalSubaccount2(subaccount) {
|
|
5046
|
-
if (!subaccount) {
|
|
5047
|
-
return void 0;
|
|
5048
|
-
}
|
|
5049
|
-
return subaccount instanceof Uint8Array ? subaccount : Uint8Array.from(subaccount);
|
|
5050
|
-
}
|
|
5051
|
-
function normalizeOutflowType(rawOutflowType) {
|
|
5052
|
-
switch (rawOutflowType) {
|
|
5053
|
-
case "Withdraw":
|
|
5054
|
-
return OutflowType.withdraw;
|
|
5055
|
-
case "Borrow":
|
|
5056
|
-
return OutflowType.borrow;
|
|
5057
|
-
case "FeeClaim":
|
|
5058
|
-
return OutflowType.feeClaim;
|
|
5059
|
-
default:
|
|
5060
|
-
throw new LiquidiumError(
|
|
5061
|
-
LiquidiumErrorCode.INTERNAL,
|
|
5062
|
-
`Unsupported outflow type: ${rawOutflowType}`
|
|
5063
|
-
);
|
|
5064
|
-
}
|
|
5065
|
-
}
|
|
5066
|
-
function mapWalletChainToLendingChain(chain) {
|
|
5067
|
-
switch (chain) {
|
|
5068
|
-
case Chain.BTC:
|
|
5069
|
-
return { BTC: null };
|
|
5070
|
-
case Chain.ETH:
|
|
5071
|
-
return { ETH: null };
|
|
5072
|
-
}
|
|
5073
|
-
}
|
|
5074
|
-
|
|
5075
|
-
// src/modules/lending/lending.ts
|
|
5076
|
-
var SUBMIT_INFLOW_MAX_ATTEMPTS = 4;
|
|
5077
|
-
var SUBMIT_INFLOW_INITIAL_RETRY_DELAY_MS = 1500;
|
|
5078
|
-
var SUBMIT_INFLOW_RETRY_BACKOFF_MULTIPLIER = 2;
|
|
5079
|
-
var ETH_APPROVAL_POLL_INTERVAL_MS = 2e3;
|
|
5080
|
-
var ETH_APPROVAL_MAX_POLLS = 30;
|
|
5081
|
-
var LendingModule = class {
|
|
5082
|
-
constructor(canisterContext, apiClient, evmReadClient) {
|
|
5083
|
-
this.canisterContext = canisterContext;
|
|
5084
|
-
this.apiClient = apiClient;
|
|
5085
|
-
this.evmReadClient = evmReadClient;
|
|
4908
|
+
async getEvmSupplyContext(request) {
|
|
4909
|
+
const selectedPool = await this.params.getPoolById(request.poolId);
|
|
4910
|
+
return await this.getEvmSupplyContextForPool({
|
|
4911
|
+
request,
|
|
4912
|
+
asset: selectedPool.asset,
|
|
4913
|
+
chain: selectedPool.chain
|
|
4914
|
+
});
|
|
5086
4915
|
}
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
* Prepares a withdraw action that can be signed and submitted later.
|
|
5092
|
-
*
|
|
5093
|
-
* Use this when you need explicit control over signing and submission.
|
|
5094
|
-
*
|
|
5095
|
-
* @param request - Profile, pool, amount (pool asset base units), outflow address, and signer wallet.
|
|
5096
|
-
* @returns A signable {@link WithdrawAction} with `submit` wired to the canister.
|
|
5097
|
-
*/
|
|
5098
|
-
async prepareWithdraw(request) {
|
|
5099
|
-
const destinationAccount = request.receiverAddress.trim();
|
|
5100
|
-
const signerAccount = normalizeEvmAddress(
|
|
5101
|
-
request.signerWalletAddress.trim()
|
|
4916
|
+
async getEvmSupplyContextForPool(params) {
|
|
4917
|
+
const { request, asset, chain } = params;
|
|
4918
|
+
const evmReadClient = this.requireEvmReadClient(
|
|
4919
|
+
"EVM supply context requires an EVM RPC URL or public client"
|
|
5102
4920
|
);
|
|
5103
|
-
|
|
4921
|
+
const walletAddress = normalizeAndValidateEvmAddress(
|
|
4922
|
+
request.walletAddress,
|
|
4923
|
+
"Invalid EVM wallet address"
|
|
4924
|
+
);
|
|
4925
|
+
if (request.amount <= 0n) {
|
|
5104
4926
|
throw new LiquidiumError(
|
|
5105
4927
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5106
|
-
"
|
|
4928
|
+
"EVM supply context requires a positive amount"
|
|
5107
4929
|
);
|
|
5108
4930
|
}
|
|
5109
|
-
if (!
|
|
4931
|
+
if (!isEthStablecoin(asset, chain)) {
|
|
5110
4932
|
throw new LiquidiumError(
|
|
5111
4933
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5112
|
-
|
|
4934
|
+
`EVM supply context only supports ETH stablecoin pools, received ${asset} on ${chain}`
|
|
5113
4935
|
);
|
|
5114
4936
|
}
|
|
5115
|
-
const
|
|
5116
|
-
|
|
5117
|
-
|
|
4937
|
+
const tokenAddress = getEthStablecoinContractAddress(asset);
|
|
4938
|
+
const spenderAddress = CK_ETH_DEPOSIT_CONTRACT_ADDRESS;
|
|
4939
|
+
const [allowance, balance] = await Promise.all([
|
|
4940
|
+
readErc20Allowance({
|
|
4941
|
+
evmReadClient,
|
|
4942
|
+
tokenAddress,
|
|
4943
|
+
ownerAddress: walletAddress,
|
|
4944
|
+
spenderAddress
|
|
4945
|
+
}),
|
|
4946
|
+
readErc20Balance({
|
|
4947
|
+
evmReadClient,
|
|
4948
|
+
tokenAddress,
|
|
4949
|
+
ownerAddress: walletAddress
|
|
4950
|
+
})
|
|
4951
|
+
]);
|
|
4952
|
+
const approvalStrategy = getApprovalStrategy({
|
|
4953
|
+
allowance,
|
|
4954
|
+
amount: request.amount
|
|
5118
4955
|
});
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
transferMode: TransferMode.native,
|
|
5136
|
-
account: signerAccount,
|
|
5137
|
-
message: createWithdrawAssetMessage(
|
|
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);
|
|
5156
|
-
}
|
|
5157
|
-
}
|
|
5158
|
-
async submitWithdraw(request, signatureInfo) {
|
|
5159
|
-
try {
|
|
5160
|
-
const result = await createLendingActor(this.canisterContext).withdraw(
|
|
5161
|
-
principal.Principal.fromText(request.profileId),
|
|
5162
|
-
{
|
|
5163
|
-
data: {
|
|
5164
|
-
expiry_timestamp: request.expiryTimestamp,
|
|
5165
|
-
account: { External: request.receiverAddress },
|
|
5166
|
-
pool_id: principal.Principal.fromText(request.poolId),
|
|
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
|
-
}
|
|
5180
|
-
);
|
|
5181
|
-
if ("Err" in result) {
|
|
5182
|
-
throw mapLendingProtocolErrorToLiquidiumError(result.Err);
|
|
5183
|
-
}
|
|
5184
|
-
return mapExpectedOutflowDetails(
|
|
5185
|
-
mapCanisterOutflowDetails(result.Ok),
|
|
5186
|
-
OutflowType.withdraw,
|
|
5187
|
-
"withdraw"
|
|
5188
|
-
);
|
|
5189
|
-
} catch (error) {
|
|
5190
|
-
if (error instanceof LiquidiumError) {
|
|
5191
|
-
throw error;
|
|
5192
|
-
}
|
|
5193
|
-
throw mapCanisterCallErrorToLiquidiumError("withdraw", error);
|
|
5194
|
-
}
|
|
5195
|
-
}
|
|
5196
|
-
/**
|
|
5197
|
-
* Creates a withdraw outflow using the provided wallet adapter.
|
|
5198
|
-
*
|
|
5199
|
-
* This is the convenience form of `prepareWithdraw(...)` plus execution.
|
|
5200
|
-
*
|
|
5201
|
-
* @param params - Withdraw request fields plus `signerChain` and `signerWalletAdapter`.
|
|
5202
|
-
* @returns The canister {@link OutflowDetails} for the completed withdraw.
|
|
5203
|
-
*/
|
|
5204
|
-
async withdraw(params) {
|
|
5205
|
-
const action = await this.prepareWithdraw(params);
|
|
5206
|
-
return await executeWith({
|
|
5207
|
-
walletAdapter: params.signerWalletAdapter,
|
|
5208
|
-
chain: params.signerChain,
|
|
5209
|
-
account: action.account
|
|
5210
|
-
})(action);
|
|
4956
|
+
return {
|
|
4957
|
+
profileId: request.profileId,
|
|
4958
|
+
poolId: request.poolId,
|
|
4959
|
+
walletAddress,
|
|
4960
|
+
action: request.action,
|
|
4961
|
+
asset,
|
|
4962
|
+
chain: Chain.ETH,
|
|
4963
|
+
amount: request.amount.toString(),
|
|
4964
|
+
tokenAddress,
|
|
4965
|
+
spenderAddress,
|
|
4966
|
+
depositContractAddress: CK_ETH_DEPOSIT_CONTRACT_ADDRESS,
|
|
4967
|
+
balance: balance.toString(),
|
|
4968
|
+
allowance: allowance.toString(),
|
|
4969
|
+
requiresApproval: approvalStrategy !== EvmSupplyApprovalStrategy.none,
|
|
4970
|
+
approvalStrategy
|
|
4971
|
+
};
|
|
5211
4972
|
}
|
|
5212
|
-
|
|
5213
|
-
|
|
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()
|
|
5224
|
-
);
|
|
5225
|
-
if (!destinationAccount) {
|
|
4973
|
+
async sendAndSubmitNativeSupplyInflow(params) {
|
|
4974
|
+
const { request, instruction, defaultSubmitInflowRequest } = params;
|
|
4975
|
+
if (instruction.target.type !== "nativeAddress") {
|
|
5226
4976
|
throw new LiquidiumError(
|
|
5227
4977
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5228
|
-
"
|
|
4978
|
+
"Wallet-executed supply requires a native-address target"
|
|
5229
4979
|
);
|
|
5230
4980
|
}
|
|
5231
|
-
if (!
|
|
4981
|
+
if (!request.walletAdapter) {
|
|
5232
4982
|
throw new LiquidiumError(
|
|
5233
4983
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5234
|
-
"
|
|
4984
|
+
"Wallet-executed supply requires a wallet adapter"
|
|
5235
4985
|
);
|
|
5236
4986
|
}
|
|
5237
|
-
|
|
4987
|
+
const account = request.account?.trim();
|
|
4988
|
+
if (!account) {
|
|
5238
4989
|
throw new LiquidiumError(
|
|
5239
4990
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5240
|
-
"
|
|
4991
|
+
"Wallet-executed transfer supply requires an account"
|
|
5241
4992
|
);
|
|
5242
4993
|
}
|
|
5243
|
-
|
|
5244
|
-
const selectedAsset = selectedPool.asset;
|
|
5245
|
-
const minimumBorrowAmountError = getBorrowAmountMinimumValidationError({
|
|
5246
|
-
amount: request.amount,
|
|
5247
|
-
asset: selectedAsset
|
|
5248
|
-
});
|
|
5249
|
-
if (minimumBorrowAmountError) {
|
|
4994
|
+
if (!request.amount || request.amount <= 0n) {
|
|
5250
4995
|
throw new LiquidiumError(
|
|
5251
4996
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5252
|
-
|
|
4997
|
+
"Wallet-executed supply requires a positive amount"
|
|
5253
4998
|
);
|
|
5254
4999
|
}
|
|
5255
|
-
const
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5000
|
+
const txid = await this.sendNativeSupplyTransaction({
|
|
5001
|
+
walletAdapter: request.walletAdapter,
|
|
5002
|
+
chain: instruction.chain,
|
|
5003
|
+
toAddress: instruction.target.address,
|
|
5004
|
+
amount: request.amount,
|
|
5005
|
+
senderAccount: account,
|
|
5006
|
+
asset: instruction.asset,
|
|
5007
|
+
action: request.action
|
|
5259
5008
|
});
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
expiryTimestamp
|
|
5271
|
-
};
|
|
5009
|
+
if (shouldSubmitInflow({ instruction, mechanism: SupplyPlanType.transfer })) {
|
|
5010
|
+
try {
|
|
5011
|
+
await this.submitInflowWithRetry(txid, defaultSubmitInflowRequest);
|
|
5012
|
+
} catch {
|
|
5013
|
+
}
|
|
5014
|
+
}
|
|
5015
|
+
return txid;
|
|
5016
|
+
}
|
|
5017
|
+
async submitSupplyFlowInflow(params) {
|
|
5018
|
+
if (!shouldSubmitInflow(params)) {
|
|
5272
5019
|
return {
|
|
5273
|
-
|
|
5274
|
-
executionKind: WalletExecutionKind.signMessage,
|
|
5275
|
-
actionType: WalletActionKind.createBorrow,
|
|
5276
|
-
transferMode: TransferMode.native,
|
|
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);
|
|
5290
|
-
}
|
|
5020
|
+
txid: params.submitRequest.txid
|
|
5291
5021
|
};
|
|
5292
|
-
} catch (error) {
|
|
5293
|
-
if (error instanceof LiquidiumError) {
|
|
5294
|
-
throw error;
|
|
5295
|
-
}
|
|
5296
|
-
throw mapCanisterCallErrorToLiquidiumError("get_nonce", error);
|
|
5297
5022
|
}
|
|
5023
|
+
const defaultSubmitInflowRequest = params.defaultSubmitInflowRequest;
|
|
5024
|
+
if (!defaultSubmitInflowRequest) {
|
|
5025
|
+
throw new LiquidiumError(
|
|
5026
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5027
|
+
"Supply flow submit requires an inflow operation"
|
|
5028
|
+
);
|
|
5029
|
+
}
|
|
5030
|
+
return await this.params.submitInflow({
|
|
5031
|
+
...defaultSubmitInflowRequest,
|
|
5032
|
+
...params.submitRequest
|
|
5033
|
+
});
|
|
5298
5034
|
}
|
|
5299
|
-
async
|
|
5035
|
+
async executeContractSupply(params) {
|
|
5036
|
+
const { request, instruction, defaultSubmitInflowRequest } = params;
|
|
5037
|
+
if (instruction.target.type !== "icrcAccount" || instruction.chain !== Chain.ETH) {
|
|
5038
|
+
throw new LiquidiumError(
|
|
5039
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5040
|
+
"Contract-interaction supply is only supported for ETH ICRC-account targets"
|
|
5041
|
+
);
|
|
5042
|
+
}
|
|
5043
|
+
const walletAddressInput = request.account?.trim();
|
|
5044
|
+
if (!walletAddressInput) {
|
|
5045
|
+
throw new LiquidiumError(
|
|
5046
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5047
|
+
"Contract-interaction supply requires an account"
|
|
5048
|
+
);
|
|
5049
|
+
}
|
|
5050
|
+
const walletAddress = normalizeAndValidateEvmAddress(
|
|
5051
|
+
walletAddressInput,
|
|
5052
|
+
"Invalid EVM wallet address"
|
|
5053
|
+
);
|
|
5054
|
+
if (!request.amount || request.amount <= 0n) {
|
|
5055
|
+
throw new LiquidiumError(
|
|
5056
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5057
|
+
"Contract-interaction supply requires a positive amount"
|
|
5058
|
+
);
|
|
5059
|
+
}
|
|
5060
|
+
const walletAdapter = request.walletAdapter;
|
|
5061
|
+
if (!walletAdapter?.sendEthTransaction) {
|
|
5062
|
+
throw new LiquidiumError(
|
|
5063
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5064
|
+
"Contract-interaction supply requires an ETH wallet adapter"
|
|
5065
|
+
);
|
|
5066
|
+
}
|
|
5067
|
+
this.params.requireApi();
|
|
5068
|
+
this.requireEvmReadClient(
|
|
5069
|
+
"Contract-interaction supply requires an EVM RPC URL or public client"
|
|
5070
|
+
);
|
|
5071
|
+
const evmSupplyContext = await this.getEvmSupplyContextForPool({
|
|
5072
|
+
request: {
|
|
5073
|
+
profileId: request.profileId,
|
|
5074
|
+
poolId: request.poolId,
|
|
5075
|
+
walletAddress,
|
|
5076
|
+
amount: request.amount,
|
|
5077
|
+
action: request.action
|
|
5078
|
+
},
|
|
5079
|
+
asset: instruction.asset,
|
|
5080
|
+
chain: instruction.chain
|
|
5081
|
+
});
|
|
5082
|
+
const supplyAmount = BigInt(evmSupplyContext.amount);
|
|
5083
|
+
if (BigInt(evmSupplyContext.balance) < supplyAmount) {
|
|
5084
|
+
throw new LiquidiumError(
|
|
5085
|
+
LiquidiumErrorCode.INSUFFICIENT_FUNDS,
|
|
5086
|
+
`Insufficient ${evmSupplyContext.asset} balance for ${request.action}`
|
|
5087
|
+
);
|
|
5088
|
+
}
|
|
5089
|
+
if (evmSupplyContext.approvalStrategy === EvmSupplyApprovalStrategy.resetThenApproveMax) {
|
|
5090
|
+
await this.sendEthContractTransaction(
|
|
5091
|
+
walletAdapter,
|
|
5092
|
+
walletAddress,
|
|
5093
|
+
createApproveTransaction({
|
|
5094
|
+
tokenAddress: evmSupplyContext.tokenAddress,
|
|
5095
|
+
spenderAddress: evmSupplyContext.spenderAddress,
|
|
5096
|
+
amount: 0n
|
|
5097
|
+
}),
|
|
5098
|
+
`supply-${request.action}-approve-reset`
|
|
5099
|
+
);
|
|
5100
|
+
await this.waitForExpectedAllowance({
|
|
5101
|
+
walletAddress,
|
|
5102
|
+
tokenAddress: evmSupplyContext.tokenAddress,
|
|
5103
|
+
spenderAddress: evmSupplyContext.spenderAddress,
|
|
5104
|
+
amount: supplyAmount,
|
|
5105
|
+
expectation: "zero"
|
|
5106
|
+
});
|
|
5107
|
+
}
|
|
5108
|
+
if (evmSupplyContext.approvalStrategy !== EvmSupplyApprovalStrategy.none) {
|
|
5109
|
+
await this.sendEthContractTransaction(
|
|
5110
|
+
walletAdapter,
|
|
5111
|
+
walletAddress,
|
|
5112
|
+
createApproveTransaction({
|
|
5113
|
+
tokenAddress: evmSupplyContext.tokenAddress,
|
|
5114
|
+
spenderAddress: evmSupplyContext.spenderAddress,
|
|
5115
|
+
amount: MAX_UINT256
|
|
5116
|
+
}),
|
|
5117
|
+
`supply-${request.action}-approve-max`
|
|
5118
|
+
);
|
|
5119
|
+
await this.waitForExpectedAllowance({
|
|
5120
|
+
walletAddress,
|
|
5121
|
+
tokenAddress: evmSupplyContext.tokenAddress,
|
|
5122
|
+
spenderAddress: evmSupplyContext.spenderAddress,
|
|
5123
|
+
amount: supplyAmount,
|
|
5124
|
+
expectation: "sufficient"
|
|
5125
|
+
});
|
|
5126
|
+
}
|
|
5127
|
+
const depositTxid = await this.sendEthContractTransaction(
|
|
5128
|
+
walletAdapter,
|
|
5129
|
+
walletAddress,
|
|
5130
|
+
createDepositErc20Transaction({
|
|
5131
|
+
depositContractAddress: evmSupplyContext.depositContractAddress,
|
|
5132
|
+
tokenAddress: evmSupplyContext.tokenAddress,
|
|
5133
|
+
amount: supplyAmount,
|
|
5134
|
+
poolId: request.poolId,
|
|
5135
|
+
profileId: request.profileId,
|
|
5136
|
+
destinationAccount: instruction.target.account,
|
|
5137
|
+
action: request.action
|
|
5138
|
+
}),
|
|
5139
|
+
`supply-${request.action}-deposit-erc20`
|
|
5140
|
+
);
|
|
5300
5141
|
try {
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
signatureInfo.chain
|
|
5315
|
-
),
|
|
5316
|
-
chain: mapWalletChainToLendingChain(signatureInfo.chain),
|
|
5317
|
-
account: request.signerWalletAddress
|
|
5318
|
-
}
|
|
5142
|
+
await this.submitInflowWithRetry(depositTxid, defaultSubmitInflowRequest);
|
|
5143
|
+
} catch {
|
|
5144
|
+
}
|
|
5145
|
+
return depositTxid;
|
|
5146
|
+
}
|
|
5147
|
+
async sendNativeSupplyTransaction(params) {
|
|
5148
|
+
switch (params.chain) {
|
|
5149
|
+
case Chain.BTC: {
|
|
5150
|
+
if (!params.walletAdapter.sendBtcTransaction) {
|
|
5151
|
+
throw new LiquidiumError(
|
|
5152
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5153
|
+
"BTC wallet adapter does not support transaction sending"
|
|
5154
|
+
);
|
|
5319
5155
|
}
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5156
|
+
return await params.walletAdapter.sendBtcTransaction({
|
|
5157
|
+
chain: Chain.BTC,
|
|
5158
|
+
toAddress: params.toAddress,
|
|
5159
|
+
amountSats: params.amount,
|
|
5160
|
+
account: params.senderAccount,
|
|
5161
|
+
actionType: `supply-${params.action}`,
|
|
5162
|
+
transferMode: TransferMode.native
|
|
5163
|
+
});
|
|
5323
5164
|
}
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5165
|
+
case Chain.ETH: {
|
|
5166
|
+
if (!params.walletAdapter.sendEthTransaction) {
|
|
5167
|
+
throw new LiquidiumError(
|
|
5168
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5169
|
+
"ETH wallet adapter does not support transaction sending"
|
|
5170
|
+
);
|
|
5171
|
+
}
|
|
5172
|
+
if (isEthStablecoin(params.asset, params.chain)) {
|
|
5173
|
+
return await params.walletAdapter.sendEthTransaction({
|
|
5174
|
+
chain: Chain.ETH,
|
|
5175
|
+
account: params.senderAccount,
|
|
5176
|
+
actionType: `supply-${params.action}`,
|
|
5177
|
+
transferMode: TransferMode.native,
|
|
5178
|
+
transaction: createTransferErc20Transaction({
|
|
5179
|
+
tokenAddress: getEthStablecoinContractAddress(params.asset),
|
|
5180
|
+
recipientAddress: params.toAddress,
|
|
5181
|
+
amount: params.amount
|
|
5182
|
+
})
|
|
5183
|
+
});
|
|
5184
|
+
}
|
|
5185
|
+
return await params.walletAdapter.sendEthTransaction({
|
|
5186
|
+
chain: Chain.ETH,
|
|
5187
|
+
account: params.senderAccount,
|
|
5188
|
+
actionType: `supply-${params.action}`,
|
|
5189
|
+
transferMode: TransferMode.native,
|
|
5190
|
+
transaction: {
|
|
5191
|
+
to: params.toAddress,
|
|
5192
|
+
value: params.amount.toString()
|
|
5193
|
+
}
|
|
5194
|
+
});
|
|
5332
5195
|
}
|
|
5333
|
-
|
|
5196
|
+
default:
|
|
5197
|
+
throw new LiquidiumError(
|
|
5198
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5199
|
+
`Native-address wallet execution is not supported for ${params.chain}`
|
|
5200
|
+
);
|
|
5334
5201
|
}
|
|
5335
5202
|
}
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
walletAdapter: params.signerWalletAdapter,
|
|
5352
|
-
chain: params.signerChain,
|
|
5353
|
-
account: action.account
|
|
5354
|
-
})(action);
|
|
5355
|
-
}
|
|
5356
|
-
/**
|
|
5357
|
-
* Resolves a supply target for a deposit or repayment and optionally broadcasts it.
|
|
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
|
|
5203
|
+
async submitInflowWithRetry(txid, extraRequest) {
|
|
5204
|
+
if (!extraRequest) {
|
|
5205
|
+
throw new LiquidiumError(
|
|
5206
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5207
|
+
"Inflow submission requires an operation"
|
|
5208
|
+
);
|
|
5209
|
+
}
|
|
5210
|
+
await retryWithBackoff({
|
|
5211
|
+
execute: async () => {
|
|
5212
|
+
await this.params.submitInflow({ txid, ...extraRequest });
|
|
5213
|
+
},
|
|
5214
|
+
maxAttempts: SUBMIT_INFLOW_MAX_ATTEMPTS,
|
|
5215
|
+
initialRetryDelayMs: SUBMIT_INFLOW_INITIAL_RETRY_DELAY_MS,
|
|
5216
|
+
backoffMultiplier: SUBMIT_INFLOW_RETRY_BACKOFF_MULTIPLIER,
|
|
5217
|
+
shouldRetryError: isRetriableInflowSubmitError
|
|
5382
5218
|
});
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5219
|
+
}
|
|
5220
|
+
async sendEthContractTransaction(walletAdapter, walletAddress, request, actionType) {
|
|
5221
|
+
if (!walletAdapter.sendEthTransaction) {
|
|
5222
|
+
throw new LiquidiumError(
|
|
5223
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5224
|
+
"ETH wallet adapter does not support transaction sending"
|
|
5225
|
+
);
|
|
5226
|
+
}
|
|
5227
|
+
return await walletAdapter.sendEthTransaction({
|
|
5228
|
+
chain: Chain.ETH,
|
|
5229
|
+
account: walletAddress,
|
|
5230
|
+
actionType,
|
|
5231
|
+
transferMode: TransferMode.native,
|
|
5232
|
+
transaction: request
|
|
5386
5233
|
});
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
request,
|
|
5401
|
-
instruction,
|
|
5402
|
-
defaultSubmitInflowRequest
|
|
5234
|
+
}
|
|
5235
|
+
async waitForExpectedAllowance(params) {
|
|
5236
|
+
const evmReadClient = this.requireEvmReadClient(
|
|
5237
|
+
"Contract-interaction supply requires an EVM RPC URL or public client"
|
|
5238
|
+
);
|
|
5239
|
+
let lastPollingError;
|
|
5240
|
+
for (let pollIndex = 0; pollIndex < ETH_APPROVAL_MAX_POLLS; pollIndex += 1) {
|
|
5241
|
+
try {
|
|
5242
|
+
const allowance = await readErc20Allowance({
|
|
5243
|
+
evmReadClient,
|
|
5244
|
+
tokenAddress: params.tokenAddress,
|
|
5245
|
+
ownerAddress: params.walletAddress,
|
|
5246
|
+
spenderAddress: params.spenderAddress
|
|
5403
5247
|
});
|
|
5404
|
-
|
|
5248
|
+
const matchesExpectation = params.expectation === "zero" ? allowance === 0n : allowance >= params.amount;
|
|
5249
|
+
if (matchesExpectation) {
|
|
5250
|
+
return;
|
|
5251
|
+
}
|
|
5252
|
+
} catch (error) {
|
|
5253
|
+
lastPollingError = error;
|
|
5254
|
+
}
|
|
5255
|
+
await delay(ETH_APPROVAL_POLL_INTERVAL_MS);
|
|
5405
5256
|
}
|
|
5257
|
+
const lastPollingErrorMessage = getUnknownErrorMessage(lastPollingError);
|
|
5258
|
+
throw new LiquidiumError(
|
|
5259
|
+
LiquidiumErrorCode.SERVICE_UNAVAILABLE,
|
|
5260
|
+
lastPollingErrorMessage ? `Timed out waiting for ${params.expectation} ETH allowance update. Last polling error: ${lastPollingErrorMessage}` : `Timed out waiting for ${params.expectation} ETH allowance update`
|
|
5261
|
+
);
|
|
5262
|
+
}
|
|
5263
|
+
requireEvmReadClient(message) {
|
|
5264
|
+
if (!this.params.evmReadClient) {
|
|
5265
|
+
throw new LiquidiumError(LiquidiumErrorCode.VALIDATION_ERROR, message);
|
|
5266
|
+
}
|
|
5267
|
+
return this.params.evmReadClient;
|
|
5268
|
+
}
|
|
5269
|
+
};
|
|
5270
|
+
async function readErc20Allowance(params) {
|
|
5271
|
+
const allowance = await params.evmReadClient.readContract({
|
|
5272
|
+
address: params.tokenAddress,
|
|
5273
|
+
abi: ERC20_ABI,
|
|
5274
|
+
functionName: "allowance",
|
|
5275
|
+
args: [
|
|
5276
|
+
params.ownerAddress,
|
|
5277
|
+
params.spenderAddress
|
|
5278
|
+
]
|
|
5279
|
+
});
|
|
5280
|
+
return BigInt(allowance);
|
|
5281
|
+
}
|
|
5282
|
+
async function readErc20Balance(params) {
|
|
5283
|
+
const balance = await params.evmReadClient.readContract({
|
|
5284
|
+
address: params.tokenAddress,
|
|
5285
|
+
abi: ERC20_ABI,
|
|
5286
|
+
functionName: "balanceOf",
|
|
5287
|
+
args: [params.ownerAddress]
|
|
5288
|
+
});
|
|
5289
|
+
return BigInt(balance);
|
|
5290
|
+
}
|
|
5291
|
+
function getApprovalStrategy(params) {
|
|
5292
|
+
if (params.allowance >= params.amount) {
|
|
5293
|
+
return EvmSupplyApprovalStrategy.none;
|
|
5294
|
+
}
|
|
5295
|
+
if (params.allowance === 0n) {
|
|
5296
|
+
return EvmSupplyApprovalStrategy.approveMax;
|
|
5297
|
+
}
|
|
5298
|
+
return EvmSupplyApprovalStrategy.resetThenApproveMax;
|
|
5299
|
+
}
|
|
5300
|
+
async function delay(timeoutMs) {
|
|
5301
|
+
await new Promise((resolve) => setTimeout(resolve, timeoutMs));
|
|
5302
|
+
}
|
|
5303
|
+
function isRetriableInflowSubmitError(error) {
|
|
5304
|
+
if (!(error instanceof LiquidiumError)) {
|
|
5305
|
+
return false;
|
|
5306
|
+
}
|
|
5307
|
+
return error.code === LiquidiumErrorCode.SERVICE_UNAVAILABLE;
|
|
5308
|
+
}
|
|
5309
|
+
function getUnknownErrorMessage(error) {
|
|
5310
|
+
if (error instanceof Error) {
|
|
5311
|
+
return error.message;
|
|
5312
|
+
}
|
|
5313
|
+
if (typeof error === "string") {
|
|
5314
|
+
return error;
|
|
5315
|
+
}
|
|
5316
|
+
return null;
|
|
5317
|
+
}
|
|
5318
|
+
function getDefaultSubmitInflowRequest(params) {
|
|
5319
|
+
if (params.chain !== Chain.BTC && params.chain !== Chain.ETH) {
|
|
5320
|
+
return void 0;
|
|
5321
|
+
}
|
|
5322
|
+
return {
|
|
5323
|
+
chain: params.chain,
|
|
5324
|
+
operation: mapSupplyActionToStatusOperation(params.action)
|
|
5325
|
+
};
|
|
5326
|
+
}
|
|
5327
|
+
function mapSupplyActionToStatusOperation(action) {
|
|
5328
|
+
return action === SupplyAction.repayment ? "repayment" : "deposit";
|
|
5329
|
+
}
|
|
5330
|
+
function shouldSubmitInflow(params) {
|
|
5331
|
+
if (params.mechanism !== SupplyPlanType.transfer) {
|
|
5332
|
+
return true;
|
|
5333
|
+
}
|
|
5334
|
+
return !isEthStablecoin(params.instruction.asset, params.instruction.chain);
|
|
5335
|
+
}
|
|
5336
|
+
function mapCanisterOutflowDetails(outflow) {
|
|
5337
|
+
const rawOutflowType = getVariantKey(outflow.outflow_type);
|
|
5338
|
+
return {
|
|
5339
|
+
id: outflow.id,
|
|
5340
|
+
outflowType: normalizeOutflowType(rawOutflowType),
|
|
5341
|
+
outflowRef: outflow.outflow_ref[0],
|
|
5342
|
+
txid: outflow.txid[0],
|
|
5343
|
+
amount: outflow.amount,
|
|
5344
|
+
receiver: mapCanisterAccountType(outflow.receiver)
|
|
5345
|
+
};
|
|
5346
|
+
}
|
|
5347
|
+
function mapCanisterAccountType(receiver) {
|
|
5348
|
+
if ("Native" in receiver) {
|
|
5406
5349
|
return {
|
|
5407
|
-
type:
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
submitRequest
|
|
5416
|
-
});
|
|
5417
|
-
}
|
|
5350
|
+
type: "Native",
|
|
5351
|
+
account: receiver.Native.toText()
|
|
5352
|
+
};
|
|
5353
|
+
}
|
|
5354
|
+
if ("AccountIdentifier" in receiver) {
|
|
5355
|
+
return {
|
|
5356
|
+
type: "AccountIdentifier",
|
|
5357
|
+
account: receiver.AccountIdentifier
|
|
5418
5358
|
};
|
|
5419
5359
|
}
|
|
5360
|
+
if ("Icrc" in receiver) {
|
|
5361
|
+
const subaccount = normalizeOptionalSubaccount2(receiver.Icrc.subaccount[0]);
|
|
5362
|
+
return {
|
|
5363
|
+
type: "Icrc",
|
|
5364
|
+
owner: receiver.Icrc.owner.toText(),
|
|
5365
|
+
subaccount,
|
|
5366
|
+
account: icrc.encodeIcrcAccount({
|
|
5367
|
+
owner: receiver.Icrc.owner,
|
|
5368
|
+
subaccount
|
|
5369
|
+
})
|
|
5370
|
+
};
|
|
5371
|
+
}
|
|
5372
|
+
return {
|
|
5373
|
+
type: "External",
|
|
5374
|
+
account: receiver.External
|
|
5375
|
+
};
|
|
5376
|
+
}
|
|
5377
|
+
function normalizeOptionalSubaccount2(subaccount) {
|
|
5378
|
+
if (!subaccount) {
|
|
5379
|
+
return void 0;
|
|
5380
|
+
}
|
|
5381
|
+
return subaccount instanceof Uint8Array ? subaccount : Uint8Array.from(subaccount);
|
|
5382
|
+
}
|
|
5383
|
+
function normalizeOutflowType(rawOutflowType) {
|
|
5384
|
+
switch (rawOutflowType) {
|
|
5385
|
+
case "Withdraw":
|
|
5386
|
+
return OutflowType.withdrawal;
|
|
5387
|
+
case "Borrow":
|
|
5388
|
+
return OutflowType.borrow;
|
|
5389
|
+
case "FeeClaim":
|
|
5390
|
+
return OutflowType.feeClaim;
|
|
5391
|
+
default:
|
|
5392
|
+
throw new LiquidiumError(
|
|
5393
|
+
LiquidiumErrorCode.INTERNAL,
|
|
5394
|
+
`Unsupported outflow type: ${rawOutflowType}`
|
|
5395
|
+
);
|
|
5396
|
+
}
|
|
5397
|
+
}
|
|
5398
|
+
function mapWalletChainToLendingChain(chain) {
|
|
5399
|
+
switch (chain) {
|
|
5400
|
+
case Chain.BTC:
|
|
5401
|
+
return { BTC: null };
|
|
5402
|
+
case Chain.ETH:
|
|
5403
|
+
return { ETH: null };
|
|
5404
|
+
}
|
|
5405
|
+
}
|
|
5406
|
+
|
|
5407
|
+
// src/modules/lending/lending.ts
|
|
5408
|
+
var LendingModule = class {
|
|
5409
|
+
constructor(canisterContext, apiClient, evmReadClient) {
|
|
5410
|
+
this.canisterContext = canisterContext;
|
|
5411
|
+
this.apiClient = apiClient;
|
|
5412
|
+
this.evmReadClient = evmReadClient;
|
|
5413
|
+
}
|
|
5414
|
+
canisterContext;
|
|
5415
|
+
apiClient;
|
|
5416
|
+
evmReadClient;
|
|
5420
5417
|
/**
|
|
5421
|
-
*
|
|
5418
|
+
* Prepares a withdraw action that can be signed and submitted later.
|
|
5422
5419
|
*
|
|
5423
|
-
*
|
|
5424
|
-
* contract-interaction `supply`.
|
|
5420
|
+
* Use this when you need explicit control over signing and submission.
|
|
5425
5421
|
*
|
|
5426
|
-
* @param request - Profile, pool,
|
|
5427
|
-
* @returns
|
|
5422
|
+
* @param request - Profile, pool, amount (pool asset base units), outflow address, and signer wallet.
|
|
5423
|
+
* @returns A signable {@link WithdrawAction} with `submit` wired to the canister.
|
|
5428
5424
|
*/
|
|
5429
|
-
async
|
|
5430
|
-
const
|
|
5431
|
-
|
|
5432
|
-
request
|
|
5433
|
-
asset: selectedPool.asset,
|
|
5434
|
-
chain: selectedPool.chain
|
|
5435
|
-
});
|
|
5436
|
-
}
|
|
5437
|
-
async getEvmSupplyContextForPool(params) {
|
|
5438
|
-
const { request, asset, chain } = params;
|
|
5439
|
-
const evmReadClient = this.requireEvmReadClient(
|
|
5440
|
-
"EVM supply context requires an EVM RPC URL or public client"
|
|
5441
|
-
);
|
|
5442
|
-
const walletAddress = normalizeAndValidateEvmAddress(
|
|
5443
|
-
request.walletAddress,
|
|
5444
|
-
"Invalid EVM wallet address"
|
|
5425
|
+
async prepareWithdraw(request) {
|
|
5426
|
+
const destinationAccount = request.receiverAddress.trim();
|
|
5427
|
+
const signerAccount = normalizeEvmAddress(
|
|
5428
|
+
request.signerWalletAddress.trim()
|
|
5445
5429
|
);
|
|
5446
|
-
if (
|
|
5430
|
+
if (!destinationAccount) {
|
|
5447
5431
|
throw new LiquidiumError(
|
|
5448
5432
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5449
|
-
"
|
|
5433
|
+
"Withdraw requires a custom outflow account"
|
|
5450
5434
|
);
|
|
5451
5435
|
}
|
|
5452
|
-
if (!
|
|
5436
|
+
if (!signerAccount) {
|
|
5453
5437
|
throw new LiquidiumError(
|
|
5454
5438
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5455
|
-
|
|
5439
|
+
"Withdraw requires a signer account"
|
|
5456
5440
|
);
|
|
5457
5441
|
}
|
|
5458
|
-
const
|
|
5459
|
-
const spenderAddress = CK_ETH_DEPOSIT_CONTRACT_ADDRESS;
|
|
5460
|
-
const [allowance, balance] = await Promise.all([
|
|
5461
|
-
readErc20Allowance({
|
|
5462
|
-
evmReadClient,
|
|
5463
|
-
tokenAddress,
|
|
5464
|
-
ownerAddress: walletAddress,
|
|
5465
|
-
spenderAddress
|
|
5466
|
-
}),
|
|
5467
|
-
readErc20Balance({
|
|
5468
|
-
evmReadClient,
|
|
5469
|
-
tokenAddress,
|
|
5470
|
-
ownerAddress: walletAddress
|
|
5471
|
-
})
|
|
5472
|
-
]);
|
|
5473
|
-
const approvalStrategy = getApprovalStrategy({
|
|
5474
|
-
allowance,
|
|
5475
|
-
amount: request.amount
|
|
5476
|
-
});
|
|
5477
|
-
return {
|
|
5478
|
-
success: true,
|
|
5479
|
-
profileId: request.profileId,
|
|
5442
|
+
const receiverAddress = await this.normalizeOutflowReceiverAddress({
|
|
5480
5443
|
poolId: request.poolId,
|
|
5481
|
-
|
|
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
|
|
5493
|
-
};
|
|
5494
|
-
}
|
|
5495
|
-
/**
|
|
5496
|
-
* Returns the read-only deposit address for an ETH stablecoin inflow target.
|
|
5497
|
-
*
|
|
5498
|
-
* This is a query call that does not create or mutate state. Use it when you
|
|
5499
|
-
* need the deposit address without hitting the authorization-gated update path.
|
|
5500
|
-
*
|
|
5501
|
-
* @param request - Profile, pool, asset, and supply action.
|
|
5502
|
-
* @returns The EVM deposit address for the derived account.
|
|
5503
|
-
*/
|
|
5504
|
-
async getDepositAddress(request) {
|
|
5505
|
-
if (!isEthStablecoin(request.asset, Chain.ETH)) {
|
|
5506
|
-
throw new LiquidiumError(
|
|
5507
|
-
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5508
|
-
"getDepositAddress is only supported for ETH stablecoins"
|
|
5509
|
-
);
|
|
5510
|
-
}
|
|
5511
|
-
const tokenAddress = getEthStablecoinContractAddress(request.asset);
|
|
5512
|
-
const subaccount = encodeInflowSubaccount({
|
|
5513
|
-
action: request.action,
|
|
5514
|
-
principal: principal.Principal.fromText(request.profileId)
|
|
5444
|
+
receiverAddress: destinationAccount
|
|
5515
5445
|
});
|
|
5516
|
-
const
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
}
|
|
5528
|
-
return result.Ok;
|
|
5529
|
-
}
|
|
5530
|
-
/**
|
|
5531
|
-
* Estimates the network/deposit fee for an inflow target.
|
|
5532
|
-
*
|
|
5533
|
-
* ETH stablecoin deposit-address estimates are served by the deposit-address
|
|
5534
|
-
* canister. BTC estimates include the ckBTC minter deposit fee and ledger fee.
|
|
5535
|
-
*
|
|
5536
|
-
* @param request - Asset and chain pair to estimate for.
|
|
5537
|
-
* @returns Total fee estimate rounded up in the asset's base units.
|
|
5538
|
-
*/
|
|
5539
|
-
async estimateInflowFee(request) {
|
|
5540
|
-
if (isEthStablecoin(request.asset, request.chain)) {
|
|
5541
|
-
const result = await createDepositAccountsActor(
|
|
5542
|
-
this.canisterContext
|
|
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)
|
|
5446
|
+
const lendingActor = createLendingActor(this.canisterContext);
|
|
5447
|
+
try {
|
|
5448
|
+
const expiryTimestamp = computeExpiryTimestampFromNow();
|
|
5449
|
+
const nonce = await lendingActor.get_nonce(signerAccount);
|
|
5450
|
+
const withdrawRequestData = {
|
|
5451
|
+
profileId: request.profileId,
|
|
5452
|
+
poolId: request.poolId,
|
|
5453
|
+
amount: request.amount,
|
|
5454
|
+
receiverAddress,
|
|
5455
|
+
signerWalletAddress: signerAccount,
|
|
5456
|
+
expiryTimestamp
|
|
5549
5457
|
};
|
|
5550
|
-
}
|
|
5551
|
-
if (request.asset === Asset.BTC && request.chain === Chain.BTC) {
|
|
5552
|
-
const estimate = await this.estimateBtcInflowFee();
|
|
5553
5458
|
return {
|
|
5554
|
-
|
|
5459
|
+
kind: WalletActionKind.createWithdraw,
|
|
5460
|
+
executionKind: WalletExecutionKind.signMessage,
|
|
5461
|
+
actionType: WalletActionKind.createWithdraw,
|
|
5462
|
+
transferMode: TransferMode.native,
|
|
5463
|
+
account: signerAccount,
|
|
5464
|
+
message: createWithdrawAssetMessage(
|
|
5465
|
+
{
|
|
5466
|
+
pool_id: request.poolId,
|
|
5467
|
+
amount: request.amount.toString(),
|
|
5468
|
+
account: { type: "External", data: receiverAddress },
|
|
5469
|
+
expiry_timestamp: expiryTimestamp
|
|
5470
|
+
},
|
|
5471
|
+
nonce
|
|
5472
|
+
),
|
|
5473
|
+
data: withdrawRequestData,
|
|
5474
|
+
submit: async (signatureInfo) => {
|
|
5475
|
+
return await this.submitWithdraw(withdrawRequestData, signatureInfo);
|
|
5476
|
+
}
|
|
5555
5477
|
};
|
|
5478
|
+
} catch (error) {
|
|
5479
|
+
if (error instanceof LiquidiumError) {
|
|
5480
|
+
throw error;
|
|
5481
|
+
}
|
|
5482
|
+
throw mapCanisterCallErrorToLiquidiumError("get_nonce", error);
|
|
5556
5483
|
}
|
|
5557
|
-
throw new LiquidiumError(
|
|
5558
|
-
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5559
|
-
`Inflow fee estimates are not supported for ${request.asset} on ${request.chain}`
|
|
5560
|
-
);
|
|
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
5484
|
}
|
|
5569
|
-
async
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
throw new LiquidiumError(
|
|
5592
|
-
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5593
|
-
"Wallet-executed supply requires a positive amount"
|
|
5485
|
+
async submitWithdraw(request, signatureInfo) {
|
|
5486
|
+
try {
|
|
5487
|
+
const result = await createLendingActor(this.canisterContext).withdraw(
|
|
5488
|
+
principal.Principal.fromText(request.profileId),
|
|
5489
|
+
{
|
|
5490
|
+
data: {
|
|
5491
|
+
expiry_timestamp: request.expiryTimestamp,
|
|
5492
|
+
account: { External: request.receiverAddress },
|
|
5493
|
+
pool_id: principal.Principal.fromText(request.poolId),
|
|
5494
|
+
amount: request.amount
|
|
5495
|
+
},
|
|
5496
|
+
signature_info: {
|
|
5497
|
+
Wallet: {
|
|
5498
|
+
signature: normalizeWalletSignature(
|
|
5499
|
+
signatureInfo.signature,
|
|
5500
|
+
signatureInfo.chain
|
|
5501
|
+
),
|
|
5502
|
+
chain: mapWalletChainToLendingChain(signatureInfo.chain),
|
|
5503
|
+
account: request.signerWalletAddress
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
}
|
|
5594
5507
|
);
|
|
5508
|
+
if ("Err" in result) {
|
|
5509
|
+
throw mapLendingProtocolErrorToLiquidiumError(result.Err);
|
|
5510
|
+
}
|
|
5511
|
+
return mapExpectedOutflowDetails(
|
|
5512
|
+
mapCanisterOutflowDetails(result.Ok),
|
|
5513
|
+
OutflowType.withdrawal,
|
|
5514
|
+
"withdraw"
|
|
5515
|
+
);
|
|
5516
|
+
} catch (error) {
|
|
5517
|
+
if (error instanceof LiquidiumError) {
|
|
5518
|
+
throw error;
|
|
5519
|
+
}
|
|
5520
|
+
throw mapCanisterCallErrorToLiquidiumError("withdraw", error);
|
|
5595
5521
|
}
|
|
5596
|
-
const txid = await this.sendNativeSupplyTransaction({
|
|
5597
|
-
walletAdapter: request.walletAdapter,
|
|
5598
|
-
chain: instruction.chain,
|
|
5599
|
-
toAddress: instruction.target.address,
|
|
5600
|
-
amount: request.amount,
|
|
5601
|
-
senderAccount: account,
|
|
5602
|
-
asset: instruction.asset,
|
|
5603
|
-
action: request.action
|
|
5604
|
-
});
|
|
5605
|
-
if (shouldSubmitInflow({ instruction, mechanism: SupplyPlanType.transfer })) {
|
|
5606
|
-
await this.submitInflowWithRetry(txid, defaultSubmitInflowRequest);
|
|
5607
|
-
}
|
|
5608
|
-
return txid;
|
|
5609
5522
|
}
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5523
|
+
/**
|
|
5524
|
+
* Creates a withdraw outflow using the provided wallet adapter.
|
|
5525
|
+
*
|
|
5526
|
+
* This is the convenience form of `prepareWithdraw(...)` plus execution.
|
|
5527
|
+
*
|
|
5528
|
+
* @param params - Withdraw request fields plus `signerChain` and `signerWalletAdapter`.
|
|
5529
|
+
* @returns The canister {@link OutflowDetails} for the completed withdraw.
|
|
5530
|
+
*/
|
|
5531
|
+
async withdraw(params) {
|
|
5532
|
+
const action = await this.prepareWithdraw(params);
|
|
5533
|
+
return await executeWith({
|
|
5534
|
+
walletAdapter: params.signerWalletAdapter,
|
|
5535
|
+
chain: params.signerChain,
|
|
5536
|
+
account: action.account
|
|
5537
|
+
})(action);
|
|
5618
5538
|
}
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5539
|
+
/**
|
|
5540
|
+
* Prepares a borrow action that can be signed and submitted later.
|
|
5541
|
+
*
|
|
5542
|
+
* Use this when you need explicit control over signing and submission.
|
|
5543
|
+
*
|
|
5544
|
+
* @param request - Profile, pool, amount (borrow asset base units), outflow address, and signer wallet.
|
|
5545
|
+
* @returns A signable {@link BorrowAction} with `submit` wired to the canister.
|
|
5546
|
+
*/
|
|
5547
|
+
async prepareBorrow(request) {
|
|
5548
|
+
const destinationAccount = request.receiverAddress.trim();
|
|
5549
|
+
const signerAccount = normalizeEvmAddress(
|
|
5550
|
+
request.signerWalletAddress.trim()
|
|
5551
|
+
);
|
|
5552
|
+
if (!destinationAccount) {
|
|
5622
5553
|
throw new LiquidiumError(
|
|
5623
5554
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5624
|
-
"
|
|
5555
|
+
"Borrow requires a custom outflow account"
|
|
5625
5556
|
);
|
|
5626
5557
|
}
|
|
5627
|
-
|
|
5628
|
-
if (!walletAddressInput) {
|
|
5558
|
+
if (!signerAccount) {
|
|
5629
5559
|
throw new LiquidiumError(
|
|
5630
5560
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5631
|
-
"
|
|
5561
|
+
"Borrow requires a signer account"
|
|
5632
5562
|
);
|
|
5633
5563
|
}
|
|
5634
|
-
|
|
5635
|
-
walletAddressInput,
|
|
5636
|
-
"Invalid EVM wallet address"
|
|
5637
|
-
);
|
|
5638
|
-
if (!request.amount || request.amount <= 0n) {
|
|
5564
|
+
if (request.amount <= 0n) {
|
|
5639
5565
|
throw new LiquidiumError(
|
|
5640
5566
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5641
|
-
"
|
|
5567
|
+
"Borrow amount must be greater than 0"
|
|
5642
5568
|
);
|
|
5643
5569
|
}
|
|
5644
|
-
const
|
|
5645
|
-
|
|
5570
|
+
const selectedPool = await this.getPoolById(request.poolId);
|
|
5571
|
+
const selectedAsset = selectedPool.asset;
|
|
5572
|
+
const minimumBorrowAmountError = getBorrowAmountMinimumValidationError({
|
|
5573
|
+
amount: request.amount,
|
|
5574
|
+
asset: selectedAsset
|
|
5575
|
+
});
|
|
5576
|
+
if (minimumBorrowAmountError) {
|
|
5646
5577
|
throw new LiquidiumError(
|
|
5647
5578
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5648
|
-
|
|
5579
|
+
minimumBorrowAmountError.message
|
|
5649
5580
|
);
|
|
5650
5581
|
}
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5582
|
+
const receiverAddress = normalizeExternalAddress({
|
|
5583
|
+
address: destinationAccount,
|
|
5584
|
+
asset: selectedAsset,
|
|
5585
|
+
chain: selectedPool.chain
|
|
5586
|
+
});
|
|
5587
|
+
const lendingActor = createLendingActor(this.canisterContext);
|
|
5588
|
+
try {
|
|
5589
|
+
const expiryTimestamp = computeExpiryTimestampFromNow();
|
|
5590
|
+
const nonce = await lendingActor.get_nonce(signerAccount);
|
|
5591
|
+
const borrowRequestData = {
|
|
5657
5592
|
profileId: request.profileId,
|
|
5658
5593
|
poolId: request.poolId,
|
|
5659
|
-
walletAddress,
|
|
5660
5594
|
amount: request.amount,
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5595
|
+
receiverAddress,
|
|
5596
|
+
signerWalletAddress: signerAccount,
|
|
5597
|
+
expiryTimestamp
|
|
5598
|
+
};
|
|
5599
|
+
return {
|
|
5600
|
+
kind: WalletActionKind.createBorrow,
|
|
5601
|
+
executionKind: WalletExecutionKind.signMessage,
|
|
5602
|
+
actionType: WalletActionKind.createBorrow,
|
|
5603
|
+
transferMode: TransferMode.native,
|
|
5604
|
+
account: signerAccount,
|
|
5605
|
+
message: createBorrowAssetMessage(
|
|
5606
|
+
{
|
|
5607
|
+
pool_id: request.poolId,
|
|
5608
|
+
amount: request.amount.toString(),
|
|
5609
|
+
account: { type: "External", data: receiverAddress },
|
|
5610
|
+
expiry_timestamp: expiryTimestamp
|
|
5611
|
+
},
|
|
5612
|
+
nonce
|
|
5613
|
+
),
|
|
5614
|
+
data: borrowRequestData,
|
|
5615
|
+
submit: async (signatureInfo) => {
|
|
5616
|
+
return await this.submitBorrow(borrowRequestData, signatureInfo);
|
|
5617
|
+
}
|
|
5618
|
+
};
|
|
5619
|
+
} catch (error) {
|
|
5620
|
+
if (error instanceof LiquidiumError) {
|
|
5621
|
+
throw error;
|
|
5622
|
+
}
|
|
5623
|
+
throw mapCanisterCallErrorToLiquidiumError("get_nonce", error);
|
|
5672
5624
|
}
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5625
|
+
}
|
|
5626
|
+
async submitBorrow(request, signatureInfo) {
|
|
5627
|
+
try {
|
|
5628
|
+
const result = await createLendingActor(
|
|
5629
|
+
this.canisterContext
|
|
5630
|
+
).borrow_assets(principal.Principal.fromText(request.profileId), {
|
|
5631
|
+
data: {
|
|
5632
|
+
expiry_timestamp: request.expiryTimestamp,
|
|
5633
|
+
account: { External: request.receiverAddress },
|
|
5634
|
+
pool_id: principal.Principal.fromText(request.poolId),
|
|
5635
|
+
amount: request.amount
|
|
5636
|
+
},
|
|
5637
|
+
signature_info: {
|
|
5638
|
+
Wallet: {
|
|
5639
|
+
signature: normalizeWalletSignature(
|
|
5640
|
+
signatureInfo.signature,
|
|
5641
|
+
signatureInfo.chain
|
|
5642
|
+
),
|
|
5643
|
+
chain: mapWalletChainToLendingChain(signatureInfo.chain),
|
|
5644
|
+
account: request.signerWalletAddress
|
|
5645
|
+
}
|
|
5646
|
+
}
|
|
5690
5647
|
});
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5648
|
+
if ("Err" in result) {
|
|
5649
|
+
throw mapLendingProtocolErrorToLiquidiumError(result.Err);
|
|
5650
|
+
}
|
|
5651
|
+
return mapExpectedOutflowDetails(
|
|
5652
|
+
mapCanisterOutflowDetails(result.Ok),
|
|
5653
|
+
OutflowType.borrow,
|
|
5654
|
+
"borrow_assets"
|
|
5655
|
+
);
|
|
5656
|
+
} catch (error) {
|
|
5657
|
+
if (error instanceof LiquidiumError) {
|
|
5658
|
+
throw error;
|
|
5659
|
+
}
|
|
5660
|
+
throw mapCanisterCallErrorToLiquidiumError("borrow_assets", error);
|
|
5661
|
+
}
|
|
5662
|
+
}
|
|
5663
|
+
/**
|
|
5664
|
+
* Creates a borrow outflow using the provided wallet adapter.
|
|
5665
|
+
*
|
|
5666
|
+
* This is the convenience form of `prepareBorrow(...)` plus execution.
|
|
5667
|
+
*
|
|
5668
|
+
* @param params - Borrow request fields plus `signerChain` and `signerWalletAdapter`.
|
|
5669
|
+
* @returns The lending canister receipt as {@link OutflowDetails}.
|
|
5670
|
+
*
|
|
5671
|
+
* @remarks
|
|
5672
|
+
* `id` is always present. `txid` may be missing on the first response; the SDK does not
|
|
5673
|
+
* poll for it. Use history or app-level polling if you need the chain transaction id.
|
|
5674
|
+
*/
|
|
5675
|
+
async borrow(params) {
|
|
5676
|
+
const action = await this.prepareBorrow(params);
|
|
5677
|
+
return await executeWith({
|
|
5678
|
+
walletAdapter: params.signerWalletAdapter,
|
|
5679
|
+
chain: params.signerChain,
|
|
5680
|
+
account: action.account
|
|
5681
|
+
})(action);
|
|
5682
|
+
}
|
|
5683
|
+
/**
|
|
5684
|
+
* Resolves a supply target for a deposit or repayment and optionally broadcasts it.
|
|
5685
|
+
*
|
|
5686
|
+
* Transfer mode can return manual broadcast instructions when wallet fields are
|
|
5687
|
+
* omitted. Contract-interaction mode always requires `walletAdapter`, `account`,
|
|
5688
|
+
* and `amount` because it must prepare and submit approval/deposit calls.
|
|
5689
|
+
*
|
|
5690
|
+
* The SDK does not poll for inflow status. When a `txid` is returned, it is the
|
|
5691
|
+
* caller's responsibility to track confirmation state using their own polling.
|
|
5692
|
+
*
|
|
5693
|
+
* @returns A {@link SupplyFlow} receipt with `type`, `target`, `submit`, and
|
|
5694
|
+
* an optional `txid` present when the SDK broadcast for you.
|
|
5695
|
+
*/
|
|
5696
|
+
async supply(request) {
|
|
5697
|
+
return await this.createSupplyFlowExecutor().create(request);
|
|
5698
|
+
}
|
|
5699
|
+
/**
|
|
5700
|
+
* Fetches ERC-20 supply planning data with the configured EVM read client.
|
|
5701
|
+
*
|
|
5702
|
+
* Requires `evmRpcUrl` or `evmPublicClient` on the client. Used internally by
|
|
5703
|
+
* contract-interaction `supply`.
|
|
5704
|
+
*
|
|
5705
|
+
* @param request - Profile, pool, wallet, amount (token base units), and action.
|
|
5706
|
+
* @returns Locally computed {@link EvmSupplyContext} for approvals and deposit.
|
|
5707
|
+
*/
|
|
5708
|
+
async getEvmSupplyContext(request) {
|
|
5709
|
+
return await this.createSupplyFlowExecutor().getEvmSupplyContext(request);
|
|
5710
|
+
}
|
|
5711
|
+
/**
|
|
5712
|
+
* Returns the read-only deposit address for an ETH stablecoin inflow target.
|
|
5713
|
+
*
|
|
5714
|
+
* This is a query call that does not create or mutate state. Use it when you
|
|
5715
|
+
* need the deposit address without hitting the authorization-gated update path.
|
|
5716
|
+
*
|
|
5717
|
+
* @param request - Profile, pool, asset, and supply action.
|
|
5718
|
+
* @returns The EVM deposit address for the derived account.
|
|
5719
|
+
*/
|
|
5720
|
+
async getDepositAddress(request) {
|
|
5721
|
+
if (!isEthStablecoin(request.asset, Chain.ETH)) {
|
|
5722
|
+
throw new LiquidiumError(
|
|
5723
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5724
|
+
"getDepositAddress is only supported for ETH stablecoins"
|
|
5702
5725
|
);
|
|
5703
|
-
await this.waitForExpectedAllowance({
|
|
5704
|
-
walletAddress,
|
|
5705
|
-
tokenAddress: evmSupplyContext.tokenAddress,
|
|
5706
|
-
spenderAddress: evmSupplyContext.spenderAddress,
|
|
5707
|
-
amount: supplyAmount,
|
|
5708
|
-
expectation: "sufficient"
|
|
5709
|
-
});
|
|
5710
5726
|
}
|
|
5711
|
-
const
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
}
|
|
5723
|
-
|
|
5727
|
+
const tokenAddress = getEthStablecoinContractAddress(request.asset);
|
|
5728
|
+
const subaccount = encodeInflowSubaccount({
|
|
5729
|
+
action: request.action,
|
|
5730
|
+
principal: principal.Principal.fromText(request.profileId)
|
|
5731
|
+
});
|
|
5732
|
+
const result = await createDepositAccountsActor(
|
|
5733
|
+
this.canisterContext
|
|
5734
|
+
).get_deposit_address(
|
|
5735
|
+
{
|
|
5736
|
+
owner: principal.Principal.fromText(request.poolId),
|
|
5737
|
+
subaccount: [subaccount]
|
|
5738
|
+
},
|
|
5739
|
+
[tokenAddress]
|
|
5724
5740
|
);
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
} catch {
|
|
5741
|
+
if ("Err" in result) {
|
|
5742
|
+
throw mapDepositAccountErrorToLiquidiumError(result.Err);
|
|
5728
5743
|
}
|
|
5729
|
-
return
|
|
5744
|
+
return result.Ok;
|
|
5730
5745
|
}
|
|
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
|
-
});
|
|
5746
|
+
/**
|
|
5747
|
+
* Estimates the network/deposit fee for an inflow target.
|
|
5748
|
+
*
|
|
5749
|
+
* ETH stablecoin deposit-address estimates are served by the deposit-address
|
|
5750
|
+
* canister. BTC estimates include the ckBTC minter deposit fee and ledger fee.
|
|
5751
|
+
*
|
|
5752
|
+
* @param request - Asset and chain pair to estimate for.
|
|
5753
|
+
* @returns Total fee estimate rounded up in the asset's base units.
|
|
5754
|
+
*/
|
|
5755
|
+
async estimateInflowFee(request) {
|
|
5756
|
+
if (isEthStablecoin(request.asset, request.chain)) {
|
|
5757
|
+
const result = await createDepositAccountsActor(
|
|
5758
|
+
this.canisterContext
|
|
5759
|
+
).estimate_deposit_fee([getEthStablecoinContractAddress(request.asset)]);
|
|
5760
|
+
if ("Err" in result) {
|
|
5761
|
+
throw mapDepositAccountErrorToLiquidiumError(result.Err);
|
|
5779
5762
|
}
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5763
|
+
return {
|
|
5764
|
+
totalFee: roundInflowFeeEstimate(request, result.Ok)
|
|
5765
|
+
};
|
|
5766
|
+
}
|
|
5767
|
+
if (request.asset === Asset.BTC && request.chain === Chain.BTC) {
|
|
5768
|
+
const estimate = await this.estimateBtcInflowFee();
|
|
5769
|
+
return {
|
|
5770
|
+
totalFee: roundInflowFeeEstimate(request, estimate.totalFee)
|
|
5771
|
+
};
|
|
5785
5772
|
}
|
|
5773
|
+
throw new LiquidiumError(
|
|
5774
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5775
|
+
`Inflow fee estimates are not supported for ${request.asset} on ${request.chain}`
|
|
5776
|
+
);
|
|
5786
5777
|
}
|
|
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
|
-
});
|
|
5778
|
+
async estimateBtcInflowFee() {
|
|
5779
|
+
const [minterFee, ledgerFee] = await Promise.all([
|
|
5780
|
+
createCkBtcMinterActor(this.canisterContext).get_deposit_fee(),
|
|
5781
|
+
createCkBtcLedgerActor(this.canisterContext).icrc1_fee()
|
|
5782
|
+
]);
|
|
5783
|
+
return { totalFee: minterFee + ledgerFee };
|
|
5797
5784
|
}
|
|
5798
5785
|
/**
|
|
5799
5786
|
* Submits an inflow transaction id for faster indexing.
|
|
5800
5787
|
*
|
|
5801
5788
|
* Uses the Liquidium SDK API.
|
|
5802
5789
|
*
|
|
5803
|
-
* @param request - Broadcast `txid` plus
|
|
5790
|
+
* @param request - Broadcast `txid` plus inflow `operation` and optional `chain`.
|
|
5804
5791
|
* @returns Acknowledgement including the submitted `txid`.
|
|
5805
5792
|
*/
|
|
5806
5793
|
async submitInflow(request) {
|
|
5807
5794
|
const apiClient = this.requireApi();
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5795
|
+
const response = await apiClient.post(SdkApiPath.inflow, request);
|
|
5796
|
+
return {
|
|
5797
|
+
txid: response.txid
|
|
5798
|
+
};
|
|
5812
5799
|
}
|
|
5813
5800
|
/**
|
|
5814
5801
|
* Returns whether borrowing is currently disabled by the protocol.
|
|
@@ -5839,11 +5826,16 @@ var LendingModule = class {
|
|
|
5839
5826
|
}
|
|
5840
5827
|
return this.apiClient;
|
|
5841
5828
|
}
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5829
|
+
createSupplyFlowExecutor() {
|
|
5830
|
+
return new SupplyFlowExecutor({
|
|
5831
|
+
canisterContext: this.canisterContext,
|
|
5832
|
+
evmReadClient: this.evmReadClient,
|
|
5833
|
+
getPoolById: async (poolId) => await this.getPoolById(poolId),
|
|
5834
|
+
requireApi: () => {
|
|
5835
|
+
this.requireApi();
|
|
5836
|
+
},
|
|
5837
|
+
submitInflow: async (request) => await this.submitInflow(request)
|
|
5838
|
+
});
|
|
5847
5839
|
}
|
|
5848
5840
|
async getPoolById(poolId) {
|
|
5849
5841
|
const pools = await createFlexibleLendingActor(
|
|
@@ -5869,80 +5861,7 @@ var LendingModule = class {
|
|
|
5869
5861
|
chain: selectedPool.chain
|
|
5870
5862
|
});
|
|
5871
5863
|
}
|
|
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
5864
|
};
|
|
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
5865
|
function mapExpectedOutflowDetails(details, expectedOutflowType, operation) {
|
|
5947
5866
|
if (details.outflowType !== expectedOutflowType) {
|
|
5948
5867
|
throw new LiquidiumError(
|
|
@@ -5950,42 +5869,22 @@ function mapExpectedOutflowDetails(details, expectedOutflowType, operation) {
|
|
|
5950
5869
|
`${operation} returned unexpected outflow type ${details.outflowType}`
|
|
5951
5870
|
);
|
|
5952
5871
|
}
|
|
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
5872
|
return {
|
|
5978
|
-
|
|
5979
|
-
|
|
5873
|
+
...details,
|
|
5874
|
+
status: createLiquidiumStatus({
|
|
5875
|
+
operation: mapOutflowTypeToStatusOperation(expectedOutflowType),
|
|
5876
|
+
state: details.txid ? "confirming" : "processing"
|
|
5877
|
+
})
|
|
5980
5878
|
};
|
|
5981
5879
|
}
|
|
5982
|
-
function
|
|
5983
|
-
|
|
5984
|
-
return true;
|
|
5985
|
-
}
|
|
5986
|
-
return !isEthStablecoin(params.instruction.asset, params.instruction.chain);
|
|
5880
|
+
function mapOutflowTypeToStatusOperation(outflowType) {
|
|
5881
|
+
return outflowType;
|
|
5987
5882
|
}
|
|
5988
5883
|
|
|
5884
|
+
// src/core/rates.ts
|
|
5885
|
+
var RATE_SCALE2 = 1000000000000000000000000000n;
|
|
5886
|
+
var RATE_DECIMALS = BigInt(RATE_SCALE2.toString().length - 1);
|
|
5887
|
+
|
|
5989
5888
|
// src/modules/market/mappers.ts
|
|
5990
5889
|
var DECIMAL_BASE = 10;
|
|
5991
5890
|
var PAIR_SEPARATOR = "_";
|
|
@@ -6064,16 +5963,18 @@ var MarketModule = class {
|
|
|
6064
5963
|
canisterContext;
|
|
6065
5964
|
apiClient;
|
|
6066
5965
|
/**
|
|
6067
|
-
* Lists
|
|
5966
|
+
* Lists SDK-supported pools with their current rates.
|
|
6068
5967
|
*
|
|
6069
|
-
*
|
|
5968
|
+
* Unsupported asset or chain variants returned by the canister are omitted.
|
|
5969
|
+
*
|
|
5970
|
+
* @returns Supported lending pools enriched with their current rate data.
|
|
6070
5971
|
*/
|
|
6071
5972
|
async listPools() {
|
|
6072
5973
|
void this.apiClient;
|
|
6073
5974
|
try {
|
|
6074
5975
|
const flexibleActor = createFlexibleLendingActor(this.canisterContext);
|
|
6075
5976
|
const rawPools = await flexibleActor.list_pools();
|
|
6076
|
-
const decodedPools = rawPools
|
|
5977
|
+
const decodedPools = decodeSupportedFlexiblePools(rawPools);
|
|
6077
5978
|
return await Promise.all(
|
|
6078
5979
|
decodedPools.map(async (pool) => {
|
|
6079
5980
|
const poolRate = await flexibleActor.get_pool_rate(pool.principal);
|
|
@@ -6180,6 +6081,17 @@ var MarketModule = class {
|
|
|
6180
6081
|
}
|
|
6181
6082
|
}
|
|
6182
6083
|
};
|
|
6084
|
+
function decodeSupportedFlexiblePools(rawPools) {
|
|
6085
|
+
const decodedPools = [];
|
|
6086
|
+
for (const rawPool of rawPools) {
|
|
6087
|
+
const decodedPool = decodeFlexiblePool(rawPool);
|
|
6088
|
+
if (!decodedPool) {
|
|
6089
|
+
continue;
|
|
6090
|
+
}
|
|
6091
|
+
decodedPools.push(decodedPool);
|
|
6092
|
+
}
|
|
6093
|
+
return decodedPools;
|
|
6094
|
+
}
|
|
6183
6095
|
|
|
6184
6096
|
// src/modules/positions/mappers.ts
|
|
6185
6097
|
var USD_VALUE_SCALE_DECIMALS = 27n;
|
|
@@ -6510,6 +6422,7 @@ var LiquidiumClient = class {
|
|
|
6510
6422
|
this.instantLoans = new InstantLoansModule(
|
|
6511
6423
|
this.canisterContext,
|
|
6512
6424
|
this.apiClient,
|
|
6425
|
+
this.activities,
|
|
6513
6426
|
this.lending,
|
|
6514
6427
|
this.positions
|
|
6515
6428
|
);
|
|
@@ -6533,18 +6446,13 @@ function resolveEvmReadClient(config) {
|
|
|
6533
6446
|
|
|
6534
6447
|
exports.AccountsModule = AccountsModule;
|
|
6535
6448
|
exports.ActivitiesModule = ActivitiesModule;
|
|
6536
|
-
exports.ActivityDirection = ActivityDirection;
|
|
6537
6449
|
exports.ActivityFilter = ActivityFilter;
|
|
6538
|
-
exports.ActivityKind = ActivityKind;
|
|
6539
|
-
exports.ActivityStatus = ActivityStatus;
|
|
6540
6450
|
exports.Asset = Asset;
|
|
6541
6451
|
exports.CK_ETH_DEPOSIT_CONTRACT_ADDRESS = CK_ETH_DEPOSIT_CONTRACT_ADDRESS;
|
|
6542
6452
|
exports.Chain = Chain;
|
|
6543
6453
|
exports.Environment = Environment;
|
|
6544
6454
|
exports.EvmSupplyApprovalStrategy = EvmSupplyApprovalStrategy;
|
|
6545
6455
|
exports.HistoryModule = HistoryModule;
|
|
6546
|
-
exports.InflowSubmitType = InflowSubmitType;
|
|
6547
|
-
exports.InstantLoanStatus = InstantLoanStatus;
|
|
6548
6456
|
exports.InstantLoansModule = InstantLoansModule;
|
|
6549
6457
|
exports.LendingModule = LendingModule;
|
|
6550
6458
|
exports.LiquidiumClient = LiquidiumClient;
|
|
@@ -6558,13 +6466,13 @@ exports.QuoteModule = QuoteModule;
|
|
|
6558
6466
|
exports.QuoteValidationErrorCode = QuoteValidationErrorCode;
|
|
6559
6467
|
exports.QuoteWarningCode = QuoteWarningCode;
|
|
6560
6468
|
exports.RATE_DECIMALS = RATE_DECIMALS;
|
|
6561
|
-
exports.RATE_SCALE =
|
|
6469
|
+
exports.RATE_SCALE = RATE_SCALE2;
|
|
6562
6470
|
exports.SupplyAction = SupplyAction;
|
|
6563
6471
|
exports.SupplyPlanType = SupplyPlanType;
|
|
6564
6472
|
exports.TransferMode = TransferMode;
|
|
6565
6473
|
exports.USDC_CONTRACT_ADDRESS = USDC_CONTRACT_ADDRESS;
|
|
6566
6474
|
exports.USDT_CONTRACT_ADDRESS = USDT_CONTRACT_ADDRESS;
|
|
6567
|
-
exports.
|
|
6475
|
+
exports.WalletActionKind = WalletActionKind;
|
|
6568
6476
|
exports.WalletExecutionKind = WalletExecutionKind;
|
|
6569
6477
|
exports.createTransferErc20Transaction = createTransferErc20Transaction;
|
|
6570
6478
|
exports.executeWith = executeWith;
|