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