@liquidium/client 0.4.0 → 0.5.0-rc.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 +87 -51
- package/dist/index.cjs +1136 -455
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +348 -348
- package/dist/index.d.ts +348 -348
- package/dist/index.js +1133 -456
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,60 +4,12 @@ import { Actor, HttpAgent } from '@icp-sdk/core/agent';
|
|
|
4
4
|
import { Principal } from '@icp-sdk/core/principal';
|
|
5
5
|
import { base64, base64nopad } from '@scure/base';
|
|
6
6
|
import { idlLabelToId } from '@icp-sdk/core/candid';
|
|
7
|
-
import {
|
|
7
|
+
import { AccountIdentifier, SubAccount } from '@icp-sdk/canisters/ledger/icp';
|
|
8
|
+
import { decodeIcrcAccount, encodeIcrcAccount } from '@icp-sdk/canisters/ledger/icrc';
|
|
8
9
|
import { validate, Network } from 'bitcoin-address-validation';
|
|
9
10
|
|
|
10
11
|
// src/client.ts
|
|
11
12
|
|
|
12
|
-
// src/core/types.ts
|
|
13
|
-
var Environment = {
|
|
14
|
-
mainnet: "mainnet"
|
|
15
|
-
};
|
|
16
|
-
var Asset = {
|
|
17
|
-
BTC: "BTC",
|
|
18
|
-
SOL: "SOL",
|
|
19
|
-
USDC: "USDC",
|
|
20
|
-
USDT: "USDT"
|
|
21
|
-
};
|
|
22
|
-
var Chain = {
|
|
23
|
-
BTC: "BTC",
|
|
24
|
-
ETH: "ETH"
|
|
25
|
-
};
|
|
26
|
-
var SupplyAction = {
|
|
27
|
-
deposit: "deposit",
|
|
28
|
-
repayment: "repayment"
|
|
29
|
-
};
|
|
30
|
-
var OutflowType = {
|
|
31
|
-
borrow: "borrow",
|
|
32
|
-
feeClaim: "feeClaim",
|
|
33
|
-
withdrawal: "withdrawal"
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
// src/core/config.ts
|
|
37
|
-
var MAINNET_HOST = "https://icp-api.io";
|
|
38
|
-
var DEFAULT_API_BASE_URL = "https://app.liquidium.fi/api/sdk";
|
|
39
|
-
var DEFAULT_ENVIRONMENT = Environment.mainnet;
|
|
40
|
-
var MAINNET_CANISTER_IDS = {
|
|
41
|
-
lending: "hyk4r-jqaaa-aaaar-qb4ca-cai",
|
|
42
|
-
btcPool: "hkmli-faaaa-aaaar-qb4ba-cai",
|
|
43
|
-
ercPool: "hnnn4-iyaaa-aaaar-qb4bq-cai",
|
|
44
|
-
ethDeposit: "z5jz7-nyaaa-aaaar-qb6pq-cai",
|
|
45
|
-
instantLoans: "u5rm3-niaaa-aaaar-qb7eq-cai"
|
|
46
|
-
};
|
|
47
|
-
var CANISTER_IDS_BY_ENVIRONMENT = {
|
|
48
|
-
mainnet: MAINNET_CANISTER_IDS
|
|
49
|
-
};
|
|
50
|
-
function resolveHost(override) {
|
|
51
|
-
return override ?? MAINNET_HOST;
|
|
52
|
-
}
|
|
53
|
-
function resolveCanisterIds(environment = DEFAULT_ENVIRONMENT, overrides) {
|
|
54
|
-
return { ...CANISTER_IDS_BY_ENVIRONMENT[environment], ...overrides };
|
|
55
|
-
}
|
|
56
|
-
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
57
|
-
var CK_CANISTER_IDS = {
|
|
58
|
-
btcMinter: "mqygn-kiaaa-aaaar-qaadq-cai",
|
|
59
|
-
btcLedger: "mxzaz-hqaaa-aaaar-qaada-cai"};
|
|
60
|
-
|
|
61
13
|
// src/core/errors.ts
|
|
62
14
|
var LiquidiumErrorCode = {
|
|
63
15
|
// Protocol errors
|
|
@@ -128,6 +80,127 @@ var LiquidiumError = class extends Error {
|
|
|
128
80
|
}
|
|
129
81
|
};
|
|
130
82
|
|
|
83
|
+
// src/core/types.ts
|
|
84
|
+
var Environment = {
|
|
85
|
+
mainnet: "mainnet"
|
|
86
|
+
};
|
|
87
|
+
var Asset = {
|
|
88
|
+
BTC: "BTC",
|
|
89
|
+
ICP: "ICP",
|
|
90
|
+
USDC: "USDC",
|
|
91
|
+
USDT: "USDT"
|
|
92
|
+
};
|
|
93
|
+
var Chain = {
|
|
94
|
+
BTC: "BTC",
|
|
95
|
+
ETH: "ETH",
|
|
96
|
+
ICP: "ICP"
|
|
97
|
+
};
|
|
98
|
+
function isAssetIdentifier(identifier) {
|
|
99
|
+
switch (identifier.chain) {
|
|
100
|
+
case Chain.BTC:
|
|
101
|
+
return identifier.asset === Asset.BTC;
|
|
102
|
+
case Chain.ETH:
|
|
103
|
+
return identifier.asset === Asset.USDC || identifier.asset === Asset.USDT;
|
|
104
|
+
case Chain.ICP:
|
|
105
|
+
return identifier.asset === Asset.BTC || identifier.asset === Asset.ICP || identifier.asset === Asset.USDC || identifier.asset === Asset.USDT;
|
|
106
|
+
default:
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
var SupplyAction = {
|
|
111
|
+
deposit: "deposit",
|
|
112
|
+
repayment: "repayment"
|
|
113
|
+
};
|
|
114
|
+
var OutflowType = {
|
|
115
|
+
borrow: "borrow",
|
|
116
|
+
feeClaim: "feeClaim",
|
|
117
|
+
withdrawal: "withdrawal"
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// src/core/config.ts
|
|
121
|
+
var MAINNET_HOST = "https://icp-api.io";
|
|
122
|
+
var DEFAULT_API_BASE_URL = "https://app.liquidium.fi/api/sdk";
|
|
123
|
+
var DEFAULT_ENVIRONMENT = Environment.mainnet;
|
|
124
|
+
var MAINNET_CANISTER_IDS = {
|
|
125
|
+
lending: "hyk4r-jqaaa-aaaar-qb4ca-cai",
|
|
126
|
+
pools: {
|
|
127
|
+
btc: "hkmli-faaaa-aaaar-qb4ba-cai",
|
|
128
|
+
usdt: "hnnn4-iyaaa-aaaar-qb4bq-cai",
|
|
129
|
+
usdc: "6sna2-oiaaa-aaaar-qb66q-cai",
|
|
130
|
+
icp: "r2pk3-4yaaa-aaaar-qb7zq-cai"
|
|
131
|
+
},
|
|
132
|
+
ethDeposit: "z5jz7-nyaaa-aaaar-qb6pq-cai",
|
|
133
|
+
instantLoans: "u5rm3-niaaa-aaaar-qb7eq-cai"
|
|
134
|
+
};
|
|
135
|
+
var CANISTER_IDS_BY_ENVIRONMENT = {
|
|
136
|
+
mainnet: MAINNET_CANISTER_IDS
|
|
137
|
+
};
|
|
138
|
+
var SUPPORTED_CANISTER_ID_OVERRIDE_KEYS = /* @__PURE__ */ new Set([
|
|
139
|
+
"lending",
|
|
140
|
+
"pools",
|
|
141
|
+
"ethDeposit",
|
|
142
|
+
"instantLoans"
|
|
143
|
+
]);
|
|
144
|
+
var SUPPORTED_POOL_CANISTER_ID_OVERRIDE_KEYS = /* @__PURE__ */ new Set([
|
|
145
|
+
"btc",
|
|
146
|
+
"usdt",
|
|
147
|
+
"usdc",
|
|
148
|
+
"icp"
|
|
149
|
+
]);
|
|
150
|
+
function resolveHost(override) {
|
|
151
|
+
return override ?? MAINNET_HOST;
|
|
152
|
+
}
|
|
153
|
+
function resolveCanisterIds(environment = DEFAULT_ENVIRONMENT, overrides) {
|
|
154
|
+
const defaults = CANISTER_IDS_BY_ENVIRONMENT[environment];
|
|
155
|
+
validateCanisterIdOverrides(overrides);
|
|
156
|
+
const pools = {
|
|
157
|
+
...defaults.pools,
|
|
158
|
+
...overrides?.pools
|
|
159
|
+
};
|
|
160
|
+
return {
|
|
161
|
+
lending: overrides?.lending ?? defaults.lending,
|
|
162
|
+
pools,
|
|
163
|
+
ethDeposit: overrides?.ethDeposit ?? defaults.ethDeposit,
|
|
164
|
+
instantLoans: overrides?.instantLoans ?? defaults.instantLoans
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function validateCanisterIdOverrides(overrides) {
|
|
168
|
+
if (!overrides) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
for (const key of Object.keys(overrides)) {
|
|
172
|
+
if (!SUPPORTED_CANISTER_ID_OVERRIDE_KEYS.has(key)) {
|
|
173
|
+
throw new LiquidiumError(
|
|
174
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
175
|
+
`Unsupported canisterIds override: ${key}`
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (!overrides.pools) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
for (const key of Object.keys(overrides.pools)) {
|
|
183
|
+
if (!SUPPORTED_POOL_CANISTER_ID_OVERRIDE_KEYS.has(key)) {
|
|
184
|
+
throw new LiquidiumError(
|
|
185
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
186
|
+
`Unsupported canisterIds.pools override: ${key}`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
192
|
+
var CK_CANISTER_IDS = {
|
|
193
|
+
BTC: {
|
|
194
|
+
minter: "mqygn-kiaaa-aaaar-qaadq-cai",
|
|
195
|
+
ledger: "mxzaz-hqaaa-aaaar-qaada-cai"},
|
|
196
|
+
USDT: {
|
|
197
|
+
ledger: "cngnf-vqaaa-aaaar-qag4q-cai"},
|
|
198
|
+
USDC: {
|
|
199
|
+
ledger: "xevnm-gaaaa-aaaar-qafnq-cai"},
|
|
200
|
+
ICP: {
|
|
201
|
+
ledger: "ryjl3-tyaaa-aaaaa-aaaba-cai"}
|
|
202
|
+
};
|
|
203
|
+
|
|
131
204
|
// src/core/transports/api-client.ts
|
|
132
205
|
var DOM_EXCEPTION_ABORT_ERROR_NAME = "AbortError";
|
|
133
206
|
var HTTP_HEADER_CONTENT_TYPE = "content-type";
|
|
@@ -981,14 +1054,8 @@ function getVariantKey(variant) {
|
|
|
981
1054
|
}
|
|
982
1055
|
|
|
983
1056
|
// src/core/wallet-actions.ts
|
|
984
|
-
var TransferMode = {
|
|
985
|
-
ck: "ck",
|
|
986
|
-
native: "native"
|
|
987
|
-
};
|
|
988
1057
|
var WalletExecutionKind = {
|
|
989
|
-
|
|
990
|
-
signMessage: "sign-message",
|
|
991
|
-
signPsbt: "sign-psbt"
|
|
1058
|
+
signMessage: "sign-message"
|
|
992
1059
|
};
|
|
993
1060
|
var WalletActionKind = {
|
|
994
1061
|
createAccount: "create-account",
|
|
@@ -1017,8 +1084,7 @@ function executeWith(options) {
|
|
|
1017
1084
|
chain: options.chain,
|
|
1018
1085
|
message: action.message,
|
|
1019
1086
|
account: options.account ?? action.account,
|
|
1020
|
-
actionType: action.actionType
|
|
1021
|
-
transferMode: action.transferMode
|
|
1087
|
+
actionType: action.actionType
|
|
1022
1088
|
});
|
|
1023
1089
|
return action.submit({
|
|
1024
1090
|
signature,
|
|
@@ -1026,38 +1092,6 @@ function executeWith(options) {
|
|
|
1026
1092
|
account: options.account ?? action.account
|
|
1027
1093
|
});
|
|
1028
1094
|
}
|
|
1029
|
-
case WalletExecutionKind.signPsbt: {
|
|
1030
|
-
if (!options.walletAdapter.signPsbt) {
|
|
1031
|
-
throw new LiquidiumError(
|
|
1032
|
-
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
1033
|
-
"Wallet adapter does not support PSBT signing"
|
|
1034
|
-
);
|
|
1035
|
-
}
|
|
1036
|
-
const signedPsbtBase64 = await options.walletAdapter.signPsbt({
|
|
1037
|
-
chain: Chain.BTC,
|
|
1038
|
-
psbtBase64: action.psbtBase64,
|
|
1039
|
-
account: options.account ?? action.account,
|
|
1040
|
-
actionType: action.actionType,
|
|
1041
|
-
transferMode: action.transferMode
|
|
1042
|
-
});
|
|
1043
|
-
return action.submit({ signedPsbtBase64 });
|
|
1044
|
-
}
|
|
1045
|
-
case WalletExecutionKind.sendEthTransaction: {
|
|
1046
|
-
if (!options.walletAdapter.sendEthTransaction) {
|
|
1047
|
-
throw new LiquidiumError(
|
|
1048
|
-
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
1049
|
-
"Wallet adapter does not support ETH transaction sending"
|
|
1050
|
-
);
|
|
1051
|
-
}
|
|
1052
|
-
const txHash = await options.walletAdapter.sendEthTransaction({
|
|
1053
|
-
chain: Chain.ETH,
|
|
1054
|
-
transaction: action.transaction,
|
|
1055
|
-
account: options.account ?? action.account,
|
|
1056
|
-
actionType: action.actionType,
|
|
1057
|
-
transferMode: action.transferMode
|
|
1058
|
-
});
|
|
1059
|
-
return action.submit({ txHash });
|
|
1060
|
-
}
|
|
1061
1095
|
default:
|
|
1062
1096
|
throw new LiquidiumError(
|
|
1063
1097
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
@@ -1273,7 +1307,6 @@ var AccountsModule = class {
|
|
|
1273
1307
|
kind: WalletActionKind.createAccount,
|
|
1274
1308
|
executionKind: WalletExecutionKind.signMessage,
|
|
1275
1309
|
actionType: WalletActionKind.createAccount,
|
|
1276
|
-
transferMode: TransferMode.native,
|
|
1277
1310
|
account: normalizedAccount,
|
|
1278
1311
|
message: createInitializeAccountMessage(expiryTimestamp, nonce),
|
|
1279
1312
|
data: {
|
|
@@ -1364,8 +1397,8 @@ function mapCanisterWalletToWallet(canisterWallet) {
|
|
|
1364
1397
|
);
|
|
1365
1398
|
}
|
|
1366
1399
|
}
|
|
1367
|
-
var KNOWN_ASSET_TAGS = ["BTC", "USDC", "USDT"];
|
|
1368
|
-
var KNOWN_CHAIN_TAGS = ["BTC", "ETH"];
|
|
1400
|
+
var KNOWN_ASSET_TAGS = ["BTC", "ICP", "USDC", "USDT"];
|
|
1401
|
+
var KNOWN_CHAIN_TAGS = ["BTC", "ETH", "ICP"];
|
|
1369
1402
|
function extractVariantTag(variant, knownTags) {
|
|
1370
1403
|
const [key] = Object.keys(variant);
|
|
1371
1404
|
if (!key) {
|
|
@@ -1944,17 +1977,14 @@ function mapInstantLoanLookupError(error) {
|
|
|
1944
1977
|
}
|
|
1945
1978
|
function mapActivity(wire) {
|
|
1946
1979
|
const amount = parseBigInt(wire.amount, "activity amount");
|
|
1947
|
-
const activityBase = {
|
|
1948
|
-
id: wire.id,
|
|
1949
|
-
poolId: wire.poolId,
|
|
1950
|
-
asset: wire.asset,
|
|
1951
|
-
chain: wire.chain,
|
|
1952
|
-
amount,
|
|
1953
|
-
timestampMs: wire.timestampMs
|
|
1954
|
-
};
|
|
1955
1980
|
if (isInflowOperation(wire.status.operation)) {
|
|
1956
1981
|
const activity = {
|
|
1957
|
-
|
|
1982
|
+
id: wire.id,
|
|
1983
|
+
poolId: wire.poolId,
|
|
1984
|
+
asset: wire.asset,
|
|
1985
|
+
chain: wire.chain,
|
|
1986
|
+
amount,
|
|
1987
|
+
timestampMs: wire.timestampMs,
|
|
1958
1988
|
status: wire.status
|
|
1959
1989
|
};
|
|
1960
1990
|
if (wire.txids) {
|
|
@@ -1967,7 +1997,12 @@ function mapActivity(wire) {
|
|
|
1967
1997
|
}
|
|
1968
1998
|
if (isOutflowOperation(wire.status.operation)) {
|
|
1969
1999
|
const activity = {
|
|
1970
|
-
|
|
2000
|
+
id: wire.id,
|
|
2001
|
+
poolId: wire.poolId,
|
|
2002
|
+
asset: wire.asset,
|
|
2003
|
+
chain: wire.chain,
|
|
2004
|
+
amount,
|
|
2005
|
+
timestampMs: wire.timestampMs,
|
|
1971
2006
|
status: wire.status
|
|
1972
2007
|
};
|
|
1973
2008
|
if (wire.txids) {
|
|
@@ -2143,6 +2178,72 @@ function validateHistoryStateFilter(state) {
|
|
|
2143
2178
|
);
|
|
2144
2179
|
}
|
|
2145
2180
|
}
|
|
2181
|
+
var LiquidiumAccountType = {
|
|
2182
|
+
ChainAddress: "ChainAddress",
|
|
2183
|
+
IcPrincipal: "IcPrincipal",
|
|
2184
|
+
IcpAccountIdentifier: "IcpAccountIdentifier",
|
|
2185
|
+
IcrcAccount: "IcrcAccount"
|
|
2186
|
+
};
|
|
2187
|
+
function createIcrcAccount(params) {
|
|
2188
|
+
return {
|
|
2189
|
+
type: "IcrcAccount",
|
|
2190
|
+
owner: params.owner.toText(),
|
|
2191
|
+
subaccount: params.subaccount,
|
|
2192
|
+
address: encodeIcrcAccount({
|
|
2193
|
+
owner: params.owner,
|
|
2194
|
+
subaccount: params.subaccount
|
|
2195
|
+
})
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
function mapCanisterAccountToLiquidiumAccount(account) {
|
|
2199
|
+
if ("Native" in account) {
|
|
2200
|
+
return { type: "IcPrincipal", address: account.Native.toText() };
|
|
2201
|
+
}
|
|
2202
|
+
if ("AccountIdentifier" in account) {
|
|
2203
|
+
return {
|
|
2204
|
+
type: "IcpAccountIdentifier",
|
|
2205
|
+
address: account.AccountIdentifier
|
|
2206
|
+
};
|
|
2207
|
+
}
|
|
2208
|
+
if ("Icrc" in account) {
|
|
2209
|
+
const subaccount = normalizeOptionalSubaccount(account.Icrc.subaccount[0]);
|
|
2210
|
+
return {
|
|
2211
|
+
type: "IcrcAccount",
|
|
2212
|
+
owner: account.Icrc.owner.toText(),
|
|
2213
|
+
subaccount,
|
|
2214
|
+
address: encodeIcrcAccount({
|
|
2215
|
+
owner: account.Icrc.owner,
|
|
2216
|
+
subaccount
|
|
2217
|
+
})
|
|
2218
|
+
};
|
|
2219
|
+
}
|
|
2220
|
+
return { type: "ChainAddress", address: account.External };
|
|
2221
|
+
}
|
|
2222
|
+
function decodeIcrcAccountAddress(address) {
|
|
2223
|
+
const decoded = decodeIcrcAccount(address);
|
|
2224
|
+
const owner = Principal.fromText(decoded.owner.toText());
|
|
2225
|
+
const subaccount = decoded.subaccount ? Uint8Array.from(decoded.subaccount) : void 0;
|
|
2226
|
+
return {
|
|
2227
|
+
owner,
|
|
2228
|
+
subaccount,
|
|
2229
|
+
account: createIcrcAccount({ owner, subaccount })
|
|
2230
|
+
};
|
|
2231
|
+
}
|
|
2232
|
+
function encodeIcpAccountIdentifier(params) {
|
|
2233
|
+
return AccountIdentifier.fromPrincipal({
|
|
2234
|
+
principal: params.owner,
|
|
2235
|
+
subAccount: params.subaccount ? SubAccount.fromBytes(params.subaccount) : void 0
|
|
2236
|
+
}).toHex();
|
|
2237
|
+
}
|
|
2238
|
+
function normalizeIcpAccountIdentifier(address) {
|
|
2239
|
+
return AccountIdentifier.fromHex(address).toHex();
|
|
2240
|
+
}
|
|
2241
|
+
function normalizeOptionalSubaccount(subaccount) {
|
|
2242
|
+
if (!subaccount) {
|
|
2243
|
+
return void 0;
|
|
2244
|
+
}
|
|
2245
|
+
return subaccount instanceof Uint8Array ? subaccount : Uint8Array.from(subaccount);
|
|
2246
|
+
}
|
|
2146
2247
|
|
|
2147
2248
|
// src/core/status.ts
|
|
2148
2249
|
function createLiquidiumStatus(params) {
|
|
@@ -2192,6 +2293,7 @@ function throwInvalidApiResponseField(field) {
|
|
|
2192
2293
|
// src/core/utils/asset-decimals.ts
|
|
2193
2294
|
var ASSET_NATIVE_DECIMALS = {
|
|
2194
2295
|
BTC: 8n,
|
|
2296
|
+
ICP: 8n,
|
|
2195
2297
|
USDC: 6n,
|
|
2196
2298
|
USDT: 6n,
|
|
2197
2299
|
SOL: 9n
|
|
@@ -2587,7 +2689,7 @@ var idlFactory2 = ({ IDL }) => {
|
|
|
2587
2689
|
|
|
2588
2690
|
// src/core/canisters/ckbtc/minter.ts
|
|
2589
2691
|
function createCkBtcMinterActor(canisterContext) {
|
|
2590
|
-
const canisterId = CK_CANISTER_IDS.
|
|
2692
|
+
const canisterId = CK_CANISTER_IDS.BTC.minter;
|
|
2591
2693
|
return Actor.createActor(idlFactory2, {
|
|
2592
2694
|
agent: canisterContext.agent,
|
|
2593
2695
|
canisterId
|
|
@@ -3038,55 +3140,43 @@ var EvmSupplyApprovalStrategy = {
|
|
|
3038
3140
|
};
|
|
3039
3141
|
|
|
3040
3142
|
// src/modules/lending/_internal/supply-targets.ts
|
|
3041
|
-
async function
|
|
3042
|
-
const
|
|
3043
|
-
|
|
3044
|
-
|
|
3143
|
+
async function resolveSupplyTargetForPool(canisterContext, request, selectedPool) {
|
|
3144
|
+
const identifier = resolveSupplyAssetIdentifier({
|
|
3145
|
+
asset: selectedPool.asset,
|
|
3146
|
+
poolChain: selectedPool.chain,
|
|
3147
|
+
transferChain: request.chain ?? selectedPool.chain
|
|
3148
|
+
});
|
|
3045
3149
|
const mechanism = resolveSupplyMechanism({
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
requestedMechanism: request.mechanism
|
|
3150
|
+
identifier,
|
|
3151
|
+
mechanism: request.mechanism
|
|
3049
3152
|
});
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
asset,
|
|
3058
|
-
chain,
|
|
3059
|
-
action: request.action
|
|
3060
|
-
}
|
|
3061
|
-
);
|
|
3062
|
-
case SupplyPlanType.contractInteraction:
|
|
3063
|
-
return getIcrcAccountSupplyTarget(request.profileId, {
|
|
3064
|
-
poolId: request.poolId,
|
|
3065
|
-
asset,
|
|
3066
|
-
chain,
|
|
3067
|
-
action: request.action
|
|
3068
|
-
});
|
|
3153
|
+
const targetRequest = {
|
|
3154
|
+
...identifier,
|
|
3155
|
+
poolId: request.poolId,
|
|
3156
|
+
action: request.action
|
|
3157
|
+
};
|
|
3158
|
+
if (mechanism === SupplyPlanType.contractInteraction) {
|
|
3159
|
+
return getIcrcAccountSupplyTarget(request.profileId, targetRequest);
|
|
3069
3160
|
}
|
|
3161
|
+
if (identifier.chain === Chain.ICP) {
|
|
3162
|
+
return identifier.asset === Asset.ICP ? getIcpLedgerSupplyTarget(request.profileId, targetRequest) : getIcrcAccountSupplyTarget(request.profileId, targetRequest);
|
|
3163
|
+
}
|
|
3164
|
+
return await getChainAddressSupplyTarget(
|
|
3165
|
+
canisterContext,
|
|
3166
|
+
request.profileId,
|
|
3167
|
+
targetRequest
|
|
3168
|
+
);
|
|
3070
3169
|
}
|
|
3071
3170
|
function resolveSupplyMechanism(params) {
|
|
3072
|
-
if (params.
|
|
3073
|
-
if (params.requestedMechanism === SupplyPlanType.contractInteraction) {
|
|
3074
|
-
throw new LiquidiumError(
|
|
3075
|
-
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
3076
|
-
"Contract-interaction supply is not supported for BTC on BTC"
|
|
3077
|
-
);
|
|
3078
|
-
}
|
|
3171
|
+
if (params.mechanism !== SupplyPlanType.contractInteraction) {
|
|
3079
3172
|
return SupplyPlanType.transfer;
|
|
3080
3173
|
}
|
|
3081
|
-
if (
|
|
3082
|
-
|
|
3083
|
-
return params.requestedMechanism;
|
|
3084
|
-
}
|
|
3085
|
-
return SupplyPlanType.transfer;
|
|
3174
|
+
if (params.identifier.chain === Chain.ETH && (params.identifier.asset === Asset.USDC || params.identifier.asset === Asset.USDT)) {
|
|
3175
|
+
return SupplyPlanType.contractInteraction;
|
|
3086
3176
|
}
|
|
3087
3177
|
throw new LiquidiumError(
|
|
3088
3178
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
3089
|
-
`
|
|
3179
|
+
`Contract-interaction supply is not supported for ${params.identifier.asset} on ${params.identifier.chain}`
|
|
3090
3180
|
);
|
|
3091
3181
|
}
|
|
3092
3182
|
function isEthStablecoin(asset, chain) {
|
|
@@ -3158,9 +3248,33 @@ async function getPoolById(canisterContext, poolId) {
|
|
|
3158
3248
|
}
|
|
3159
3249
|
return decodedPool;
|
|
3160
3250
|
}
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3251
|
+
function resolveSupplyAssetIdentifier(params) {
|
|
3252
|
+
if (params.asset === Asset.BTC && params.poolChain === Chain.BTC) {
|
|
3253
|
+
if (params.transferChain === Chain.BTC) {
|
|
3254
|
+
return { asset: Asset.BTC, chain: Chain.BTC };
|
|
3255
|
+
}
|
|
3256
|
+
if (params.transferChain === Chain.ICP) {
|
|
3257
|
+
return { asset: Asset.BTC, chain: Chain.ICP };
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
if ((params.asset === Asset.USDC || params.asset === Asset.USDT) && params.poolChain === Chain.ETH) {
|
|
3261
|
+
if (params.transferChain === Chain.ETH) {
|
|
3262
|
+
return { asset: params.asset, chain: Chain.ETH };
|
|
3263
|
+
}
|
|
3264
|
+
if (params.transferChain === Chain.ICP) {
|
|
3265
|
+
return { asset: params.asset, chain: Chain.ICP };
|
|
3266
|
+
}
|
|
3267
|
+
}
|
|
3268
|
+
if (params.asset === Asset.ICP && params.poolChain === Chain.ICP && params.transferChain === Chain.ICP) {
|
|
3269
|
+
return { asset: Asset.ICP, chain: Chain.ICP };
|
|
3270
|
+
}
|
|
3271
|
+
throw new LiquidiumError(
|
|
3272
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
3273
|
+
`Supply is not supported for ${params.asset} pool on ${params.poolChain} using ${params.transferChain}`
|
|
3274
|
+
);
|
|
3275
|
+
}
|
|
3276
|
+
async function getChainAddressSupplyTarget(canisterContext, profileId, request) {
|
|
3277
|
+
if (request.chain === Chain.ETH) {
|
|
3164
3278
|
const tokenAddress = getEthStablecoinContractAddress(request.asset);
|
|
3165
3279
|
const subaccount2 = encodeInflowSubaccount({
|
|
3166
3280
|
action: request.action,
|
|
@@ -3179,7 +3293,6 @@ async function getNativeAddressSupplyTarget(canisterContext, profileId, request)
|
|
|
3179
3293
|
throw mapDepositAccountErrorToLiquidiumError(result.Err);
|
|
3180
3294
|
}
|
|
3181
3295
|
return {
|
|
3182
|
-
type: "nativeAddress",
|
|
3183
3296
|
poolId: request.poolId,
|
|
3184
3297
|
asset: request.asset,
|
|
3185
3298
|
chain: request.chain,
|
|
@@ -3187,11 +3300,11 @@ async function getNativeAddressSupplyTarget(canisterContext, profileId, request)
|
|
|
3187
3300
|
address: result.Ok
|
|
3188
3301
|
};
|
|
3189
3302
|
}
|
|
3190
|
-
const configuredBtcPoolId = canisterContext.canisterIds.
|
|
3303
|
+
const configuredBtcPoolId = canisterContext.canisterIds.pools.btc;
|
|
3191
3304
|
if (request.poolId !== configuredBtcPoolId) {
|
|
3192
3305
|
throw new LiquidiumError(
|
|
3193
3306
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
3194
|
-
`
|
|
3307
|
+
`Chain-address BTC inflow targets require the configured BTC pool ${configuredBtcPoolId}, received ${request.poolId}`
|
|
3195
3308
|
);
|
|
3196
3309
|
}
|
|
3197
3310
|
const subaccount = encodeInflowSubaccount({
|
|
@@ -3204,66 +3317,51 @@ async function getNativeAddressSupplyTarget(canisterContext, profileId, request)
|
|
|
3204
3317
|
subaccount: [subaccount]
|
|
3205
3318
|
}
|
|
3206
3319
|
);
|
|
3207
|
-
return {
|
|
3208
|
-
type: "nativeAddress",
|
|
3209
|
-
poolId: request.poolId,
|
|
3210
|
-
asset: request.asset,
|
|
3211
|
-
chain: request.chain,
|
|
3212
|
-
action: request.action,
|
|
3213
|
-
address
|
|
3214
|
-
};
|
|
3320
|
+
return { ...request, address };
|
|
3215
3321
|
}
|
|
3216
3322
|
function getIcrcAccountSupplyTarget(profileId, request) {
|
|
3217
|
-
|
|
3323
|
+
const account = createInflowIcrcAccount(profileId, request);
|
|
3324
|
+
return { ...request, address: account.address };
|
|
3325
|
+
}
|
|
3326
|
+
function getIcpLedgerSupplyTarget(profileId, request) {
|
|
3218
3327
|
const poolPrincipal = Principal.fromText(request.poolId);
|
|
3219
3328
|
const subaccount = encodeInflowSubaccount({
|
|
3220
3329
|
action: request.action,
|
|
3221
3330
|
principal: Principal.fromText(profileId)
|
|
3222
3331
|
});
|
|
3332
|
+
const account = createIcrcAccount({
|
|
3333
|
+
owner: poolPrincipal,
|
|
3334
|
+
subaccount
|
|
3335
|
+
});
|
|
3223
3336
|
return {
|
|
3224
|
-
type: "icrcAccount",
|
|
3225
3337
|
poolId: request.poolId,
|
|
3226
|
-
asset:
|
|
3227
|
-
chain:
|
|
3338
|
+
asset: Asset.ICP,
|
|
3339
|
+
chain: Chain.ICP,
|
|
3228
3340
|
action: request.action,
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
account: encodeIcrcAccount({
|
|
3341
|
+
address: account.address,
|
|
3342
|
+
icpAccountIdentifier: encodeIcpAccountIdentifier({
|
|
3232
3343
|
owner: poolPrincipal,
|
|
3233
3344
|
subaccount
|
|
3234
3345
|
})
|
|
3235
3346
|
};
|
|
3236
3347
|
}
|
|
3237
|
-
function
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
3246
|
-
`Native address inflow targets are not supported for ${asset} on ${chain}`
|
|
3247
|
-
);
|
|
3248
|
-
}
|
|
3249
|
-
function assertSupportsIcrcAccountInflowTarget(asset) {
|
|
3250
|
-
if (asset === Asset.BTC || asset === Asset.USDT || asset === Asset.USDC) {
|
|
3251
|
-
return;
|
|
3252
|
-
}
|
|
3253
|
-
throw new LiquidiumError(
|
|
3254
|
-
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
3255
|
-
`ICRC account inflow targets are not supported for ${asset}`
|
|
3256
|
-
);
|
|
3348
|
+
function createInflowIcrcAccount(profileId, request) {
|
|
3349
|
+
return createIcrcAccount({
|
|
3350
|
+
owner: Principal.fromText(request.poolId),
|
|
3351
|
+
subaccount: encodeInflowSubaccount({
|
|
3352
|
+
action: request.action,
|
|
3353
|
+
principal: Principal.fromText(profileId)
|
|
3354
|
+
})
|
|
3355
|
+
});
|
|
3257
3356
|
}
|
|
3258
3357
|
|
|
3259
3358
|
// src/core/borrow-minimums.ts
|
|
3359
|
+
var MINIMUM_BTC_BORROW_AMOUNT_SATS = 5100n;
|
|
3360
|
+
var MINIMUM_STABLECOIN_BORROW_AMOUNT_BASE_UNITS = 1000000n;
|
|
3260
3361
|
var MIN_BORROW_AMOUNTS_BY_ASSET = {
|
|
3261
|
-
BTC:
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
// 1 USDC
|
|
3265
|
-
USDT: 1000000n
|
|
3266
|
-
// 1 USDT
|
|
3362
|
+
[Asset.BTC]: MINIMUM_BTC_BORROW_AMOUNT_SATS,
|
|
3363
|
+
[Asset.USDC]: MINIMUM_STABLECOIN_BORROW_AMOUNT_BASE_UNITS,
|
|
3364
|
+
[Asset.USDT]: MINIMUM_STABLECOIN_BORROW_AMOUNT_BASE_UNITS
|
|
3267
3365
|
};
|
|
3268
3366
|
function getMinimumBorrowAmount(asset) {
|
|
3269
3367
|
if (!isMinimumBorrowAsset(asset)) {
|
|
@@ -3728,25 +3826,20 @@ function isEthStablecoin2(asset) {
|
|
|
3728
3826
|
}
|
|
3729
3827
|
|
|
3730
3828
|
// src/modules/instant-loans/_internal/address-validation.ts
|
|
3731
|
-
function validateInstantLoanBorrowDestination(address, asset) {
|
|
3829
|
+
function validateInstantLoanBorrowDestination(address, asset, chain) {
|
|
3732
3830
|
return normalizeExternalAddress({
|
|
3733
3831
|
address,
|
|
3734
3832
|
asset,
|
|
3735
|
-
chain
|
|
3833
|
+
chain
|
|
3736
3834
|
});
|
|
3737
3835
|
}
|
|
3738
|
-
function validateInstantLoanRefundDestination(address, asset) {
|
|
3836
|
+
function validateInstantLoanRefundDestination(address, asset, chain) {
|
|
3739
3837
|
return normalizeExternalAddress({
|
|
3740
3838
|
address,
|
|
3741
3839
|
asset,
|
|
3742
|
-
chain
|
|
3840
|
+
chain
|
|
3743
3841
|
});
|
|
3744
3842
|
}
|
|
3745
|
-
function getChainForInstantLoanAsset(asset) {
|
|
3746
|
-
if (asset === Asset.BTC) return Chain.BTC;
|
|
3747
|
-
if (asset === Asset.USDC || asset === Asset.USDT) return Chain.ETH;
|
|
3748
|
-
return asset;
|
|
3749
|
-
}
|
|
3750
3843
|
|
|
3751
3844
|
// src/modules/instant-loans/instant-loans.ts
|
|
3752
3845
|
var REPAYMENT_BUFFER_SECONDS = 86400n;
|
|
@@ -3759,10 +3852,32 @@ var INSTANT_LOAN_FIND_QUERY_MAX_LENGTH = 256;
|
|
|
3759
3852
|
var INSTANT_LOAN_WIRE_CONTEXT = "instant loan";
|
|
3760
3853
|
var INSTANT_LOAN_ASSETS = [
|
|
3761
3854
|
Asset.BTC,
|
|
3762
|
-
Asset.
|
|
3855
|
+
Asset.ICP,
|
|
3763
3856
|
Asset.USDC,
|
|
3764
3857
|
Asset.USDT
|
|
3765
3858
|
];
|
|
3859
|
+
var INSTANT_LOAN_CHAINS = [
|
|
3860
|
+
Chain.BTC,
|
|
3861
|
+
Chain.ETH,
|
|
3862
|
+
Chain.ICP
|
|
3863
|
+
];
|
|
3864
|
+
var ICP_ACCOUNT_IDENTIFIER_HEX_LENGTH = 64;
|
|
3865
|
+
var InstantLoanCreatedError = class extends Error {
|
|
3866
|
+
code = "INSTANT_LOAN_HYDRATION_FAILED";
|
|
3867
|
+
loanId;
|
|
3868
|
+
ref;
|
|
3869
|
+
cause;
|
|
3870
|
+
constructor(loanId, cause) {
|
|
3871
|
+
const ref = publicIdFromInt(loanId);
|
|
3872
|
+
super(
|
|
3873
|
+
`Instant loan ${ref} was created, but its enriched state could not be loaded`
|
|
3874
|
+
);
|
|
3875
|
+
this.name = "InstantLoanCreatedError";
|
|
3876
|
+
this.loanId = loanId;
|
|
3877
|
+
this.ref = ref;
|
|
3878
|
+
this.cause = cause;
|
|
3879
|
+
}
|
|
3880
|
+
};
|
|
3766
3881
|
var InstantLoansModule = class {
|
|
3767
3882
|
constructor(canisterContext, apiClient, activities, lending, positions) {
|
|
3768
3883
|
this.canisterContext = canisterContext;
|
|
@@ -3785,37 +3900,39 @@ var InstantLoansModule = class {
|
|
|
3785
3900
|
* selected pool decimals, and call `client.quote.calculateLtv(...)` before
|
|
3786
3901
|
* creation to block invalid LTV input.
|
|
3787
3902
|
*
|
|
3788
|
-
* `
|
|
3789
|
-
* `
|
|
3903
|
+
* `borrow.destination` receives the borrowed asset after the loan starts.
|
|
3904
|
+
* `refund.destination` receives collateral refunds or withdrawals. Use
|
|
3790
3905
|
* `depositWindowSeconds` for the user-facing collateral deposit timeout; the
|
|
3791
3906
|
* SDK maps it to the canister's internal `ltv_timer_s` field.
|
|
3792
3907
|
*
|
|
3793
|
-
* @param request -
|
|
3908
|
+
* @param request - Collateral, borrow, refund, LTV limit, timeout, and inflow options.
|
|
3794
3909
|
* @returns Hydrated loan state plus generated initial-deposit and repayment quote targets.
|
|
3795
3910
|
*/
|
|
3796
3911
|
async create(request) {
|
|
3797
3912
|
validateCreateRequest(request);
|
|
3798
|
-
const borrowDestination = {
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3913
|
+
const borrowDestination = resolveInstantLoanDestination({
|
|
3914
|
+
destination: request.borrow.destination,
|
|
3915
|
+
asset: request.borrow.asset,
|
|
3916
|
+
role: "borrow",
|
|
3917
|
+
chain: request.borrow.chain,
|
|
3918
|
+
normalizeExternalDestination: validateInstantLoanBorrowDestination
|
|
3919
|
+
});
|
|
3920
|
+
const refundDestination = resolveInstantLoanDestination({
|
|
3921
|
+
destination: request.refund.destination,
|
|
3922
|
+
asset: request.collateral.asset,
|
|
3923
|
+
role: "refund",
|
|
3924
|
+
chain: request.refund.chain,
|
|
3925
|
+
normalizeExternalDestination: validateInstantLoanRefundDestination
|
|
3926
|
+
});
|
|
3810
3927
|
const apiClient = this.requireApi("Instant loan creation");
|
|
3811
3928
|
await this.validateInstantLoanLtvPolicy(request);
|
|
3812
3929
|
const response = await apiClient.post(SdkApiPath.instantLoans, {
|
|
3813
|
-
collateralPoolId: request.
|
|
3814
|
-
borrowPoolId: request.
|
|
3815
|
-
collateralAsset: request.
|
|
3816
|
-
borrowAsset: request.
|
|
3817
|
-
collateralAmount: request.
|
|
3818
|
-
borrowAmount: request.
|
|
3930
|
+
collateralPoolId: request.collateral.poolId,
|
|
3931
|
+
borrowPoolId: request.borrow.poolId,
|
|
3932
|
+
collateralAsset: request.collateral.asset,
|
|
3933
|
+
borrowAsset: request.borrow.asset,
|
|
3934
|
+
collateralAmount: request.collateral.amount.toString(),
|
|
3935
|
+
borrowAmount: request.borrow.amount.toString(),
|
|
3819
3936
|
ltvMaxBps: request.ltvMaxBps.toString(),
|
|
3820
3937
|
depositWindowSeconds: request.depositWindowSeconds.toString(),
|
|
3821
3938
|
borrowDestination,
|
|
@@ -3825,15 +3942,21 @@ var InstantLoansModule = class {
|
|
|
3825
3942
|
context: INSTANT_LOAN_WIRE_CONTEXT,
|
|
3826
3943
|
label: "loan ID"
|
|
3827
3944
|
});
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3945
|
+
try {
|
|
3946
|
+
const collateralAmount = parseUnsignedApiBigint(
|
|
3947
|
+
response.loan.collateral.amountHint,
|
|
3948
|
+
{
|
|
3949
|
+
context: INSTANT_LOAN_WIRE_CONTEXT,
|
|
3950
|
+
label: "collateral amount"
|
|
3951
|
+
}
|
|
3952
|
+
);
|
|
3953
|
+
const record = await this.getLoanRecord(loanId);
|
|
3954
|
+
return await this.mapLoanRecord(record, collateralAmount, {
|
|
3955
|
+
borrowChain: request.borrow.chain
|
|
3956
|
+
});
|
|
3957
|
+
} catch (cause) {
|
|
3958
|
+
throw new InstantLoanCreatedError(loanId, cause);
|
|
3959
|
+
}
|
|
3837
3960
|
}
|
|
3838
3961
|
/**
|
|
3839
3962
|
* Resolves canonical canister state by loan id or short reference.
|
|
@@ -3999,7 +4122,16 @@ var InstantLoansModule = class {
|
|
|
3999
4122
|
throw mapCanisterCallErrorToLiquidiumError("get_loan", error);
|
|
4000
4123
|
}
|
|
4001
4124
|
}
|
|
4002
|
-
async mapLoanRecord(record, collateralAmount) {
|
|
4125
|
+
async mapLoanRecord(record, collateralAmount, chainOptions = {}) {
|
|
4126
|
+
const borrowDestination = accountFromCanister(record.borrow_destination);
|
|
4127
|
+
const collateralAsset = parseInstantLoanAsset(
|
|
4128
|
+
record.lend_asset,
|
|
4129
|
+
"collateral asset"
|
|
4130
|
+
);
|
|
4131
|
+
const borrowAsset = parseInstantLoanAsset(
|
|
4132
|
+
record.borrow_asset,
|
|
4133
|
+
"borrow asset"
|
|
4134
|
+
);
|
|
4003
4135
|
return await this.hydrateLoan({
|
|
4004
4136
|
loanId: record.id,
|
|
4005
4137
|
profileId: record.lending_profile.toText(),
|
|
@@ -4008,17 +4140,18 @@ var InstantLoansModule = class {
|
|
|
4008
4140
|
collateralPoolId: record.lend_pool_id.toText(),
|
|
4009
4141
|
collateralAmount,
|
|
4010
4142
|
borrowPoolId: record.borrow_pool_id.toText(),
|
|
4011
|
-
collateralAsset
|
|
4012
|
-
borrowAsset
|
|
4143
|
+
collateralAsset,
|
|
4144
|
+
borrowAsset,
|
|
4013
4145
|
borrowAmount: record.borrow_amount,
|
|
4014
|
-
borrowDestination
|
|
4146
|
+
borrowDestination,
|
|
4015
4147
|
refundDestination: accountFromCanister(record.refund_destination),
|
|
4016
4148
|
started: record.started,
|
|
4017
4149
|
depositDetectedTimestamp: record.deposit_detected_ts[0] ?? null,
|
|
4018
4150
|
expiryTimestamp: record.expires_at[0] ?? deriveDepositExpiryTimestamp({
|
|
4019
4151
|
depositDetectedTimestamp: record.deposit_detected_ts[0] ?? null,
|
|
4020
4152
|
depositWindowSeconds: record.ltv_timer_s
|
|
4021
|
-
})
|
|
4153
|
+
}),
|
|
4154
|
+
borrowChain: chainOptions.borrowChain ?? inferInstantLoanDeliveryChain(borrowDestination, borrowAsset)
|
|
4022
4155
|
});
|
|
4023
4156
|
}
|
|
4024
4157
|
async getCollateralAmountHint(loanId) {
|
|
@@ -4038,24 +4171,22 @@ var InstantLoansModule = class {
|
|
|
4038
4171
|
const collateralAsset = input.collateralAsset;
|
|
4039
4172
|
const borrowAsset = input.borrowAsset;
|
|
4040
4173
|
const [
|
|
4041
|
-
|
|
4042
|
-
|
|
4174
|
+
depositTargetSet,
|
|
4175
|
+
repaymentTargetSet,
|
|
4043
4176
|
collateralPosition,
|
|
4044
4177
|
borrowPosition,
|
|
4045
4178
|
borrowPoolRate,
|
|
4046
4179
|
activeActivities
|
|
4047
4180
|
] = await Promise.all([
|
|
4048
|
-
|
|
4181
|
+
this.resolveInstantLoanInflowTargets({
|
|
4049
4182
|
profileId,
|
|
4050
4183
|
poolId: collateralPoolId,
|
|
4051
|
-
action: SupplyAction.deposit
|
|
4052
|
-
mechanism: SupplyPlanType.transfer
|
|
4184
|
+
action: SupplyAction.deposit
|
|
4053
4185
|
}),
|
|
4054
|
-
|
|
4186
|
+
this.resolveInstantLoanInflowTargets({
|
|
4055
4187
|
profileId,
|
|
4056
4188
|
poolId: borrowPoolId,
|
|
4057
|
-
action: SupplyAction.repayment
|
|
4058
|
-
mechanism: SupplyPlanType.transfer
|
|
4189
|
+
action: SupplyAction.repayment
|
|
4059
4190
|
}),
|
|
4060
4191
|
this.positions.getPosition(profileId, collateralPoolId),
|
|
4061
4192
|
this.positions.getPosition(profileId, borrowPoolId),
|
|
@@ -4067,8 +4198,6 @@ var InstantLoansModule = class {
|
|
|
4067
4198
|
borrowPosition,
|
|
4068
4199
|
borrowPoolRate.borrowRate
|
|
4069
4200
|
);
|
|
4070
|
-
const repaymentInflowFee = totalDebtAmount > 0n ? await this.estimateRepaymentInflowFee(borrowAsset, repayTarget.chain) : { totalFee: 0n, estimateAvailable: false };
|
|
4071
|
-
const repaymentAmount = totalDebtAmount + interestBufferAmount + repaymentInflowFee.totalFee;
|
|
4072
4201
|
const currentCollateralAmount = collateralPosition?.deposited ?? 0n;
|
|
4073
4202
|
const collateralAmount = input.collateralAmount;
|
|
4074
4203
|
const collateralDecimals = collateralPosition?.depositedDecimals ?? getAssetNativeDecimals(collateralAsset);
|
|
@@ -4088,22 +4217,28 @@ var InstantLoansModule = class {
|
|
|
4088
4217
|
collateralAmount,
|
|
4089
4218
|
decimals: collateralDecimals,
|
|
4090
4219
|
asset: collateralAsset,
|
|
4091
|
-
|
|
4220
|
+
targets: depositTargetSet.targets,
|
|
4092
4221
|
detectedTimestamp: input.depositDetectedTimestamp,
|
|
4093
4222
|
expiryTimestamp: input.expiryTimestamp
|
|
4094
4223
|
});
|
|
4224
|
+
const repaymentTargets = await this.createRepaymentTargetQuotes({
|
|
4225
|
+
baseRepaymentAmount: totalDebtAmount + interestBufferAmount,
|
|
4226
|
+
asset: borrowAsset,
|
|
4227
|
+
targets: repaymentTargetSet.targets
|
|
4228
|
+
});
|
|
4095
4229
|
const repayment = {
|
|
4096
|
-
amount: repaymentAmount,
|
|
4097
4230
|
decimals: borrowedDecimals,
|
|
4098
4231
|
debtAmount: totalDebtAmount,
|
|
4099
4232
|
interestBufferAmount,
|
|
4100
4233
|
interestBufferSeconds: REPAYMENT_BUFFER_SECONDS,
|
|
4101
|
-
inflowFeeAmount: repaymentInflowFee.totalFee,
|
|
4102
|
-
inflowFeeEstimateAvailable: repaymentInflowFee.estimateAvailable,
|
|
4103
4234
|
asset: borrowAsset,
|
|
4104
|
-
|
|
4105
|
-
target: repayTarget
|
|
4235
|
+
targets: repaymentTargets
|
|
4106
4236
|
};
|
|
4237
|
+
const borrowIdentifier = getInstantLoanAssetIdentifier(
|
|
4238
|
+
borrowAsset,
|
|
4239
|
+
input.borrowChain,
|
|
4240
|
+
"borrow"
|
|
4241
|
+
);
|
|
4107
4242
|
return {
|
|
4108
4243
|
loanId: input.loanId,
|
|
4109
4244
|
ref: publicIdFromInt(input.loanId),
|
|
@@ -4114,16 +4249,14 @@ var InstantLoansModule = class {
|
|
|
4114
4249
|
depositWindowSeconds: input.depositWindowSeconds
|
|
4115
4250
|
},
|
|
4116
4251
|
collateral: {
|
|
4117
|
-
poolId: collateralPoolId,
|
|
4118
4252
|
asset: collateralAsset,
|
|
4119
|
-
|
|
4253
|
+
poolId: collateralPoolId,
|
|
4120
4254
|
decimals: collateralDecimals,
|
|
4121
4255
|
amount: collateralAmount
|
|
4122
4256
|
},
|
|
4123
4257
|
borrow: {
|
|
4258
|
+
...borrowIdentifier,
|
|
4124
4259
|
poolId: borrowPoolId,
|
|
4125
|
-
asset: borrowAsset,
|
|
4126
|
-
chain: repayTarget.chain,
|
|
4127
4260
|
decimals: borrowedDecimals,
|
|
4128
4261
|
amount: input.borrowAmount,
|
|
4129
4262
|
destination: input.borrowDestination
|
|
@@ -4143,32 +4276,81 @@ var InstantLoansModule = class {
|
|
|
4143
4276
|
};
|
|
4144
4277
|
}
|
|
4145
4278
|
async createInitialDepositQuote(input) {
|
|
4146
|
-
const
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4279
|
+
const targets = {};
|
|
4280
|
+
for (const chain of INSTANT_LOAN_CHAINS) {
|
|
4281
|
+
const target = input.targets[chain];
|
|
4282
|
+
if (!target) continue;
|
|
4283
|
+
assertTargetAsset(target, input.asset);
|
|
4284
|
+
const inflowFee = await this.lending.estimateInflowFee(target);
|
|
4285
|
+
targets[chain] = {
|
|
4286
|
+
amount: input.collateralAmount + inflowFee.totalFee,
|
|
4287
|
+
inflowFeeAmount: inflowFee.totalFee,
|
|
4288
|
+
target
|
|
4289
|
+
};
|
|
4290
|
+
}
|
|
4150
4291
|
return {
|
|
4151
|
-
amount: input.collateralAmount + inflowFee.totalFee,
|
|
4152
4292
|
decimals: input.decimals,
|
|
4153
4293
|
collateralAmount: input.collateralAmount,
|
|
4154
|
-
inflowFeeAmount: inflowFee.totalFee,
|
|
4155
4294
|
asset: input.asset,
|
|
4156
|
-
|
|
4157
|
-
target: input.target,
|
|
4295
|
+
targets,
|
|
4158
4296
|
detectedTimestamp: input.detectedTimestamp,
|
|
4159
4297
|
expiryTimestamp: input.expiryTimestamp
|
|
4160
4298
|
};
|
|
4161
4299
|
}
|
|
4162
|
-
async
|
|
4300
|
+
async resolveInstantLoanInflowTargets(request) {
|
|
4301
|
+
const selectedPool = await getPoolById(
|
|
4302
|
+
this.canisterContext,
|
|
4303
|
+
request.poolId
|
|
4304
|
+
);
|
|
4305
|
+
const baseRequest = {
|
|
4306
|
+
profileId: request.profileId,
|
|
4307
|
+
poolId: request.poolId,
|
|
4308
|
+
action: request.action,
|
|
4309
|
+
mechanism: SupplyPlanType.transfer
|
|
4310
|
+
};
|
|
4311
|
+
const poolChainTarget = await resolveSupplyTargetForPool(
|
|
4312
|
+
this.canisterContext,
|
|
4313
|
+
baseRequest,
|
|
4314
|
+
selectedPool
|
|
4315
|
+
);
|
|
4316
|
+
const poolChain = poolChainTarget.chain;
|
|
4317
|
+
const targets = {
|
|
4318
|
+
[poolChain]: poolChainTarget
|
|
4319
|
+
};
|
|
4320
|
+
if (poolChain === Chain.ICP) {
|
|
4321
|
+
return { targets };
|
|
4322
|
+
}
|
|
4323
|
+
const icpTarget = await resolveSupplyTargetForPool(
|
|
4324
|
+
this.canisterContext,
|
|
4325
|
+
{ ...baseRequest, chain: Chain.ICP },
|
|
4326
|
+
selectedPool
|
|
4327
|
+
);
|
|
4328
|
+
targets[Chain.ICP] = icpTarget;
|
|
4329
|
+
return { targets };
|
|
4330
|
+
}
|
|
4331
|
+
async createRepaymentTargetQuotes(input) {
|
|
4332
|
+
const quotes = {};
|
|
4333
|
+
for (const chain of INSTANT_LOAN_CHAINS) {
|
|
4334
|
+
const target = input.targets[chain];
|
|
4335
|
+
if (!target) continue;
|
|
4336
|
+
assertTargetAsset(target, input.asset);
|
|
4337
|
+
const repaymentInflowFee = input.baseRepaymentAmount > 0n ? await this.estimateRepaymentInflowFee(target) : { totalFee: 0n, estimateAvailable: false };
|
|
4338
|
+
quotes[chain] = {
|
|
4339
|
+
amount: input.baseRepaymentAmount + repaymentInflowFee.totalFee,
|
|
4340
|
+
inflowFeeAmount: repaymentInflowFee.totalFee,
|
|
4341
|
+
inflowFeeEstimateAvailable: repaymentInflowFee.estimateAvailable,
|
|
4342
|
+
target
|
|
4343
|
+
};
|
|
4344
|
+
}
|
|
4345
|
+
return quotes;
|
|
4346
|
+
}
|
|
4347
|
+
async estimateRepaymentInflowFee(target) {
|
|
4163
4348
|
try {
|
|
4164
|
-
const fee = await this.lending.estimateInflowFee(
|
|
4165
|
-
asset,
|
|
4166
|
-
chain
|
|
4167
|
-
});
|
|
4349
|
+
const fee = await this.lending.estimateInflowFee(target);
|
|
4168
4350
|
return { totalFee: fee.totalFee, estimateAvailable: true };
|
|
4169
4351
|
} catch {
|
|
4170
4352
|
return {
|
|
4171
|
-
totalFee: getRepaymentInflowFeeFallback(asset, chain),
|
|
4353
|
+
totalFee: getRepaymentInflowFeeFallback(target.asset, target.chain),
|
|
4172
4354
|
estimateAvailable: false
|
|
4173
4355
|
};
|
|
4174
4356
|
}
|
|
@@ -4204,10 +4386,10 @@ var InstantLoansModule = class {
|
|
|
4204
4386
|
]);
|
|
4205
4387
|
const ltvCalculation = new QuoteModule().calculateLtv(
|
|
4206
4388
|
{
|
|
4207
|
-
borrowAmount: request.
|
|
4208
|
-
borrowPoolId: request.
|
|
4209
|
-
collateralAmount: request.
|
|
4210
|
-
collateralPoolId: request.
|
|
4389
|
+
borrowAmount: request.borrow.amount,
|
|
4390
|
+
borrowPoolId: request.borrow.poolId,
|
|
4391
|
+
collateralAmount: request.collateral.amount,
|
|
4392
|
+
collateralPoolId: request.collateral.poolId
|
|
4211
4393
|
},
|
|
4212
4394
|
pools,
|
|
4213
4395
|
assetPrices
|
|
@@ -4229,6 +4411,15 @@ function getRepaymentInflowFeeFallback(asset, chain) {
|
|
|
4229
4411
|
}
|
|
4230
4412
|
return 0n;
|
|
4231
4413
|
}
|
|
4414
|
+
function assertTargetAsset(target, expectedAsset) {
|
|
4415
|
+
if (target.asset === expectedAsset) {
|
|
4416
|
+
return;
|
|
4417
|
+
}
|
|
4418
|
+
throw new LiquidiumError(
|
|
4419
|
+
LiquidiumErrorCode.INTERNAL,
|
|
4420
|
+
`Instant loan pool target asset ${target.asset} does not match ${expectedAsset}`
|
|
4421
|
+
);
|
|
4422
|
+
}
|
|
4232
4423
|
function calculateInterestBufferAmount(borrowPosition, annualBorrowRate) {
|
|
4233
4424
|
const totalDebtAmount = calculateTotalDebtAmount(borrowPosition);
|
|
4234
4425
|
if (totalDebtAmount <= 0n || annualBorrowRate <= 0n) {
|
|
@@ -4321,13 +4512,23 @@ function deriveDepositExpiryTimestamp(input) {
|
|
|
4321
4512
|
return input.depositDetectedTimestamp + input.depositWindowSeconds;
|
|
4322
4513
|
}
|
|
4323
4514
|
function validateCreateRequest(request) {
|
|
4324
|
-
|
|
4515
|
+
getInstantLoanAssetIdentifier(
|
|
4516
|
+
request.borrow.asset,
|
|
4517
|
+
request.borrow.chain,
|
|
4518
|
+
"borrow"
|
|
4519
|
+
);
|
|
4520
|
+
getInstantLoanAssetIdentifier(
|
|
4521
|
+
request.collateral.asset,
|
|
4522
|
+
request.refund.chain,
|
|
4523
|
+
"refund"
|
|
4524
|
+
);
|
|
4525
|
+
if (request.collateral.amount <= 0n) {
|
|
4325
4526
|
throw new LiquidiumError(
|
|
4326
4527
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
4327
4528
|
"Instant loan collateral amount must be greater than zero"
|
|
4328
4529
|
);
|
|
4329
4530
|
}
|
|
4330
|
-
if (request.
|
|
4531
|
+
if (request.borrow.amount <= 0n) {
|
|
4331
4532
|
throw new LiquidiumError(
|
|
4332
4533
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
4333
4534
|
"Instant loan borrow amount must be greater than zero"
|
|
@@ -4352,45 +4553,200 @@ function throwLtvCalculationError(error) {
|
|
|
4352
4553
|
error?.message ?? "Unable to calculate instant loan LTV"
|
|
4353
4554
|
);
|
|
4354
4555
|
}
|
|
4355
|
-
function
|
|
4356
|
-
|
|
4357
|
-
|
|
4556
|
+
function inferInstantLoanDeliveryChain(destination, asset) {
|
|
4557
|
+
if (destination.type !== "ChainAddress") {
|
|
4558
|
+
return Chain.ICP;
|
|
4559
|
+
}
|
|
4560
|
+
if (asset === Asset.BTC) {
|
|
4561
|
+
return Chain.BTC;
|
|
4562
|
+
}
|
|
4563
|
+
if (asset === Asset.USDC || asset === Asset.USDT) {
|
|
4564
|
+
return Chain.ETH;
|
|
4565
|
+
}
|
|
4566
|
+
return Chain.ICP;
|
|
4567
|
+
}
|
|
4568
|
+
function resolveInstantLoanDestination(params) {
|
|
4569
|
+
getInstantLoanAssetIdentifier(params.asset, params.chain, params.role);
|
|
4570
|
+
if (typeof params.destination === "string") {
|
|
4571
|
+
return resolveInstantLoanStringDestination({
|
|
4572
|
+
destination: params.destination,
|
|
4573
|
+
asset: params.asset,
|
|
4574
|
+
role: params.role,
|
|
4575
|
+
chain: params.chain,
|
|
4576
|
+
normalizeExternalDestination: params.normalizeExternalDestination
|
|
4577
|
+
});
|
|
4578
|
+
}
|
|
4579
|
+
assertInstantLoanDestinationTypeMatchesChain({
|
|
4580
|
+
destinationType: params.destination.type,
|
|
4581
|
+
asset: params.asset,
|
|
4582
|
+
role: params.role,
|
|
4583
|
+
chain: params.chain
|
|
4584
|
+
});
|
|
4585
|
+
switch (params.destination.type) {
|
|
4586
|
+
case "ChainAddress":
|
|
4587
|
+
assertExternalInstantLoanDestinationSupported(params.asset);
|
|
4588
|
+
return {
|
|
4589
|
+
External: params.normalizeExternalDestination(
|
|
4590
|
+
normalizeInstantLoanDestinationAddress(params.destination.address),
|
|
4591
|
+
params.asset,
|
|
4592
|
+
params.chain
|
|
4593
|
+
)
|
|
4594
|
+
};
|
|
4595
|
+
case "IcPrincipal":
|
|
4596
|
+
return {
|
|
4597
|
+
Native: normalizeInstantLoanPrincipal(
|
|
4598
|
+
normalizeInstantLoanDestinationAddress(params.destination.address)
|
|
4599
|
+
)
|
|
4600
|
+
};
|
|
4601
|
+
case "IcpAccountIdentifier":
|
|
4602
|
+
return parseAccountIdentifierInstantLoanDestination(
|
|
4603
|
+
normalizeInstantLoanDestinationAddress(params.destination.address)
|
|
4604
|
+
);
|
|
4605
|
+
case "IcrcAccount":
|
|
4606
|
+
return parseIcrcInstantLoanDestination(
|
|
4607
|
+
normalizeInstantLoanDestinationAddress(params.destination.address)
|
|
4608
|
+
);
|
|
4609
|
+
}
|
|
4610
|
+
}
|
|
4611
|
+
function resolveInstantLoanStringDestination(params) {
|
|
4612
|
+
const address = normalizeInstantLoanDestinationAddress(params.destination);
|
|
4613
|
+
if (params.chain === Chain.ICP && params.asset !== Asset.ICP) {
|
|
4614
|
+
try {
|
|
4615
|
+
return parsePrincipalInstantLoanDestination(address);
|
|
4616
|
+
} catch {
|
|
4617
|
+
throwInvalidCkInstantLoanDestination(params.role);
|
|
4618
|
+
}
|
|
4619
|
+
}
|
|
4620
|
+
if (params.chain !== Chain.ICP) {
|
|
4621
|
+
assertExternalInstantLoanDestinationSupported(params.asset);
|
|
4622
|
+
return {
|
|
4623
|
+
External: params.normalizeExternalDestination(
|
|
4624
|
+
address,
|
|
4625
|
+
params.asset,
|
|
4626
|
+
params.chain
|
|
4627
|
+
)
|
|
4628
|
+
};
|
|
4629
|
+
}
|
|
4630
|
+
for (const parser of [
|
|
4631
|
+
parseAccountIdentifierInstantLoanDestination,
|
|
4632
|
+
parsePrincipalInstantLoanDestination,
|
|
4633
|
+
parseIcrcInstantLoanDestination
|
|
4634
|
+
]) {
|
|
4635
|
+
try {
|
|
4636
|
+
return parser(address);
|
|
4637
|
+
} catch {
|
|
4638
|
+
}
|
|
4639
|
+
}
|
|
4640
|
+
throwInvalidIcpInstantLoanDestination();
|
|
4641
|
+
}
|
|
4642
|
+
function getInstantLoanAssetIdentifier(asset, chain, role) {
|
|
4643
|
+
const identifier = { asset, chain };
|
|
4644
|
+
if (isAssetIdentifier(identifier)) {
|
|
4645
|
+
return identifier;
|
|
4646
|
+
}
|
|
4647
|
+
throw new LiquidiumError(
|
|
4648
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
4649
|
+
`${chain} instant loan ${role} delivery is not supported for ${asset}`
|
|
4650
|
+
);
|
|
4651
|
+
}
|
|
4652
|
+
function assertInstantLoanDestinationTypeMatchesChain(params) {
|
|
4653
|
+
if (params.chain === Chain.ICP && params.asset !== Asset.ICP) {
|
|
4654
|
+
if (params.destinationType === "IcPrincipal") {
|
|
4655
|
+
return;
|
|
4656
|
+
}
|
|
4657
|
+
throwInvalidCkInstantLoanDestination(params.role);
|
|
4658
|
+
}
|
|
4659
|
+
if (params.chain === Chain.ICP && params.asset === Asset.ICP) {
|
|
4660
|
+
if (params.destinationType !== "ChainAddress") {
|
|
4661
|
+
return;
|
|
4662
|
+
}
|
|
4663
|
+
throwInvalidIcpInstantLoanDestination();
|
|
4664
|
+
}
|
|
4665
|
+
if (params.destinationType === "ChainAddress") {
|
|
4666
|
+
return;
|
|
4667
|
+
}
|
|
4668
|
+
if (params.destinationType === "IcPrincipal") {
|
|
4358
4669
|
throw new LiquidiumError(
|
|
4359
4670
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
4360
|
-
|
|
4671
|
+
`${params.chain} instant loan ${params.role} destination must be an external chain address for ${params.asset}`
|
|
4361
4672
|
);
|
|
4362
4673
|
}
|
|
4363
|
-
|
|
4674
|
+
throw new LiquidiumError(
|
|
4675
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
4676
|
+
`${params.asset} instant loan ${params.role} destination only supports ChainAddress or IcPrincipal destinations`
|
|
4677
|
+
);
|
|
4364
4678
|
}
|
|
4365
|
-
function
|
|
4366
|
-
|
|
4367
|
-
|
|
4679
|
+
function throwInvalidCkInstantLoanDestination(role) {
|
|
4680
|
+
throw new LiquidiumError(
|
|
4681
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
4682
|
+
`ICP instant loan ${role} destination must be an IC principal`
|
|
4683
|
+
);
|
|
4684
|
+
}
|
|
4685
|
+
function assertExternalInstantLoanDestinationSupported(asset) {
|
|
4686
|
+
if (asset !== Asset.ICP) {
|
|
4687
|
+
return;
|
|
4368
4688
|
}
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4689
|
+
throwInvalidIcpInstantLoanDestination();
|
|
4690
|
+
}
|
|
4691
|
+
function throwInvalidIcpInstantLoanDestination() {
|
|
4692
|
+
throw new LiquidiumError(
|
|
4693
|
+
LiquidiumErrorCode.INVALID_ADDRESS,
|
|
4694
|
+
"ICP instant loan destination must be an IC principal, ICP account identifier, or ICRC account"
|
|
4695
|
+
);
|
|
4696
|
+
}
|
|
4697
|
+
function parsePrincipalInstantLoanDestination(address) {
|
|
4698
|
+
return { Native: normalizeInstantLoanPrincipal(address) };
|
|
4699
|
+
}
|
|
4700
|
+
function parseAccountIdentifierInstantLoanDestination(address) {
|
|
4701
|
+
if (address.length !== ICP_ACCOUNT_IDENTIFIER_HEX_LENGTH) {
|
|
4702
|
+
throw new LiquidiumError(
|
|
4703
|
+
LiquidiumErrorCode.INVALID_ADDRESS,
|
|
4704
|
+
"Invalid ICP account identifier"
|
|
4705
|
+
);
|
|
4374
4706
|
}
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
owner: account.Icrc.owner,
|
|
4383
|
-
subaccount
|
|
4384
|
-
})
|
|
4385
|
-
};
|
|
4707
|
+
try {
|
|
4708
|
+
return { AccountIdentifier: normalizeIcpAccountIdentifier(address) };
|
|
4709
|
+
} catch {
|
|
4710
|
+
throw new LiquidiumError(
|
|
4711
|
+
LiquidiumErrorCode.INVALID_ADDRESS,
|
|
4712
|
+
"Invalid ICP account identifier"
|
|
4713
|
+
);
|
|
4386
4714
|
}
|
|
4387
|
-
return { type: "External", address: account.External };
|
|
4388
4715
|
}
|
|
4389
|
-
function
|
|
4390
|
-
|
|
4391
|
-
|
|
4716
|
+
function parseIcrcInstantLoanDestination(address) {
|
|
4717
|
+
let decoded;
|
|
4718
|
+
try {
|
|
4719
|
+
decoded = decodeIcrcAccountAddress(address);
|
|
4720
|
+
} catch {
|
|
4721
|
+
throw new LiquidiumError(
|
|
4722
|
+
LiquidiumErrorCode.INVALID_ADDRESS,
|
|
4723
|
+
"Invalid instant loan ICRC destination"
|
|
4724
|
+
);
|
|
4392
4725
|
}
|
|
4393
|
-
return
|
|
4726
|
+
return { Icrc: decoded.account.address };
|
|
4727
|
+
}
|
|
4728
|
+
function normalizeInstantLoanPrincipal(address) {
|
|
4729
|
+
try {
|
|
4730
|
+
return Principal.fromText(address).toText();
|
|
4731
|
+
} catch {
|
|
4732
|
+
throw new LiquidiumError(
|
|
4733
|
+
LiquidiumErrorCode.INVALID_ADDRESS,
|
|
4734
|
+
"Invalid instant loan IC principal destination"
|
|
4735
|
+
);
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
4738
|
+
function normalizeInstantLoanDestinationAddress(address) {
|
|
4739
|
+
const trimmedAddress = address.trim();
|
|
4740
|
+
if (!trimmedAddress) {
|
|
4741
|
+
throw new LiquidiumError(
|
|
4742
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
4743
|
+
"Instant loan account address must be non-empty"
|
|
4744
|
+
);
|
|
4745
|
+
}
|
|
4746
|
+
return trimmedAddress;
|
|
4747
|
+
}
|
|
4748
|
+
function accountFromCanister(account) {
|
|
4749
|
+
return mapCanisterAccountToLiquidiumAccount(account);
|
|
4394
4750
|
}
|
|
4395
4751
|
function mapInstantLoanEvent(event) {
|
|
4396
4752
|
return {
|
|
@@ -4407,7 +4763,10 @@ function mapInstantLoanEventType(eventType) {
|
|
|
4407
4763
|
type: "LoanCreated",
|
|
4408
4764
|
loanId: event.loan_id,
|
|
4409
4765
|
borrowDestination: accountFromCanister(event.borrow_destination),
|
|
4410
|
-
collateralAsset:
|
|
4766
|
+
collateralAsset: parseInstantLoanAsset(
|
|
4767
|
+
event.lend_asset,
|
|
4768
|
+
"event collateral asset"
|
|
4769
|
+
),
|
|
4411
4770
|
borrowAmount: event.borrow_amount,
|
|
4412
4771
|
collateralPoolId: event.lend_pool_id.toText(),
|
|
4413
4772
|
refundDestination: accountFromCanister(event.refund_destination),
|
|
@@ -4415,7 +4774,10 @@ function mapInstantLoanEventType(eventType) {
|
|
|
4415
4774
|
depositWindowSeconds: event.ltv_timer_s,
|
|
4416
4775
|
profileId: event.lending_profile.toText(),
|
|
4417
4776
|
borrowPoolId: event.borrow_pool_id.toText(),
|
|
4418
|
-
borrowAsset:
|
|
4777
|
+
borrowAsset: parseInstantLoanAsset(
|
|
4778
|
+
event.borrow_asset,
|
|
4779
|
+
"event borrow asset"
|
|
4780
|
+
)
|
|
4419
4781
|
};
|
|
4420
4782
|
}
|
|
4421
4783
|
if ("FullLendWithdrawalRequested" in eventType) {
|
|
@@ -4582,6 +4944,12 @@ function mapCandidateWire(wire) {
|
|
|
4582
4944
|
})
|
|
4583
4945
|
};
|
|
4584
4946
|
}
|
|
4947
|
+
function parseInstantLoanAsset(value, label) {
|
|
4948
|
+
return parseApiStringUnion(value, INSTANT_LOAN_ASSETS, {
|
|
4949
|
+
context: INSTANT_LOAN_WIRE_CONTEXT,
|
|
4950
|
+
label
|
|
4951
|
+
});
|
|
4952
|
+
}
|
|
4585
4953
|
function mapInstantLoansErrorToLiquidiumError(error) {
|
|
4586
4954
|
const [key, payload] = Object.entries(error)[0];
|
|
4587
4955
|
switch (key) {
|
|
@@ -4723,11 +5091,25 @@ function encodeBytes32Hex(bytes) {
|
|
|
4723
5091
|
var idlFactory4 = ({ IDL }) => IDL.Service({
|
|
4724
5092
|
icrc1_fee: IDL.Func([], [IDL.Nat], ["query"])
|
|
4725
5093
|
});
|
|
4726
|
-
function
|
|
4727
|
-
|
|
5094
|
+
function createIcrcLedgerActor(params) {
|
|
5095
|
+
if (!params.canisterId) {
|
|
5096
|
+
throw new LiquidiumError(
|
|
5097
|
+
LiquidiumErrorCode.SERVICE_UNAVAILABLE,
|
|
5098
|
+
`${params.ledgerName} ledger canister ID is not configured`
|
|
5099
|
+
);
|
|
5100
|
+
}
|
|
4728
5101
|
return Actor.createActor(idlFactory4, {
|
|
4729
|
-
agent: canisterContext.agent,
|
|
4730
|
-
canisterId
|
|
5102
|
+
agent: params.canisterContext.agent,
|
|
5103
|
+
canisterId: params.canisterId
|
|
5104
|
+
});
|
|
5105
|
+
}
|
|
5106
|
+
|
|
5107
|
+
// src/core/canisters/ckbtc/ledger.ts
|
|
5108
|
+
function createCkBtcLedgerActor(canisterContext) {
|
|
5109
|
+
return createIcrcLedgerActor({
|
|
5110
|
+
canisterContext,
|
|
5111
|
+
canisterId: CK_CANISTER_IDS.BTC.ledger,
|
|
5112
|
+
ledgerName: "ckBTC"
|
|
4731
5113
|
});
|
|
4732
5114
|
}
|
|
4733
5115
|
|
|
@@ -4756,11 +5138,83 @@ function accountTypeToString(accountType) {
|
|
|
4756
5138
|
switch (accountType.type) {
|
|
4757
5139
|
case "External":
|
|
4758
5140
|
return `Address:${accountType.data}`;
|
|
5141
|
+
case "Icrc":
|
|
5142
|
+
return `Icrc:${accountType.data}`;
|
|
5143
|
+
case "AccountIdentifier":
|
|
5144
|
+
return `AccountId:${accountType.data}`;
|
|
4759
5145
|
case "Native":
|
|
4760
5146
|
return `Principal:${accountType.data}`;
|
|
4761
5147
|
}
|
|
4762
5148
|
}
|
|
4763
5149
|
|
|
5150
|
+
// src/core/pool-ledger-assets.ts
|
|
5151
|
+
function getPoolLedgerAssetRoute(params) {
|
|
5152
|
+
if (params.asset === Asset.BTC && (params.chain === Chain.BTC || params.chain === Chain.ICP)) {
|
|
5153
|
+
return {
|
|
5154
|
+
ledgerCanisterId: CK_CANISTER_IDS.BTC.ledger,
|
|
5155
|
+
asset: Asset.BTC,
|
|
5156
|
+
chain: params.chain
|
|
5157
|
+
};
|
|
5158
|
+
}
|
|
5159
|
+
if (params.asset === Asset.USDT && (params.chain === Chain.ETH || params.chain === Chain.ICP)) {
|
|
5160
|
+
return {
|
|
5161
|
+
ledgerCanisterId: CK_CANISTER_IDS.USDT.ledger,
|
|
5162
|
+
asset: Asset.USDT,
|
|
5163
|
+
chain: params.chain
|
|
5164
|
+
};
|
|
5165
|
+
}
|
|
5166
|
+
if (params.asset === Asset.USDC && (params.chain === Chain.ETH || params.chain === Chain.ICP)) {
|
|
5167
|
+
return {
|
|
5168
|
+
ledgerCanisterId: CK_CANISTER_IDS.USDC.ledger,
|
|
5169
|
+
asset: Asset.USDC,
|
|
5170
|
+
chain: params.chain
|
|
5171
|
+
};
|
|
5172
|
+
}
|
|
5173
|
+
if (params.asset === Asset.ICP && params.chain === Chain.ICP) {
|
|
5174
|
+
return {
|
|
5175
|
+
ledgerCanisterId: CK_CANISTER_IDS.ICP.ledger,
|
|
5176
|
+
asset: Asset.ICP,
|
|
5177
|
+
chain: Chain.ICP
|
|
5178
|
+
};
|
|
5179
|
+
}
|
|
5180
|
+
throw new LiquidiumError(
|
|
5181
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5182
|
+
`IC ledger transfers are not supported for ${params.asset} on ${params.chain}`
|
|
5183
|
+
);
|
|
5184
|
+
}
|
|
5185
|
+
|
|
5186
|
+
// src/core/withdraw-minimums.ts
|
|
5187
|
+
var MINIMUM_BTC_WITHDRAW_AMOUNT_SATS = 5000n;
|
|
5188
|
+
var MINIMUM_STABLECOIN_WITHDRAW_AMOUNT_BASE_UNITS = 1000000n;
|
|
5189
|
+
var MIN_WITHDRAW_AMOUNTS_BY_ASSET = {
|
|
5190
|
+
[Asset.BTC]: MINIMUM_BTC_WITHDRAW_AMOUNT_SATS,
|
|
5191
|
+
[Asset.USDC]: MINIMUM_STABLECOIN_WITHDRAW_AMOUNT_BASE_UNITS,
|
|
5192
|
+
[Asset.USDT]: MINIMUM_STABLECOIN_WITHDRAW_AMOUNT_BASE_UNITS
|
|
5193
|
+
};
|
|
5194
|
+
function getMinimumWithdrawAmount(asset) {
|
|
5195
|
+
if (!isMinimumWithdrawAsset(asset)) {
|
|
5196
|
+
return 0n;
|
|
5197
|
+
}
|
|
5198
|
+
return MIN_WITHDRAW_AMOUNTS_BY_ASSET[asset];
|
|
5199
|
+
}
|
|
5200
|
+
function getWithdrawAmountMinimumValidationError(params) {
|
|
5201
|
+
const minimumAmount = getMinimumWithdrawAmount(params.asset);
|
|
5202
|
+
if (minimumAmount <= 0n || params.amount >= minimumAmount) {
|
|
5203
|
+
return null;
|
|
5204
|
+
}
|
|
5205
|
+
return {
|
|
5206
|
+
asset: params.asset,
|
|
5207
|
+
minimumAmount,
|
|
5208
|
+
message: formatMinimumWithdrawAmountMessage(params.asset, minimumAmount)
|
|
5209
|
+
};
|
|
5210
|
+
}
|
|
5211
|
+
function formatMinimumWithdrawAmountMessage(asset, minimumAmount) {
|
|
5212
|
+
return `Withdraw amount must be at least ${minimumAmount} base units for ${asset}`;
|
|
5213
|
+
}
|
|
5214
|
+
function isMinimumWithdrawAsset(asset) {
|
|
5215
|
+
return Object.hasOwn(MIN_WITHDRAW_AMOUNTS_BY_ASSET, asset);
|
|
5216
|
+
}
|
|
5217
|
+
|
|
4764
5218
|
// src/modules/lending/_internal/inflow-fee-rounding.ts
|
|
4765
5219
|
var ETH_STABLECOIN_INFLOW_FEE_ROUND_UP_TO_NEAREST_10_CENTS = 100000n;
|
|
4766
5220
|
var BTC_INFLOW_FEE_ROUND_UP_TO_NEAREST_SATS = 500n;
|
|
@@ -4846,48 +5300,61 @@ var SupplyFlowExecutor = class {
|
|
|
4846
5300
|
}
|
|
4847
5301
|
params;
|
|
4848
5302
|
async create(request) {
|
|
4849
|
-
const
|
|
5303
|
+
const requestedMechanism = request.mechanism ?? null;
|
|
5304
|
+
if (requestedMechanism === null && request.walletAdapter === void 0 && (request.account !== void 0 || request.amount !== void 0)) {
|
|
5305
|
+
throw new LiquidiumError(
|
|
5306
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5307
|
+
"Wallet-executed supply requires walletAdapter, account, and amount"
|
|
5308
|
+
);
|
|
5309
|
+
}
|
|
5310
|
+
const selectedPool = await getPoolById(
|
|
4850
5311
|
this.params.canisterContext,
|
|
4851
|
-
request
|
|
5312
|
+
request.poolId
|
|
4852
5313
|
);
|
|
4853
|
-
const
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
5314
|
+
const target = await resolveSupplyTargetForPool(
|
|
5315
|
+
this.params.canisterContext,
|
|
5316
|
+
{
|
|
5317
|
+
profileId: request.profileId,
|
|
5318
|
+
poolId: request.poolId,
|
|
5319
|
+
action: request.action,
|
|
5320
|
+
mechanism: requestedMechanism,
|
|
5321
|
+
chain: request.chain
|
|
5322
|
+
},
|
|
5323
|
+
selectedPool
|
|
5324
|
+
);
|
|
5325
|
+
const mechanism = requestedMechanism === SupplyPlanType.contractInteraction ? SupplyPlanType.contractInteraction : SupplyPlanType.transfer;
|
|
4865
5326
|
const defaultSubmitInflowRequest = getDefaultSubmitInflowRequest({
|
|
4866
5327
|
action: request.action,
|
|
4867
|
-
chain: mechanism === SupplyPlanType.contractInteraction ? Chain.ETH :
|
|
5328
|
+
chain: mechanism === SupplyPlanType.contractInteraction ? Chain.ETH : target.chain
|
|
4868
5329
|
});
|
|
4869
5330
|
let txid;
|
|
4870
5331
|
switch (mechanism) {
|
|
4871
5332
|
case SupplyPlanType.transfer:
|
|
4872
|
-
if (request
|
|
4873
|
-
txid = await this.
|
|
5333
|
+
if (isWalletTransferSupplyRequest(request)) {
|
|
5334
|
+
txid = await this.sendAndSubmitTransferSupplyInflow({
|
|
4874
5335
|
request,
|
|
4875
|
-
|
|
5336
|
+
target,
|
|
4876
5337
|
defaultSubmitInflowRequest
|
|
4877
5338
|
});
|
|
4878
5339
|
}
|
|
4879
5340
|
break;
|
|
4880
5341
|
case SupplyPlanType.contractInteraction:
|
|
5342
|
+
if (!isContractInteractionSupplyRequest(request)) {
|
|
5343
|
+
throw new LiquidiumError(
|
|
5344
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5345
|
+
"Contract-interaction supply requires contract-interaction request fields"
|
|
5346
|
+
);
|
|
5347
|
+
}
|
|
4881
5348
|
txid = await this.executeContractSupply({
|
|
4882
5349
|
request,
|
|
4883
|
-
|
|
5350
|
+
target,
|
|
4884
5351
|
defaultSubmitInflowRequest
|
|
4885
5352
|
});
|
|
4886
5353
|
break;
|
|
4887
5354
|
}
|
|
4888
5355
|
return {
|
|
4889
5356
|
type: mechanism,
|
|
4890
|
-
target
|
|
5357
|
+
target,
|
|
4891
5358
|
txid,
|
|
4892
5359
|
status: createLiquidiumStatus({
|
|
4893
5360
|
operation: mapSupplyActionToStatusOperation(request.action),
|
|
@@ -4895,7 +5362,7 @@ var SupplyFlowExecutor = class {
|
|
|
4895
5362
|
}),
|
|
4896
5363
|
submit: async (submitRequest) => {
|
|
4897
5364
|
return await this.submitSupplyFlowInflow({
|
|
4898
|
-
|
|
5365
|
+
target,
|
|
4899
5366
|
mechanism,
|
|
4900
5367
|
defaultSubmitInflowRequest,
|
|
4901
5368
|
submitRequest
|
|
@@ -4904,7 +5371,10 @@ var SupplyFlowExecutor = class {
|
|
|
4904
5371
|
};
|
|
4905
5372
|
}
|
|
4906
5373
|
async getEvmSupplyContext(request) {
|
|
4907
|
-
const selectedPool = await
|
|
5374
|
+
const selectedPool = await getPoolById(
|
|
5375
|
+
this.params.canisterContext,
|
|
5376
|
+
request.poolId
|
|
5377
|
+
);
|
|
4908
5378
|
return await this.getEvmSupplyContextForPool({
|
|
4909
5379
|
request,
|
|
4910
5380
|
asset: selectedPool.asset,
|
|
@@ -4968,14 +5438,8 @@ var SupplyFlowExecutor = class {
|
|
|
4968
5438
|
approvalStrategy
|
|
4969
5439
|
};
|
|
4970
5440
|
}
|
|
4971
|
-
async
|
|
4972
|
-
const { request,
|
|
4973
|
-
if (instruction.target.type !== "nativeAddress") {
|
|
4974
|
-
throw new LiquidiumError(
|
|
4975
|
-
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
4976
|
-
"Wallet-executed supply requires a native-address target"
|
|
4977
|
-
);
|
|
4978
|
-
}
|
|
5441
|
+
async sendAndSubmitTransferSupplyInflow(params) {
|
|
5442
|
+
const { request, target, defaultSubmitInflowRequest } = params;
|
|
4979
5443
|
if (!request.walletAdapter) {
|
|
4980
5444
|
throw new LiquidiumError(
|
|
4981
5445
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
@@ -4995,16 +5459,27 @@ var SupplyFlowExecutor = class {
|
|
|
4995
5459
|
"Wallet-executed supply requires a positive amount"
|
|
4996
5460
|
);
|
|
4997
5461
|
}
|
|
4998
|
-
const txid = await this.
|
|
5462
|
+
const txid = target.chain === Chain.ICP ? await this.sendIcrcSupplyTransaction({
|
|
5463
|
+
walletAdapter: request.walletAdapter,
|
|
5464
|
+
asset: target.asset,
|
|
5465
|
+
chain: target.chain,
|
|
5466
|
+
transfer: {
|
|
5467
|
+
ledgerCanisterId: getPoolLedgerAssetRoute(target).ledgerCanisterId,
|
|
5468
|
+
to: decodeIcrcAccountAddress(target.address).account,
|
|
5469
|
+
amount: request.amount
|
|
5470
|
+
},
|
|
5471
|
+
senderAccount: account,
|
|
5472
|
+
action: request.action
|
|
5473
|
+
}) : await this.sendChainAddressSupplyTransaction({
|
|
4999
5474
|
walletAdapter: request.walletAdapter,
|
|
5000
|
-
chain:
|
|
5001
|
-
toAddress:
|
|
5475
|
+
chain: target.chain,
|
|
5476
|
+
toAddress: target.address,
|
|
5002
5477
|
amount: request.amount,
|
|
5003
5478
|
senderAccount: account,
|
|
5004
|
-
asset:
|
|
5479
|
+
asset: target.asset,
|
|
5005
5480
|
action: request.action
|
|
5006
5481
|
});
|
|
5007
|
-
if (shouldSubmitInflow({
|
|
5482
|
+
if (shouldSubmitInflow({ target, mechanism: SupplyPlanType.transfer })) {
|
|
5008
5483
|
try {
|
|
5009
5484
|
await this.submitInflowWithRetry(txid, defaultSubmitInflowRequest);
|
|
5010
5485
|
} catch {
|
|
@@ -5031,11 +5506,11 @@ var SupplyFlowExecutor = class {
|
|
|
5031
5506
|
});
|
|
5032
5507
|
}
|
|
5033
5508
|
async executeContractSupply(params) {
|
|
5034
|
-
const { request,
|
|
5035
|
-
if (
|
|
5509
|
+
const { request, target, defaultSubmitInflowRequest } = params;
|
|
5510
|
+
if (!isEthStablecoin(target.asset, target.chain)) {
|
|
5036
5511
|
throw new LiquidiumError(
|
|
5037
5512
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5038
|
-
"Contract-interaction supply is only supported for ETH
|
|
5513
|
+
"Contract-interaction supply is only supported for ETH stablecoin targets"
|
|
5039
5514
|
);
|
|
5040
5515
|
}
|
|
5041
5516
|
const walletAddressInput = request.account?.trim();
|
|
@@ -5074,8 +5549,8 @@ var SupplyFlowExecutor = class {
|
|
|
5074
5549
|
amount: request.amount,
|
|
5075
5550
|
action: request.action
|
|
5076
5551
|
},
|
|
5077
|
-
asset:
|
|
5078
|
-
chain:
|
|
5552
|
+
asset: target.asset,
|
|
5553
|
+
chain: target.chain
|
|
5079
5554
|
});
|
|
5080
5555
|
const supplyAmount = BigInt(evmSupplyContext.amount);
|
|
5081
5556
|
if (BigInt(evmSupplyContext.balance) < supplyAmount) {
|
|
@@ -5131,7 +5606,7 @@ var SupplyFlowExecutor = class {
|
|
|
5131
5606
|
amount: supplyAmount,
|
|
5132
5607
|
poolId: request.poolId,
|
|
5133
5608
|
profileId: request.profileId,
|
|
5134
|
-
destinationAccount:
|
|
5609
|
+
destinationAccount: target.address,
|
|
5135
5610
|
action: request.action
|
|
5136
5611
|
}),
|
|
5137
5612
|
`supply-${request.action}-deposit-erc20`
|
|
@@ -5142,7 +5617,7 @@ var SupplyFlowExecutor = class {
|
|
|
5142
5617
|
}
|
|
5143
5618
|
return depositTxid;
|
|
5144
5619
|
}
|
|
5145
|
-
async
|
|
5620
|
+
async sendChainAddressSupplyTransaction(params) {
|
|
5146
5621
|
switch (params.chain) {
|
|
5147
5622
|
case Chain.BTC: {
|
|
5148
5623
|
if (!params.walletAdapter.sendBtcTransaction) {
|
|
@@ -5156,8 +5631,7 @@ var SupplyFlowExecutor = class {
|
|
|
5156
5631
|
toAddress: params.toAddress,
|
|
5157
5632
|
amountSats: params.amount,
|
|
5158
5633
|
account: params.senderAccount,
|
|
5159
|
-
actionType: `supply-${params.action}
|
|
5160
|
-
transferMode: TransferMode.native
|
|
5634
|
+
actionType: `supply-${params.action}`
|
|
5161
5635
|
});
|
|
5162
5636
|
}
|
|
5163
5637
|
case Chain.ETH: {
|
|
@@ -5172,7 +5646,6 @@ var SupplyFlowExecutor = class {
|
|
|
5172
5646
|
chain: Chain.ETH,
|
|
5173
5647
|
account: params.senderAccount,
|
|
5174
5648
|
actionType: `supply-${params.action}`,
|
|
5175
|
-
transferMode: TransferMode.native,
|
|
5176
5649
|
transaction: createTransferErc20Transaction({
|
|
5177
5650
|
tokenAddress: getEthStablecoinContractAddress(params.asset),
|
|
5178
5651
|
recipientAddress: params.toAddress,
|
|
@@ -5184,7 +5657,6 @@ var SupplyFlowExecutor = class {
|
|
|
5184
5657
|
chain: Chain.ETH,
|
|
5185
5658
|
account: params.senderAccount,
|
|
5186
5659
|
actionType: `supply-${params.action}`,
|
|
5187
|
-
transferMode: TransferMode.native,
|
|
5188
5660
|
transaction: {
|
|
5189
5661
|
to: params.toAddress,
|
|
5190
5662
|
value: params.amount.toString()
|
|
@@ -5194,10 +5666,32 @@ var SupplyFlowExecutor = class {
|
|
|
5194
5666
|
default:
|
|
5195
5667
|
throw new LiquidiumError(
|
|
5196
5668
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5197
|
-
`
|
|
5669
|
+
`Chain-address wallet execution is not supported for ${params.chain}`
|
|
5198
5670
|
);
|
|
5199
5671
|
}
|
|
5200
5672
|
}
|
|
5673
|
+
async sendIcrcSupplyTransaction(params) {
|
|
5674
|
+
if (!params.walletAdapter.sendIcrcTransfer) {
|
|
5675
|
+
throw new LiquidiumError(
|
|
5676
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5677
|
+
"ICRC wallet adapter does not support ledger transfers"
|
|
5678
|
+
);
|
|
5679
|
+
}
|
|
5680
|
+
const route = getPoolLedgerAssetRoute({
|
|
5681
|
+
asset: params.asset,
|
|
5682
|
+
chain: params.chain
|
|
5683
|
+
});
|
|
5684
|
+
return await params.walletAdapter.sendIcrcTransfer({
|
|
5685
|
+
chain: Chain.ICP,
|
|
5686
|
+
asset: route.asset,
|
|
5687
|
+
transfer: {
|
|
5688
|
+
...params.transfer,
|
|
5689
|
+
ledgerCanisterId: route.ledgerCanisterId
|
|
5690
|
+
},
|
|
5691
|
+
account: params.senderAccount,
|
|
5692
|
+
actionType: `supply-${params.action}`
|
|
5693
|
+
});
|
|
5694
|
+
}
|
|
5201
5695
|
async submitInflowWithRetry(txid, extraRequest) {
|
|
5202
5696
|
if (!extraRequest) {
|
|
5203
5697
|
throw new LiquidiumError(
|
|
@@ -5226,7 +5720,6 @@ var SupplyFlowExecutor = class {
|
|
|
5226
5720
|
chain: Chain.ETH,
|
|
5227
5721
|
account: walletAddress,
|
|
5228
5722
|
actionType,
|
|
5229
|
-
transferMode: TransferMode.native,
|
|
5230
5723
|
transaction: request
|
|
5231
5724
|
});
|
|
5232
5725
|
}
|
|
@@ -5315,13 +5808,19 @@ function getUnknownErrorMessage(error) {
|
|
|
5315
5808
|
}
|
|
5316
5809
|
function getDefaultSubmitInflowRequest(params) {
|
|
5317
5810
|
if (params.chain !== Chain.BTC && params.chain !== Chain.ETH) {
|
|
5318
|
-
return
|
|
5811
|
+
return null;
|
|
5319
5812
|
}
|
|
5320
5813
|
return {
|
|
5321
5814
|
chain: params.chain,
|
|
5322
5815
|
operation: mapSupplyActionToStatusOperation(params.action)
|
|
5323
5816
|
};
|
|
5324
5817
|
}
|
|
5818
|
+
function isWalletTransferSupplyRequest(request) {
|
|
5819
|
+
return request.mechanism !== SupplyPlanType.contractInteraction && request.walletAdapter !== void 0;
|
|
5820
|
+
}
|
|
5821
|
+
function isContractInteractionSupplyRequest(request) {
|
|
5822
|
+
return request.mechanism === SupplyPlanType.contractInteraction;
|
|
5823
|
+
}
|
|
5325
5824
|
function mapSupplyActionToStatusOperation(action) {
|
|
5326
5825
|
return action === SupplyAction.repayment ? "repayment" : "deposit";
|
|
5327
5826
|
}
|
|
@@ -5329,7 +5828,10 @@ function shouldSubmitInflow(params) {
|
|
|
5329
5828
|
if (params.mechanism !== SupplyPlanType.transfer) {
|
|
5330
5829
|
return true;
|
|
5331
5830
|
}
|
|
5332
|
-
|
|
5831
|
+
if (params.target.chain === Chain.ICP) {
|
|
5832
|
+
return false;
|
|
5833
|
+
}
|
|
5834
|
+
return !isEthStablecoin(params.target.asset, params.target.chain);
|
|
5333
5835
|
}
|
|
5334
5836
|
function mapCanisterOutflowDetails(outflow) {
|
|
5335
5837
|
const rawOutflowType = getVariantKey(outflow.outflow_type);
|
|
@@ -5343,41 +5845,31 @@ function mapCanisterOutflowDetails(outflow) {
|
|
|
5343
5845
|
};
|
|
5344
5846
|
}
|
|
5345
5847
|
function mapCanisterAccountType(receiver) {
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
if ("Icrc" in receiver) {
|
|
5359
|
-
const subaccount = normalizeOptionalSubaccount2(receiver.Icrc.subaccount[0]);
|
|
5360
|
-
return {
|
|
5361
|
-
type: "Icrc",
|
|
5362
|
-
owner: receiver.Icrc.owner.toText(),
|
|
5363
|
-
subaccount,
|
|
5364
|
-
account: encodeIcrcAccount({
|
|
5365
|
-
owner: receiver.Icrc.owner,
|
|
5366
|
-
subaccount
|
|
5367
|
-
})
|
|
5368
|
-
};
|
|
5848
|
+
return mapCanisterAccountToLiquidiumAccount(receiver);
|
|
5849
|
+
}
|
|
5850
|
+
function parseOutflowDestination(params) {
|
|
5851
|
+
const destination = normalizeOutflowDestinationInput(params.destination);
|
|
5852
|
+
const parsedAccount = destination.type ? parseOutflowDestinationWithHint(destination) : parseOutflowDestinationAutomatically(destination.address);
|
|
5853
|
+
assertDestinationTypeSupportedByChain({
|
|
5854
|
+
accountType: parsedAccount.accountType,
|
|
5855
|
+
poolChain: params.poolChain,
|
|
5856
|
+
destinationChain: params.destinationChain
|
|
5857
|
+
});
|
|
5858
|
+
if (parsedAccount.accountType !== "ChainAddress") {
|
|
5859
|
+
return parsedAccount;
|
|
5369
5860
|
}
|
|
5861
|
+
const externalAddress = normalizeExternalAddress({
|
|
5862
|
+
address: parsedAccount.address,
|
|
5863
|
+
asset: params.asset,
|
|
5864
|
+
chain: params.destinationChain
|
|
5865
|
+
});
|
|
5370
5866
|
return {
|
|
5371
|
-
|
|
5372
|
-
|
|
5867
|
+
address: externalAddress,
|
|
5868
|
+
accountType: "ChainAddress",
|
|
5869
|
+
canisterAccount: { External: externalAddress },
|
|
5870
|
+
messageAccount: { type: "External", data: externalAddress }
|
|
5373
5871
|
};
|
|
5374
5872
|
}
|
|
5375
|
-
function normalizeOptionalSubaccount2(subaccount) {
|
|
5376
|
-
if (!subaccount) {
|
|
5377
|
-
return void 0;
|
|
5378
|
-
}
|
|
5379
|
-
return subaccount instanceof Uint8Array ? subaccount : Uint8Array.from(subaccount);
|
|
5380
|
-
}
|
|
5381
5873
|
function normalizeOutflowType(rawOutflowType) {
|
|
5382
5874
|
switch (rawOutflowType) {
|
|
5383
5875
|
case "Withdraw":
|
|
@@ -5400,6 +5892,117 @@ function mapWalletChainToLendingChain(chain) {
|
|
|
5400
5892
|
case Chain.ETH:
|
|
5401
5893
|
return { ETH: null };
|
|
5402
5894
|
}
|
|
5895
|
+
throw new LiquidiumError(
|
|
5896
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5897
|
+
`Message signing is not supported for ${String(chain)}`
|
|
5898
|
+
);
|
|
5899
|
+
}
|
|
5900
|
+
function normalizeOutflowDestinationInput(destination) {
|
|
5901
|
+
if (typeof destination === "string") {
|
|
5902
|
+
return { address: destination, type: null };
|
|
5903
|
+
}
|
|
5904
|
+
return destination;
|
|
5905
|
+
}
|
|
5906
|
+
function parseOutflowDestinationWithHint(destination) {
|
|
5907
|
+
try {
|
|
5908
|
+
switch (destination.type) {
|
|
5909
|
+
case "ChainAddress":
|
|
5910
|
+
return parseExternalDestination(destination.address);
|
|
5911
|
+
case "IcPrincipal":
|
|
5912
|
+
return parseIcPrincipalDestination(destination.address);
|
|
5913
|
+
case "IcrcAccount":
|
|
5914
|
+
return parseIcrcDestination(destination.address);
|
|
5915
|
+
case "IcpAccountIdentifier":
|
|
5916
|
+
return parseAccountIdentifierDestination(destination.address);
|
|
5917
|
+
default:
|
|
5918
|
+
throw new LiquidiumError(
|
|
5919
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5920
|
+
`Unsupported outflow account type: ${String(destination.type)}`
|
|
5921
|
+
);
|
|
5922
|
+
}
|
|
5923
|
+
} catch (error) {
|
|
5924
|
+
if (error instanceof LiquidiumError) {
|
|
5925
|
+
throw error;
|
|
5926
|
+
}
|
|
5927
|
+
throw new LiquidiumError(
|
|
5928
|
+
LiquidiumErrorCode.INVALID_ADDRESS,
|
|
5929
|
+
`Invalid ${destination.type} outflow destination`
|
|
5930
|
+
);
|
|
5931
|
+
}
|
|
5932
|
+
}
|
|
5933
|
+
function parseOutflowDestinationAutomatically(address) {
|
|
5934
|
+
const parsers = [
|
|
5935
|
+
parseAccountIdentifierDestination,
|
|
5936
|
+
parseIcPrincipalDestination,
|
|
5937
|
+
parseIcrcDestination
|
|
5938
|
+
];
|
|
5939
|
+
for (const parser of parsers) {
|
|
5940
|
+
try {
|
|
5941
|
+
return parser(address);
|
|
5942
|
+
} catch {
|
|
5943
|
+
}
|
|
5944
|
+
}
|
|
5945
|
+
return parseExternalDestination(address);
|
|
5946
|
+
}
|
|
5947
|
+
function parseExternalDestination(address) {
|
|
5948
|
+
return {
|
|
5949
|
+
address,
|
|
5950
|
+
accountType: "ChainAddress",
|
|
5951
|
+
canisterAccount: { External: address },
|
|
5952
|
+
messageAccount: { type: "External", data: address }
|
|
5953
|
+
};
|
|
5954
|
+
}
|
|
5955
|
+
function parseIcPrincipalDestination(address) {
|
|
5956
|
+
const principal = Principal.fromText(address);
|
|
5957
|
+
const principalText = principal.toText();
|
|
5958
|
+
return {
|
|
5959
|
+
address: principalText,
|
|
5960
|
+
accountType: "IcPrincipal",
|
|
5961
|
+
canisterAccount: { Native: principal },
|
|
5962
|
+
messageAccount: { type: "Native", data: principalText }
|
|
5963
|
+
};
|
|
5964
|
+
}
|
|
5965
|
+
function parseIcrcDestination(address) {
|
|
5966
|
+
const decoded = decodeIcrcAccountAddress(address);
|
|
5967
|
+
return {
|
|
5968
|
+
address: decoded.account.address,
|
|
5969
|
+
accountType: "IcrcAccount",
|
|
5970
|
+
canisterAccount: {
|
|
5971
|
+
Icrc: {
|
|
5972
|
+
owner: decoded.owner,
|
|
5973
|
+
subaccount: decoded.subaccount ? [decoded.subaccount] : []
|
|
5974
|
+
}
|
|
5975
|
+
},
|
|
5976
|
+
messageAccount: { type: "Icrc", data: decoded.account.address }
|
|
5977
|
+
};
|
|
5978
|
+
}
|
|
5979
|
+
function parseAccountIdentifierDestination(address) {
|
|
5980
|
+
const accountIdentifier = normalizeIcpAccountIdentifier(address);
|
|
5981
|
+
return {
|
|
5982
|
+
address: accountIdentifier,
|
|
5983
|
+
accountType: "IcpAccountIdentifier",
|
|
5984
|
+
canisterAccount: { AccountIdentifier: accountIdentifier },
|
|
5985
|
+
messageAccount: { type: "AccountIdentifier", data: accountIdentifier }
|
|
5986
|
+
};
|
|
5987
|
+
}
|
|
5988
|
+
function assertDestinationTypeSupportedByChain(params) {
|
|
5989
|
+
if (params.poolChain === Chain.BTC || params.poolChain === Chain.ETH) {
|
|
5990
|
+
if (params.destinationChain === params.poolChain && params.accountType === "ChainAddress") {
|
|
5991
|
+
return;
|
|
5992
|
+
}
|
|
5993
|
+
if (params.destinationChain === Chain.ICP && params.accountType === "IcPrincipal") {
|
|
5994
|
+
return;
|
|
5995
|
+
}
|
|
5996
|
+
}
|
|
5997
|
+
if (params.poolChain === Chain.ICP && params.destinationChain === Chain.ICP) {
|
|
5998
|
+
if (params.accountType === "IcpAccountIdentifier" || params.accountType === "IcrcAccount" || params.accountType === "IcPrincipal") {
|
|
5999
|
+
return;
|
|
6000
|
+
}
|
|
6001
|
+
}
|
|
6002
|
+
throw new LiquidiumError(
|
|
6003
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
6004
|
+
"Target pool does not support this address type"
|
|
6005
|
+
);
|
|
5403
6006
|
}
|
|
5404
6007
|
|
|
5405
6008
|
// src/modules/lending/lending.ts
|
|
@@ -5421,56 +6024,86 @@ var LendingModule = class {
|
|
|
5421
6024
|
* @returns A signable {@link WithdrawAction} with `submit` wired to the canister.
|
|
5422
6025
|
*/
|
|
5423
6026
|
async prepareWithdraw(request) {
|
|
5424
|
-
const
|
|
6027
|
+
const destination = resolveOutflowDestinationInput({
|
|
6028
|
+
receiver: request.receiver,
|
|
6029
|
+
errorMessage: "Withdraw requires a custom outflow account"
|
|
6030
|
+
});
|
|
5425
6031
|
const signerAccount = normalizeEvmAddress(
|
|
5426
6032
|
request.signerWalletAddress.trim()
|
|
5427
6033
|
);
|
|
5428
|
-
if (!
|
|
6034
|
+
if (!signerAccount) {
|
|
5429
6035
|
throw new LiquidiumError(
|
|
5430
6036
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5431
|
-
"Withdraw requires a
|
|
6037
|
+
"Withdraw requires a signer account"
|
|
5432
6038
|
);
|
|
5433
6039
|
}
|
|
5434
|
-
if (
|
|
6040
|
+
if (request.amount <= 0n) {
|
|
5435
6041
|
throw new LiquidiumError(
|
|
5436
6042
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5437
|
-
"Withdraw
|
|
6043
|
+
"Withdraw amount must be greater than 0"
|
|
5438
6044
|
);
|
|
5439
6045
|
}
|
|
5440
|
-
const
|
|
5441
|
-
|
|
5442
|
-
|
|
6046
|
+
const selectedPool = await getPoolById(
|
|
6047
|
+
this.canisterContext,
|
|
6048
|
+
request.poolId
|
|
6049
|
+
);
|
|
6050
|
+
const selectedAsset = selectedPool.asset;
|
|
6051
|
+
const minimumWithdrawAmountError = getWithdrawAmountMinimumValidationError({
|
|
6052
|
+
amount: request.amount,
|
|
6053
|
+
asset: selectedAsset
|
|
6054
|
+
});
|
|
6055
|
+
if (minimumWithdrawAmountError) {
|
|
6056
|
+
throw new LiquidiumError(
|
|
6057
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
6058
|
+
minimumWithdrawAmountError.message
|
|
6059
|
+
);
|
|
6060
|
+
}
|
|
6061
|
+
const receiver = parseOutflowDestination({
|
|
6062
|
+
destination,
|
|
6063
|
+
asset: selectedAsset,
|
|
6064
|
+
poolChain: selectedPool.chain,
|
|
6065
|
+
destinationChain: request.chain
|
|
5443
6066
|
});
|
|
5444
6067
|
const lendingActor = createLendingActor(this.canisterContext);
|
|
5445
6068
|
try {
|
|
5446
6069
|
const expiryTimestamp = computeExpiryTimestampFromNow();
|
|
5447
6070
|
const nonce = await lendingActor.get_nonce(signerAccount);
|
|
5448
|
-
const
|
|
6071
|
+
const withdrawActionData = {
|
|
5449
6072
|
profileId: request.profileId,
|
|
5450
6073
|
poolId: request.poolId,
|
|
5451
6074
|
amount: request.amount,
|
|
5452
|
-
|
|
6075
|
+
chain: request.chain,
|
|
6076
|
+
receiver: {
|
|
6077
|
+
address: receiver.address,
|
|
6078
|
+
type: receiver.accountType
|
|
6079
|
+
},
|
|
5453
6080
|
signerWalletAddress: signerAccount,
|
|
5454
6081
|
expiryTimestamp
|
|
5455
6082
|
};
|
|
6083
|
+
const withdrawSubmissionData = {
|
|
6084
|
+
...withdrawActionData,
|
|
6085
|
+
receiverAccount: receiver.canisterAccount
|
|
6086
|
+
};
|
|
5456
6087
|
return {
|
|
5457
6088
|
kind: WalletActionKind.createWithdraw,
|
|
5458
6089
|
executionKind: WalletExecutionKind.signMessage,
|
|
5459
6090
|
actionType: WalletActionKind.createWithdraw,
|
|
5460
|
-
transferMode: TransferMode.native,
|
|
5461
6091
|
account: signerAccount,
|
|
5462
6092
|
message: createWithdrawAssetMessage(
|
|
5463
6093
|
{
|
|
5464
6094
|
pool_id: request.poolId,
|
|
5465
6095
|
amount: request.amount.toString(),
|
|
5466
|
-
account:
|
|
6096
|
+
account: receiver.messageAccount,
|
|
5467
6097
|
expiry_timestamp: expiryTimestamp
|
|
5468
6098
|
},
|
|
5469
6099
|
nonce
|
|
5470
6100
|
),
|
|
5471
|
-
data:
|
|
6101
|
+
data: withdrawActionData,
|
|
5472
6102
|
submit: async (signatureInfo) => {
|
|
5473
|
-
return await this.submitWithdraw(
|
|
6103
|
+
return await this.submitWithdraw(
|
|
6104
|
+
withdrawSubmissionData,
|
|
6105
|
+
signatureInfo
|
|
6106
|
+
);
|
|
5474
6107
|
}
|
|
5475
6108
|
};
|
|
5476
6109
|
} catch (error) {
|
|
@@ -5487,7 +6120,7 @@ var LendingModule = class {
|
|
|
5487
6120
|
{
|
|
5488
6121
|
data: {
|
|
5489
6122
|
expiry_timestamp: request.expiryTimestamp,
|
|
5490
|
-
account:
|
|
6123
|
+
account: request.receiverAccount,
|
|
5491
6124
|
pool_id: Principal.fromText(request.poolId),
|
|
5492
6125
|
amount: request.amount
|
|
5493
6126
|
},
|
|
@@ -5543,16 +6176,13 @@ var LendingModule = class {
|
|
|
5543
6176
|
* @returns A signable {@link BorrowAction} with `submit` wired to the canister.
|
|
5544
6177
|
*/
|
|
5545
6178
|
async prepareBorrow(request) {
|
|
5546
|
-
const
|
|
6179
|
+
const destination = resolveOutflowDestinationInput({
|
|
6180
|
+
receiver: request.receiver,
|
|
6181
|
+
errorMessage: "Borrow requires a custom outflow account"
|
|
6182
|
+
});
|
|
5547
6183
|
const signerAccount = normalizeEvmAddress(
|
|
5548
6184
|
request.signerWalletAddress.trim()
|
|
5549
6185
|
);
|
|
5550
|
-
if (!destinationAccount) {
|
|
5551
|
-
throw new LiquidiumError(
|
|
5552
|
-
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
5553
|
-
"Borrow requires a custom outflow account"
|
|
5554
|
-
);
|
|
5555
|
-
}
|
|
5556
6186
|
if (!signerAccount) {
|
|
5557
6187
|
throw new LiquidiumError(
|
|
5558
6188
|
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
@@ -5565,7 +6195,10 @@ var LendingModule = class {
|
|
|
5565
6195
|
"Borrow amount must be greater than 0"
|
|
5566
6196
|
);
|
|
5567
6197
|
}
|
|
5568
|
-
const selectedPool = await
|
|
6198
|
+
const selectedPool = await getPoolById(
|
|
6199
|
+
this.canisterContext,
|
|
6200
|
+
request.poolId
|
|
6201
|
+
);
|
|
5569
6202
|
const selectedAsset = selectedPool.asset;
|
|
5570
6203
|
const minimumBorrowAmountError = getBorrowAmountMinimumValidationError({
|
|
5571
6204
|
amount: request.amount,
|
|
@@ -5577,41 +6210,49 @@ var LendingModule = class {
|
|
|
5577
6210
|
minimumBorrowAmountError.message
|
|
5578
6211
|
);
|
|
5579
6212
|
}
|
|
5580
|
-
const
|
|
5581
|
-
|
|
6213
|
+
const receiver = parseOutflowDestination({
|
|
6214
|
+
destination,
|
|
5582
6215
|
asset: selectedAsset,
|
|
5583
|
-
|
|
6216
|
+
poolChain: selectedPool.chain,
|
|
6217
|
+
destinationChain: request.chain
|
|
5584
6218
|
});
|
|
5585
6219
|
const lendingActor = createLendingActor(this.canisterContext);
|
|
5586
6220
|
try {
|
|
5587
6221
|
const expiryTimestamp = computeExpiryTimestampFromNow();
|
|
5588
6222
|
const nonce = await lendingActor.get_nonce(signerAccount);
|
|
5589
|
-
const
|
|
6223
|
+
const borrowActionData = {
|
|
5590
6224
|
profileId: request.profileId,
|
|
5591
6225
|
poolId: request.poolId,
|
|
5592
6226
|
amount: request.amount,
|
|
5593
|
-
|
|
6227
|
+
chain: request.chain,
|
|
6228
|
+
receiver: {
|
|
6229
|
+
address: receiver.address,
|
|
6230
|
+
type: receiver.accountType
|
|
6231
|
+
},
|
|
5594
6232
|
signerWalletAddress: signerAccount,
|
|
5595
6233
|
expiryTimestamp
|
|
5596
6234
|
};
|
|
6235
|
+
const borrowSubmissionData = {
|
|
6236
|
+
...borrowActionData,
|
|
6237
|
+
receiverAccount: receiver.canisterAccount
|
|
6238
|
+
};
|
|
5597
6239
|
return {
|
|
5598
6240
|
kind: WalletActionKind.createBorrow,
|
|
5599
6241
|
executionKind: WalletExecutionKind.signMessage,
|
|
5600
6242
|
actionType: WalletActionKind.createBorrow,
|
|
5601
|
-
transferMode: TransferMode.native,
|
|
5602
6243
|
account: signerAccount,
|
|
5603
6244
|
message: createBorrowAssetMessage(
|
|
5604
6245
|
{
|
|
5605
6246
|
pool_id: request.poolId,
|
|
5606
6247
|
amount: request.amount.toString(),
|
|
5607
|
-
account:
|
|
6248
|
+
account: receiver.messageAccount,
|
|
5608
6249
|
expiry_timestamp: expiryTimestamp
|
|
5609
6250
|
},
|
|
5610
6251
|
nonce
|
|
5611
6252
|
),
|
|
5612
|
-
data:
|
|
6253
|
+
data: borrowActionData,
|
|
5613
6254
|
submit: async (signatureInfo) => {
|
|
5614
|
-
return await this.submitBorrow(
|
|
6255
|
+
return await this.submitBorrow(borrowSubmissionData, signatureInfo);
|
|
5615
6256
|
}
|
|
5616
6257
|
};
|
|
5617
6258
|
} catch (error) {
|
|
@@ -5628,7 +6269,7 @@ var LendingModule = class {
|
|
|
5628
6269
|
).borrow_assets(Principal.fromText(request.profileId), {
|
|
5629
6270
|
data: {
|
|
5630
6271
|
expiry_timestamp: request.expiryTimestamp,
|
|
5631
|
-
account:
|
|
6272
|
+
account: request.receiverAccount,
|
|
5632
6273
|
pool_id: Principal.fromText(request.poolId),
|
|
5633
6274
|
amount: request.amount
|
|
5634
6275
|
},
|
|
@@ -5746,11 +6387,18 @@ var LendingModule = class {
|
|
|
5746
6387
|
*
|
|
5747
6388
|
* ETH stablecoin deposit-address estimates are served by the deposit-address
|
|
5748
6389
|
* canister. BTC estimates include the ckBTC minter deposit fee and ledger fee.
|
|
6390
|
+
* ICP-chain estimates return the corresponding ICRC ledger fee.
|
|
5749
6391
|
*
|
|
5750
6392
|
* @param request - Asset and chain pair to estimate for.
|
|
5751
6393
|
* @returns Total fee estimate rounded up in the asset's base units.
|
|
5752
6394
|
*/
|
|
5753
6395
|
async estimateInflowFee(request) {
|
|
6396
|
+
if (request.chain === Chain.ICP) {
|
|
6397
|
+
const ledgerFee = await this.estimateIcrcLedgerFee(request);
|
|
6398
|
+
return {
|
|
6399
|
+
totalFee: roundInflowFeeEstimate(request, ledgerFee)
|
|
6400
|
+
};
|
|
6401
|
+
}
|
|
5754
6402
|
if (isEthStablecoin(request.asset, request.chain)) {
|
|
5755
6403
|
const result = await createDepositAccountsActor(
|
|
5756
6404
|
this.canisterContext
|
|
@@ -5780,6 +6428,14 @@ var LendingModule = class {
|
|
|
5780
6428
|
]);
|
|
5781
6429
|
return { totalFee: minterFee + ledgerFee };
|
|
5782
6430
|
}
|
|
6431
|
+
async estimateIcrcLedgerFee(request) {
|
|
6432
|
+
const route = getInflowFeeLedgerAssetRoute(request);
|
|
6433
|
+
return await createIcrcLedgerActor({
|
|
6434
|
+
canisterContext: this.canisterContext,
|
|
6435
|
+
canisterId: route.ledgerCanisterId,
|
|
6436
|
+
ledgerName: `${request.asset} ${request.chain}`
|
|
6437
|
+
}).icrc1_fee();
|
|
6438
|
+
}
|
|
5783
6439
|
/**
|
|
5784
6440
|
* Submits an inflow transaction id for faster indexing.
|
|
5785
6441
|
*
|
|
@@ -5789,6 +6445,12 @@ var LendingModule = class {
|
|
|
5789
6445
|
* @returns Acknowledgement including the submitted `txid`.
|
|
5790
6446
|
*/
|
|
5791
6447
|
async submitInflow(request) {
|
|
6448
|
+
if (request.chain !== void 0 && request.chain !== Chain.BTC && request.chain !== Chain.ETH) {
|
|
6449
|
+
throw new LiquidiumError(
|
|
6450
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
6451
|
+
`Inflow submission is not supported for ${String(request.chain)}`
|
|
6452
|
+
);
|
|
6453
|
+
}
|
|
5792
6454
|
const apiClient = this.requireApi();
|
|
5793
6455
|
const response = await apiClient.post(SdkApiPath.inflow, request);
|
|
5794
6456
|
return {
|
|
@@ -5828,38 +6490,39 @@ var LendingModule = class {
|
|
|
5828
6490
|
return new SupplyFlowExecutor({
|
|
5829
6491
|
canisterContext: this.canisterContext,
|
|
5830
6492
|
evmReadClient: this.evmReadClient,
|
|
5831
|
-
getPoolById: async (poolId) => await this.getPoolById(poolId),
|
|
5832
6493
|
requireApi: () => {
|
|
5833
6494
|
this.requireApi();
|
|
5834
6495
|
},
|
|
5835
6496
|
submitInflow: async (request) => await this.submitInflow(request)
|
|
5836
6497
|
});
|
|
5837
6498
|
}
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
6499
|
+
};
|
|
6500
|
+
function getInflowFeeLedgerAssetRoute(request) {
|
|
6501
|
+
if (request.chain !== Chain.ICP) {
|
|
6502
|
+
throw new LiquidiumError(
|
|
6503
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
6504
|
+
`IC ledger inflow fees are not supported for ${request.asset} on ${request.chain}`
|
|
5844
6505
|
);
|
|
5845
|
-
const decodedPool = selectedPool ? decodeFlexiblePool(selectedPool) : null;
|
|
5846
|
-
if (!decodedPool) {
|
|
5847
|
-
throw new LiquidiumError(
|
|
5848
|
-
LiquidiumErrorCode.POOL_NOT_FOUND,
|
|
5849
|
-
`Pool not found: ${poolId}`
|
|
5850
|
-
);
|
|
5851
|
-
}
|
|
5852
|
-
return decodedPool;
|
|
5853
6506
|
}
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
6507
|
+
return getPoolLedgerAssetRoute(request);
|
|
6508
|
+
}
|
|
6509
|
+
function resolveOutflowDestinationInput(params) {
|
|
6510
|
+
const receiver = params.receiver;
|
|
6511
|
+
const address = typeof receiver === "string" ? receiver.trim() : receiver.address.trim();
|
|
6512
|
+
if (!address) {
|
|
6513
|
+
throw new LiquidiumError(
|
|
6514
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
6515
|
+
params.errorMessage
|
|
6516
|
+
);
|
|
5861
6517
|
}
|
|
5862
|
-
|
|
6518
|
+
if (typeof receiver === "string") {
|
|
6519
|
+
return address;
|
|
6520
|
+
}
|
|
6521
|
+
return {
|
|
6522
|
+
address,
|
|
6523
|
+
type: receiver.type
|
|
6524
|
+
};
|
|
6525
|
+
}
|
|
5863
6526
|
function mapExpectedOutflowDetails(details, expectedOutflowType, operation) {
|
|
5864
6527
|
if (details.outflowType !== expectedOutflowType) {
|
|
5865
6528
|
throw new LiquidiumError(
|
|
@@ -5953,13 +6616,17 @@ function formatPrice(price, decimals) {
|
|
|
5953
6616
|
|
|
5954
6617
|
// src/modules/market/market.ts
|
|
5955
6618
|
var ZERO_POOL_RATE = [0n, 0n, 0n];
|
|
6619
|
+
var BACKING_POOL_CHAIN_BY_ASSET = {
|
|
6620
|
+
[Asset.BTC]: Chain.BTC,
|
|
6621
|
+
[Asset.ICP]: Chain.ICP,
|
|
6622
|
+
[Asset.USDC]: Chain.ETH,
|
|
6623
|
+
[Asset.USDT]: Chain.ETH
|
|
6624
|
+
};
|
|
5956
6625
|
var MarketModule = class {
|
|
5957
|
-
constructor(canisterContext
|
|
6626
|
+
constructor(canisterContext) {
|
|
5958
6627
|
this.canisterContext = canisterContext;
|
|
5959
|
-
this.apiClient = apiClient;
|
|
5960
6628
|
}
|
|
5961
6629
|
canisterContext;
|
|
5962
|
-
apiClient;
|
|
5963
6630
|
/**
|
|
5964
6631
|
* Lists SDK-supported pools with their current rates.
|
|
5965
6632
|
*
|
|
@@ -5968,7 +6635,6 @@ var MarketModule = class {
|
|
|
5968
6635
|
* @returns Supported lending pools enriched with their current rate data.
|
|
5969
6636
|
*/
|
|
5970
6637
|
async listPools() {
|
|
5971
|
-
void this.apiClient;
|
|
5972
6638
|
try {
|
|
5973
6639
|
const flexibleActor = createFlexibleLendingActor(this.canisterContext);
|
|
5974
6640
|
const rawPools = await flexibleActor.list_pools();
|
|
@@ -6013,15 +6679,26 @@ var MarketModule = class {
|
|
|
6013
6679
|
}
|
|
6014
6680
|
}
|
|
6015
6681
|
/**
|
|
6016
|
-
* Resolves a single pool for the given
|
|
6682
|
+
* Resolves a single backing pool for the given Chain + Asset identifier.
|
|
6683
|
+
*
|
|
6684
|
+
* Native and chain-key identifiers share a pool. For example, both
|
|
6685
|
+
* `ETH/USDT` and `ICP/USDT` resolve to the USDT lending pool.
|
|
6017
6686
|
*
|
|
6018
6687
|
* @param query - The market asset and chain pair to match.
|
|
6019
6688
|
* @returns The single pool that matches the requested asset and chain.
|
|
6020
6689
|
*/
|
|
6021
6690
|
async findPool(query) {
|
|
6691
|
+
const identifier = { chain: query.chain, asset: query.asset };
|
|
6692
|
+
if (!isAssetIdentifier(identifier)) {
|
|
6693
|
+
throw new LiquidiumError(
|
|
6694
|
+
LiquidiumErrorCode.VALIDATION_ERROR,
|
|
6695
|
+
`Unsupported asset identifier: ${identifier.chain}/${identifier.asset}`
|
|
6696
|
+
);
|
|
6697
|
+
}
|
|
6022
6698
|
const pools = await this.listPools();
|
|
6699
|
+
const backingPoolChain = BACKING_POOL_CHAIN_BY_ASSET[identifier.asset];
|
|
6023
6700
|
const matchedPools = pools.filter(
|
|
6024
|
-
(pool) => pool.asset ===
|
|
6701
|
+
(pool) => pool.asset === identifier.asset && pool.chain === backingPoolChain
|
|
6025
6702
|
);
|
|
6026
6703
|
if (matchedPools.length === 0) {
|
|
6027
6704
|
throw new LiquidiumError(
|
|
@@ -6410,7 +7087,7 @@ var LiquidiumClient = class {
|
|
|
6410
7087
|
this.apiClient,
|
|
6411
7088
|
this.evmReadClient
|
|
6412
7089
|
);
|
|
6413
|
-
this.market = new MarketModule(this.canisterContext
|
|
7090
|
+
this.market = new MarketModule(this.canisterContext);
|
|
6414
7091
|
this.positions = new PositionsModule(this.canisterContext, this.market);
|
|
6415
7092
|
this.activities = new ActivitiesModule(
|
|
6416
7093
|
this.apiClient,
|
|
@@ -6442,6 +7119,6 @@ function resolveEvmReadClient(config) {
|
|
|
6442
7119
|
});
|
|
6443
7120
|
}
|
|
6444
7121
|
|
|
6445
|
-
export { AccountsModule, ActivitiesModule, ActivityFilter, Asset, CK_ETH_DEPOSIT_CONTRACT_ADDRESS, Chain, Environment, EvmSupplyApprovalStrategy, HistoryModule, InstantLoansModule, LendingModule, LiquidiumClient, LiquidiumError, LiquidiumErrorCode, MIN_BORROW_AMOUNTS_BY_ASSET, MarketModule, OutflowType, PositionsModule, QuoteModule, QuoteValidationErrorCode, QuoteWarningCode, RATE_DECIMALS, RATE_SCALE2 as RATE_SCALE, SupplyAction, SupplyPlanType,
|
|
7122
|
+
export { AccountsModule, ActivitiesModule, ActivityFilter, Asset, CK_ETH_DEPOSIT_CONTRACT_ADDRESS, Chain, Environment, EvmSupplyApprovalStrategy, HistoryModule, InstantLoanCreatedError, InstantLoansModule, LendingModule, LiquidiumAccountType, LiquidiumClient, LiquidiumError, LiquidiumErrorCode, MIN_BORROW_AMOUNTS_BY_ASSET, MIN_WITHDRAW_AMOUNTS_BY_ASSET, MarketModule, OutflowType, PositionsModule, QuoteModule, QuoteValidationErrorCode, QuoteWarningCode, RATE_DECIMALS, RATE_SCALE2 as RATE_SCALE, SupplyAction, SupplyPlanType, USDC_CONTRACT_ADDRESS, USDT_CONTRACT_ADDRESS, WalletActionKind, WalletExecutionKind, createTransferErc20Transaction, executeWith, getMinimumBorrowAmount, getMinimumWithdrawAmount, intFromPublicId, isAssetIdentifier, publicIdFromInt };
|
|
6446
7123
|
//# sourceMappingURL=index.js.map
|
|
6447
7124
|
//# sourceMappingURL=index.js.map
|