@liquidium/client 0.4.1 → 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/dist/index.js CHANGED
@@ -4,60 +4,12 @@ import { Actor, HttpAgent } from '@icp-sdk/core/agent';
4
4
  import { Principal } from '@icp-sdk/core/principal';
5
5
  import { base64, base64nopad } from '@scure/base';
6
6
  import { idlLabelToId } from '@icp-sdk/core/candid';
7
- import { encodeIcrcAccount } from '@icp-sdk/canisters/ledger/icrc';
7
+ import { AccountIdentifier, SubAccount } from '@icp-sdk/canisters/ledger/icp';
8
+ import { decodeIcrcAccount, encodeIcrcAccount } from '@icp-sdk/canisters/ledger/icrc';
8
9
  import { validate, Network } from 'bitcoin-address-validation';
9
10
 
10
11
  // src/client.ts
11
12
 
12
- // src/core/types.ts
13
- var Environment = {
14
- mainnet: "mainnet"
15
- };
16
- var Asset = {
17
- BTC: "BTC",
18
- SOL: "SOL",
19
- USDC: "USDC",
20
- USDT: "USDT"
21
- };
22
- var Chain = {
23
- BTC: "BTC",
24
- ETH: "ETH"
25
- };
26
- var SupplyAction = {
27
- deposit: "deposit",
28
- repayment: "repayment"
29
- };
30
- var OutflowType = {
31
- borrow: "borrow",
32
- feeClaim: "feeClaim",
33
- withdrawal: "withdrawal"
34
- };
35
-
36
- // src/core/config.ts
37
- var MAINNET_HOST = "https://icp-api.io";
38
- var DEFAULT_API_BASE_URL = "https://app.liquidium.fi/api/sdk";
39
- var DEFAULT_ENVIRONMENT = Environment.mainnet;
40
- var MAINNET_CANISTER_IDS = {
41
- lending: "hyk4r-jqaaa-aaaar-qb4ca-cai",
42
- btcPool: "hkmli-faaaa-aaaar-qb4ba-cai",
43
- ercPool: "hnnn4-iyaaa-aaaar-qb4bq-cai",
44
- ethDeposit: "z5jz7-nyaaa-aaaar-qb6pq-cai",
45
- instantLoans: "u5rm3-niaaa-aaaar-qb7eq-cai"
46
- };
47
- var CANISTER_IDS_BY_ENVIRONMENT = {
48
- mainnet: MAINNET_CANISTER_IDS
49
- };
50
- function resolveHost(override) {
51
- return override ?? MAINNET_HOST;
52
- }
53
- function resolveCanisterIds(environment = DEFAULT_ENVIRONMENT, overrides) {
54
- return { ...CANISTER_IDS_BY_ENVIRONMENT[environment], ...overrides };
55
- }
56
- var DEFAULT_TIMEOUT_MS = 3e4;
57
- var CK_CANISTER_IDS = {
58
- btcMinter: "mqygn-kiaaa-aaaar-qaadq-cai",
59
- btcLedger: "mxzaz-hqaaa-aaaar-qaada-cai"};
60
-
61
13
  // src/core/errors.ts
62
14
  var LiquidiumErrorCode = {
63
15
  // Protocol errors
@@ -128,6 +80,127 @@ var LiquidiumError = class extends Error {
128
80
  }
129
81
  };
130
82
 
83
+ // src/core/types.ts
84
+ var Environment = {
85
+ mainnet: "mainnet"
86
+ };
87
+ var Asset = {
88
+ BTC: "BTC",
89
+ ICP: "ICP",
90
+ USDC: "USDC",
91
+ USDT: "USDT"
92
+ };
93
+ var Chain = {
94
+ BTC: "BTC",
95
+ ETH: "ETH",
96
+ ICP: "ICP"
97
+ };
98
+ function isAssetIdentifier(identifier) {
99
+ switch (identifier.chain) {
100
+ case Chain.BTC:
101
+ return identifier.asset === Asset.BTC;
102
+ case Chain.ETH:
103
+ return identifier.asset === Asset.USDC || identifier.asset === Asset.USDT;
104
+ case Chain.ICP:
105
+ return identifier.asset === Asset.BTC || identifier.asset === Asset.ICP || identifier.asset === Asset.USDC || identifier.asset === Asset.USDT;
106
+ default:
107
+ return false;
108
+ }
109
+ }
110
+ var SupplyAction = {
111
+ deposit: "deposit",
112
+ repayment: "repayment"
113
+ };
114
+ var OutflowType = {
115
+ borrow: "borrow",
116
+ feeClaim: "feeClaim",
117
+ withdrawal: "withdrawal"
118
+ };
119
+
120
+ // src/core/config.ts
121
+ var MAINNET_HOST = "https://icp-api.io";
122
+ var DEFAULT_API_BASE_URL = "https://app.liquidium.fi/api/sdk";
123
+ var DEFAULT_ENVIRONMENT = Environment.mainnet;
124
+ var MAINNET_CANISTER_IDS = {
125
+ lending: "hyk4r-jqaaa-aaaar-qb4ca-cai",
126
+ pools: {
127
+ btc: "hkmli-faaaa-aaaar-qb4ba-cai",
128
+ usdt: "hnnn4-iyaaa-aaaar-qb4bq-cai",
129
+ usdc: "6sna2-oiaaa-aaaar-qb66q-cai",
130
+ icp: "r2pk3-4yaaa-aaaar-qb7zq-cai"
131
+ },
132
+ ethDeposit: "z5jz7-nyaaa-aaaar-qb6pq-cai",
133
+ instantLoans: "u5rm3-niaaa-aaaar-qb7eq-cai"
134
+ };
135
+ var CANISTER_IDS_BY_ENVIRONMENT = {
136
+ mainnet: MAINNET_CANISTER_IDS
137
+ };
138
+ var SUPPORTED_CANISTER_ID_OVERRIDE_KEYS = /* @__PURE__ */ new Set([
139
+ "lending",
140
+ "pools",
141
+ "ethDeposit",
142
+ "instantLoans"
143
+ ]);
144
+ var SUPPORTED_POOL_CANISTER_ID_OVERRIDE_KEYS = /* @__PURE__ */ new Set([
145
+ "btc",
146
+ "usdt",
147
+ "usdc",
148
+ "icp"
149
+ ]);
150
+ function resolveHost(override) {
151
+ return override ?? MAINNET_HOST;
152
+ }
153
+ function resolveCanisterIds(environment = DEFAULT_ENVIRONMENT, overrides) {
154
+ const defaults = CANISTER_IDS_BY_ENVIRONMENT[environment];
155
+ validateCanisterIdOverrides(overrides);
156
+ const pools = {
157
+ ...defaults.pools,
158
+ ...overrides?.pools
159
+ };
160
+ return {
161
+ lending: overrides?.lending ?? defaults.lending,
162
+ pools,
163
+ ethDeposit: overrides?.ethDeposit ?? defaults.ethDeposit,
164
+ instantLoans: overrides?.instantLoans ?? defaults.instantLoans
165
+ };
166
+ }
167
+ function validateCanisterIdOverrides(overrides) {
168
+ if (!overrides) {
169
+ return;
170
+ }
171
+ for (const key of Object.keys(overrides)) {
172
+ if (!SUPPORTED_CANISTER_ID_OVERRIDE_KEYS.has(key)) {
173
+ throw new LiquidiumError(
174
+ LiquidiumErrorCode.VALIDATION_ERROR,
175
+ `Unsupported canisterIds override: ${key}`
176
+ );
177
+ }
178
+ }
179
+ if (!overrides.pools) {
180
+ return;
181
+ }
182
+ for (const key of Object.keys(overrides.pools)) {
183
+ if (!SUPPORTED_POOL_CANISTER_ID_OVERRIDE_KEYS.has(key)) {
184
+ throw new LiquidiumError(
185
+ LiquidiumErrorCode.VALIDATION_ERROR,
186
+ `Unsupported canisterIds.pools override: ${key}`
187
+ );
188
+ }
189
+ }
190
+ }
191
+ var DEFAULT_TIMEOUT_MS = 3e4;
192
+ var CK_CANISTER_IDS = {
193
+ BTC: {
194
+ minter: "mqygn-kiaaa-aaaar-qaadq-cai",
195
+ ledger: "mxzaz-hqaaa-aaaar-qaada-cai"},
196
+ USDT: {
197
+ ledger: "cngnf-vqaaa-aaaar-qag4q-cai"},
198
+ USDC: {
199
+ ledger: "xevnm-gaaaa-aaaar-qafnq-cai"},
200
+ ICP: {
201
+ ledger: "ryjl3-tyaaa-aaaaa-aaaba-cai"}
202
+ };
203
+
131
204
  // src/core/transports/api-client.ts
132
205
  var DOM_EXCEPTION_ABORT_ERROR_NAME = "AbortError";
133
206
  var HTTP_HEADER_CONTENT_TYPE = "content-type";
@@ -981,9 +1054,6 @@ function getVariantKey(variant) {
981
1054
  }
982
1055
 
983
1056
  // src/core/wallet-actions.ts
984
- var TransferMode = {
985
- native: "native"
986
- };
987
1057
  var WalletExecutionKind = {
988
1058
  signMessage: "sign-message"
989
1059
  };
@@ -1014,8 +1084,7 @@ function executeWith(options) {
1014
1084
  chain: options.chain,
1015
1085
  message: action.message,
1016
1086
  account: options.account ?? action.account,
1017
- actionType: action.actionType,
1018
- transferMode: action.transferMode
1087
+ actionType: action.actionType
1019
1088
  });
1020
1089
  return action.submit({
1021
1090
  signature,
@@ -1238,7 +1307,6 @@ var AccountsModule = class {
1238
1307
  kind: WalletActionKind.createAccount,
1239
1308
  executionKind: WalletExecutionKind.signMessage,
1240
1309
  actionType: WalletActionKind.createAccount,
1241
- transferMode: TransferMode.native,
1242
1310
  account: normalizedAccount,
1243
1311
  message: createInitializeAccountMessage(expiryTimestamp, nonce),
1244
1312
  data: {
@@ -1329,8 +1397,8 @@ function mapCanisterWalletToWallet(canisterWallet) {
1329
1397
  );
1330
1398
  }
1331
1399
  }
1332
- var KNOWN_ASSET_TAGS = ["BTC", "USDC", "USDT"];
1333
- var KNOWN_CHAIN_TAGS = ["BTC", "ETH"];
1400
+ var KNOWN_ASSET_TAGS = ["BTC", "ICP", "USDC", "USDT"];
1401
+ var KNOWN_CHAIN_TAGS = ["BTC", "ETH", "ICP"];
1334
1402
  function extractVariantTag(variant, knownTags) {
1335
1403
  const [key] = Object.keys(variant);
1336
1404
  if (!key) {
@@ -1909,17 +1977,14 @@ function mapInstantLoanLookupError(error) {
1909
1977
  }
1910
1978
  function mapActivity(wire) {
1911
1979
  const amount = parseBigInt(wire.amount, "activity amount");
1912
- const activityBase = {
1913
- id: wire.id,
1914
- poolId: wire.poolId,
1915
- asset: wire.asset,
1916
- chain: wire.chain,
1917
- amount,
1918
- timestampMs: wire.timestampMs
1919
- };
1920
1980
  if (isInflowOperation(wire.status.operation)) {
1921
1981
  const activity = {
1922
- ...activityBase,
1982
+ id: wire.id,
1983
+ poolId: wire.poolId,
1984
+ asset: wire.asset,
1985
+ chain: wire.chain,
1986
+ amount,
1987
+ timestampMs: wire.timestampMs,
1923
1988
  status: wire.status
1924
1989
  };
1925
1990
  if (wire.txids) {
@@ -1932,7 +1997,12 @@ function mapActivity(wire) {
1932
1997
  }
1933
1998
  if (isOutflowOperation(wire.status.operation)) {
1934
1999
  const activity = {
1935
- ...activityBase,
2000
+ id: wire.id,
2001
+ poolId: wire.poolId,
2002
+ asset: wire.asset,
2003
+ chain: wire.chain,
2004
+ amount,
2005
+ timestampMs: wire.timestampMs,
1936
2006
  status: wire.status
1937
2007
  };
1938
2008
  if (wire.txids) {
@@ -2108,6 +2178,72 @@ function validateHistoryStateFilter(state) {
2108
2178
  );
2109
2179
  }
2110
2180
  }
2181
+ var LiquidiumAccountType = {
2182
+ ChainAddress: "ChainAddress",
2183
+ IcPrincipal: "IcPrincipal",
2184
+ IcpAccountIdentifier: "IcpAccountIdentifier",
2185
+ IcrcAccount: "IcrcAccount"
2186
+ };
2187
+ function createIcrcAccount(params) {
2188
+ return {
2189
+ type: "IcrcAccount",
2190
+ owner: params.owner.toText(),
2191
+ subaccount: params.subaccount,
2192
+ address: encodeIcrcAccount({
2193
+ owner: params.owner,
2194
+ subaccount: params.subaccount
2195
+ })
2196
+ };
2197
+ }
2198
+ function mapCanisterAccountToLiquidiumAccount(account) {
2199
+ if ("Native" in account) {
2200
+ return { type: "IcPrincipal", address: account.Native.toText() };
2201
+ }
2202
+ if ("AccountIdentifier" in account) {
2203
+ return {
2204
+ type: "IcpAccountIdentifier",
2205
+ address: account.AccountIdentifier
2206
+ };
2207
+ }
2208
+ if ("Icrc" in account) {
2209
+ const subaccount = normalizeOptionalSubaccount(account.Icrc.subaccount[0]);
2210
+ return {
2211
+ type: "IcrcAccount",
2212
+ owner: account.Icrc.owner.toText(),
2213
+ subaccount,
2214
+ address: encodeIcrcAccount({
2215
+ owner: account.Icrc.owner,
2216
+ subaccount
2217
+ })
2218
+ };
2219
+ }
2220
+ return { type: "ChainAddress", address: account.External };
2221
+ }
2222
+ function decodeIcrcAccountAddress(address) {
2223
+ const decoded = decodeIcrcAccount(address);
2224
+ const owner = Principal.fromText(decoded.owner.toText());
2225
+ const subaccount = decoded.subaccount ? Uint8Array.from(decoded.subaccount) : void 0;
2226
+ return {
2227
+ owner,
2228
+ subaccount,
2229
+ account: createIcrcAccount({ owner, subaccount })
2230
+ };
2231
+ }
2232
+ function encodeIcpAccountIdentifier(params) {
2233
+ return AccountIdentifier.fromPrincipal({
2234
+ principal: params.owner,
2235
+ subAccount: params.subaccount ? SubAccount.fromBytes(params.subaccount) : void 0
2236
+ }).toHex();
2237
+ }
2238
+ function normalizeIcpAccountIdentifier(address) {
2239
+ return AccountIdentifier.fromHex(address).toHex();
2240
+ }
2241
+ function normalizeOptionalSubaccount(subaccount) {
2242
+ if (!subaccount) {
2243
+ return void 0;
2244
+ }
2245
+ return subaccount instanceof Uint8Array ? subaccount : Uint8Array.from(subaccount);
2246
+ }
2111
2247
 
2112
2248
  // src/core/status.ts
2113
2249
  function createLiquidiumStatus(params) {
@@ -2157,6 +2293,7 @@ function throwInvalidApiResponseField(field) {
2157
2293
  // src/core/utils/asset-decimals.ts
2158
2294
  var ASSET_NATIVE_DECIMALS = {
2159
2295
  BTC: 8n,
2296
+ ICP: 8n,
2160
2297
  USDC: 6n,
2161
2298
  USDT: 6n,
2162
2299
  SOL: 9n
@@ -2552,7 +2689,7 @@ var idlFactory2 = ({ IDL }) => {
2552
2689
 
2553
2690
  // src/core/canisters/ckbtc/minter.ts
2554
2691
  function createCkBtcMinterActor(canisterContext) {
2555
- const canisterId = CK_CANISTER_IDS.btcMinter;
2692
+ const canisterId = CK_CANISTER_IDS.BTC.minter;
2556
2693
  return Actor.createActor(idlFactory2, {
2557
2694
  agent: canisterContext.agent,
2558
2695
  canisterId
@@ -3003,55 +3140,43 @@ var EvmSupplyApprovalStrategy = {
3003
3140
  };
3004
3141
 
3005
3142
  // src/modules/lending/_internal/supply-targets.ts
3006
- async function resolveSupplyTarget(canisterContext, request) {
3007
- const selectedPool = await getPoolById(canisterContext, request.poolId);
3008
- const asset = selectedPool.asset;
3009
- const chain = selectedPool.chain;
3143
+ async function resolveSupplyTargetForPool(canisterContext, request, selectedPool) {
3144
+ const identifier = resolveSupplyAssetIdentifier({
3145
+ asset: selectedPool.asset,
3146
+ poolChain: selectedPool.chain,
3147
+ transferChain: request.chain ?? selectedPool.chain
3148
+ });
3010
3149
  const mechanism = resolveSupplyMechanism({
3011
- asset,
3012
- chain,
3013
- requestedMechanism: request.mechanism
3150
+ identifier,
3151
+ mechanism: request.mechanism
3014
3152
  });
3015
- switch (mechanism) {
3016
- case SupplyPlanType.transfer:
3017
- return await getNativeAddressSupplyTarget(
3018
- canisterContext,
3019
- request.profileId,
3020
- {
3021
- poolId: request.poolId,
3022
- asset,
3023
- chain,
3024
- action: request.action
3025
- }
3026
- );
3027
- case SupplyPlanType.contractInteraction:
3028
- return getIcrcAccountSupplyTarget(request.profileId, {
3029
- poolId: request.poolId,
3030
- asset,
3031
- chain,
3032
- action: request.action
3033
- });
3153
+ const targetRequest = {
3154
+ ...identifier,
3155
+ poolId: request.poolId,
3156
+ action: request.action
3157
+ };
3158
+ if (mechanism === SupplyPlanType.contractInteraction) {
3159
+ return getIcrcAccountSupplyTarget(request.profileId, targetRequest);
3034
3160
  }
3161
+ if (identifier.chain === Chain.ICP) {
3162
+ return identifier.asset === Asset.ICP ? getIcpLedgerSupplyTarget(request.profileId, targetRequest) : getIcrcAccountSupplyTarget(request.profileId, targetRequest);
3163
+ }
3164
+ return await getChainAddressSupplyTarget(
3165
+ canisterContext,
3166
+ request.profileId,
3167
+ targetRequest
3168
+ );
3035
3169
  }
3036
3170
  function resolveSupplyMechanism(params) {
3037
- if (params.asset === Asset.BTC && params.chain === Chain.BTC) {
3038
- if (params.requestedMechanism === SupplyPlanType.contractInteraction) {
3039
- throw new LiquidiumError(
3040
- LiquidiumErrorCode.VALIDATION_ERROR,
3041
- "Contract-interaction supply is not supported for BTC on BTC"
3042
- );
3043
- }
3171
+ if (params.mechanism !== SupplyPlanType.contractInteraction) {
3044
3172
  return SupplyPlanType.transfer;
3045
3173
  }
3046
- if (isEthStablecoin(params.asset, params.chain)) {
3047
- if (params.requestedMechanism) {
3048
- return params.requestedMechanism;
3049
- }
3050
- return SupplyPlanType.transfer;
3174
+ if (params.identifier.chain === Chain.ETH && (params.identifier.asset === Asset.USDC || params.identifier.asset === Asset.USDT)) {
3175
+ return SupplyPlanType.contractInteraction;
3051
3176
  }
3052
3177
  throw new LiquidiumError(
3053
3178
  LiquidiumErrorCode.VALIDATION_ERROR,
3054
- `No supply mechanism is configured for ${params.asset} on ${params.chain}`
3179
+ `Contract-interaction supply is not supported for ${params.identifier.asset} on ${params.identifier.chain}`
3055
3180
  );
3056
3181
  }
3057
3182
  function isEthStablecoin(asset, chain) {
@@ -3123,9 +3248,33 @@ async function getPoolById(canisterContext, poolId) {
3123
3248
  }
3124
3249
  return decodedPool;
3125
3250
  }
3126
- async function getNativeAddressSupplyTarget(canisterContext, profileId, request) {
3127
- assertSupportsNativeAddressInflowTarget(request.asset, request.chain);
3128
- if (isEthStablecoin(request.asset, request.chain)) {
3251
+ function resolveSupplyAssetIdentifier(params) {
3252
+ if (params.asset === Asset.BTC && params.poolChain === Chain.BTC) {
3253
+ if (params.transferChain === Chain.BTC) {
3254
+ return { asset: Asset.BTC, chain: Chain.BTC };
3255
+ }
3256
+ if (params.transferChain === Chain.ICP) {
3257
+ return { asset: Asset.BTC, chain: Chain.ICP };
3258
+ }
3259
+ }
3260
+ if ((params.asset === Asset.USDC || params.asset === Asset.USDT) && params.poolChain === Chain.ETH) {
3261
+ if (params.transferChain === Chain.ETH) {
3262
+ return { asset: params.asset, chain: Chain.ETH };
3263
+ }
3264
+ if (params.transferChain === Chain.ICP) {
3265
+ return { asset: params.asset, chain: Chain.ICP };
3266
+ }
3267
+ }
3268
+ if (params.asset === Asset.ICP && params.poolChain === Chain.ICP && params.transferChain === Chain.ICP) {
3269
+ return { asset: Asset.ICP, chain: Chain.ICP };
3270
+ }
3271
+ throw new LiquidiumError(
3272
+ LiquidiumErrorCode.VALIDATION_ERROR,
3273
+ `Supply is not supported for ${params.asset} pool on ${params.poolChain} using ${params.transferChain}`
3274
+ );
3275
+ }
3276
+ async function getChainAddressSupplyTarget(canisterContext, profileId, request) {
3277
+ if (request.chain === Chain.ETH) {
3129
3278
  const tokenAddress = getEthStablecoinContractAddress(request.asset);
3130
3279
  const subaccount2 = encodeInflowSubaccount({
3131
3280
  action: request.action,
@@ -3144,7 +3293,6 @@ async function getNativeAddressSupplyTarget(canisterContext, profileId, request)
3144
3293
  throw mapDepositAccountErrorToLiquidiumError(result.Err);
3145
3294
  }
3146
3295
  return {
3147
- type: "nativeAddress",
3148
3296
  poolId: request.poolId,
3149
3297
  asset: request.asset,
3150
3298
  chain: request.chain,
@@ -3152,11 +3300,11 @@ async function getNativeAddressSupplyTarget(canisterContext, profileId, request)
3152
3300
  address: result.Ok
3153
3301
  };
3154
3302
  }
3155
- const configuredBtcPoolId = canisterContext.canisterIds.btcPool;
3303
+ const configuredBtcPoolId = canisterContext.canisterIds.pools.btc;
3156
3304
  if (request.poolId !== configuredBtcPoolId) {
3157
3305
  throw new LiquidiumError(
3158
3306
  LiquidiumErrorCode.VALIDATION_ERROR,
3159
- `Native BTC inflow targets require the configured BTC pool ${configuredBtcPoolId}, received ${request.poolId}`
3307
+ `Chain-address BTC inflow targets require the configured BTC pool ${configuredBtcPoolId}, received ${request.poolId}`
3160
3308
  );
3161
3309
  }
3162
3310
  const subaccount = encodeInflowSubaccount({
@@ -3169,56 +3317,42 @@ async function getNativeAddressSupplyTarget(canisterContext, profileId, request)
3169
3317
  subaccount: [subaccount]
3170
3318
  }
3171
3319
  );
3172
- return {
3173
- type: "nativeAddress",
3174
- poolId: request.poolId,
3175
- asset: request.asset,
3176
- chain: request.chain,
3177
- action: request.action,
3178
- address
3179
- };
3320
+ return { ...request, address };
3180
3321
  }
3181
3322
  function getIcrcAccountSupplyTarget(profileId, request) {
3182
- assertSupportsIcrcAccountInflowTarget(request.asset);
3323
+ const account = createInflowIcrcAccount(profileId, request);
3324
+ return { ...request, address: account.address };
3325
+ }
3326
+ function getIcpLedgerSupplyTarget(profileId, request) {
3183
3327
  const poolPrincipal = Principal.fromText(request.poolId);
3184
3328
  const subaccount = encodeInflowSubaccount({
3185
3329
  action: request.action,
3186
3330
  principal: Principal.fromText(profileId)
3187
3331
  });
3332
+ const account = createIcrcAccount({
3333
+ owner: poolPrincipal,
3334
+ subaccount
3335
+ });
3188
3336
  return {
3189
- type: "icrcAccount",
3190
3337
  poolId: request.poolId,
3191
- asset: request.asset,
3192
- chain: request.chain,
3338
+ asset: Asset.ICP,
3339
+ chain: Chain.ICP,
3193
3340
  action: request.action,
3194
- owner: poolPrincipal.toText(),
3195
- subaccount,
3196
- account: encodeIcrcAccount({
3341
+ address: account.address,
3342
+ icpAccountIdentifier: encodeIcpAccountIdentifier({
3197
3343
  owner: poolPrincipal,
3198
3344
  subaccount
3199
3345
  })
3200
3346
  };
3201
3347
  }
3202
- function assertSupportsNativeAddressInflowTarget(asset, chain) {
3203
- if (asset === Asset.BTC && chain === Chain.BTC) {
3204
- return;
3205
- }
3206
- if (isEthStablecoin(asset, chain)) {
3207
- return;
3208
- }
3209
- throw new LiquidiumError(
3210
- LiquidiumErrorCode.VALIDATION_ERROR,
3211
- `Native address inflow targets are not supported for ${asset} on ${chain}`
3212
- );
3213
- }
3214
- function assertSupportsIcrcAccountInflowTarget(asset) {
3215
- if (asset === Asset.BTC || asset === Asset.USDT || asset === Asset.USDC) {
3216
- return;
3217
- }
3218
- throw new LiquidiumError(
3219
- LiquidiumErrorCode.VALIDATION_ERROR,
3220
- `ICRC account inflow targets are not supported for ${asset}`
3221
- );
3348
+ function createInflowIcrcAccount(profileId, request) {
3349
+ return createIcrcAccount({
3350
+ owner: Principal.fromText(request.poolId),
3351
+ subaccount: encodeInflowSubaccount({
3352
+ action: request.action,
3353
+ principal: Principal.fromText(profileId)
3354
+ })
3355
+ });
3222
3356
  }
3223
3357
 
3224
3358
  // src/core/borrow-minimums.ts
@@ -3692,25 +3826,20 @@ function isEthStablecoin2(asset) {
3692
3826
  }
3693
3827
 
3694
3828
  // src/modules/instant-loans/_internal/address-validation.ts
3695
- function validateInstantLoanBorrowDestination(address, asset) {
3829
+ function validateInstantLoanBorrowDestination(address, asset, chain) {
3696
3830
  return normalizeExternalAddress({
3697
3831
  address,
3698
3832
  asset,
3699
- chain: getChainForInstantLoanAsset(asset)
3833
+ chain
3700
3834
  });
3701
3835
  }
3702
- function validateInstantLoanRefundDestination(address, asset) {
3836
+ function validateInstantLoanRefundDestination(address, asset, chain) {
3703
3837
  return normalizeExternalAddress({
3704
3838
  address,
3705
3839
  asset,
3706
- chain: getChainForInstantLoanAsset(asset)
3840
+ chain
3707
3841
  });
3708
3842
  }
3709
- function getChainForInstantLoanAsset(asset) {
3710
- if (asset === Asset.BTC) return Chain.BTC;
3711
- if (asset === Asset.USDC || asset === Asset.USDT) return Chain.ETH;
3712
- return asset;
3713
- }
3714
3843
 
3715
3844
  // src/modules/instant-loans/instant-loans.ts
3716
3845
  var REPAYMENT_BUFFER_SECONDS = 86400n;
@@ -3723,10 +3852,32 @@ var INSTANT_LOAN_FIND_QUERY_MAX_LENGTH = 256;
3723
3852
  var INSTANT_LOAN_WIRE_CONTEXT = "instant loan";
3724
3853
  var INSTANT_LOAN_ASSETS = [
3725
3854
  Asset.BTC,
3726
- Asset.SOL,
3855
+ Asset.ICP,
3727
3856
  Asset.USDC,
3728
3857
  Asset.USDT
3729
3858
  ];
3859
+ var INSTANT_LOAN_CHAINS = [
3860
+ Chain.BTC,
3861
+ Chain.ETH,
3862
+ Chain.ICP
3863
+ ];
3864
+ var ICP_ACCOUNT_IDENTIFIER_HEX_LENGTH = 64;
3865
+ var InstantLoanCreatedError = class extends Error {
3866
+ code = "INSTANT_LOAN_HYDRATION_FAILED";
3867
+ loanId;
3868
+ ref;
3869
+ cause;
3870
+ constructor(loanId, cause) {
3871
+ const ref = publicIdFromInt(loanId);
3872
+ super(
3873
+ `Instant loan ${ref} was created, but its enriched state could not be loaded`
3874
+ );
3875
+ this.name = "InstantLoanCreatedError";
3876
+ this.loanId = loanId;
3877
+ this.ref = ref;
3878
+ this.cause = cause;
3879
+ }
3880
+ };
3730
3881
  var InstantLoansModule = class {
3731
3882
  constructor(canisterContext, apiClient, activities, lending, positions) {
3732
3883
  this.canisterContext = canisterContext;
@@ -3749,37 +3900,39 @@ var InstantLoansModule = class {
3749
3900
  * selected pool decimals, and call `client.quote.calculateLtv(...)` before
3750
3901
  * creation to block invalid LTV input.
3751
3902
  *
3752
- * `borrowDestination` receives the borrowed asset after the loan starts.
3753
- * `refundDestination` receives collateral refunds or withdrawals. Use
3903
+ * `borrow.destination` receives the borrowed asset after the loan starts.
3904
+ * `refund.destination` receives collateral refunds or withdrawals. Use
3754
3905
  * `depositWindowSeconds` for the user-facing collateral deposit timeout; the
3755
3906
  * SDK maps it to the canister's internal `ltv_timer_s` field.
3756
3907
  *
3757
- * @param request - Pool ids, assets, base-unit amounts, LTV limit, timeout, and destinations.
3908
+ * @param request - Collateral, borrow, refund, LTV limit, timeout, and inflow options.
3758
3909
  * @returns Hydrated loan state plus generated initial-deposit and repayment quote targets.
3759
3910
  */
3760
3911
  async create(request) {
3761
3912
  validateCreateRequest(request);
3762
- const borrowDestination = {
3763
- External: validateInstantLoanBorrowDestination(
3764
- addressFromAccountInput(request.borrowDestination),
3765
- request.borrowAsset
3766
- )
3767
- };
3768
- const refundDestination = {
3769
- External: validateInstantLoanRefundDestination(
3770
- addressFromAccountInput(request.refundDestination),
3771
- request.collateralAsset
3772
- )
3773
- };
3913
+ const borrowDestination = resolveInstantLoanDestination({
3914
+ destination: request.borrow.destination,
3915
+ asset: request.borrow.asset,
3916
+ role: "borrow",
3917
+ chain: request.borrow.chain,
3918
+ normalizeExternalDestination: validateInstantLoanBorrowDestination
3919
+ });
3920
+ const refundDestination = resolveInstantLoanDestination({
3921
+ destination: request.refund.destination,
3922
+ asset: request.collateral.asset,
3923
+ role: "refund",
3924
+ chain: request.refund.chain,
3925
+ normalizeExternalDestination: validateInstantLoanRefundDestination
3926
+ });
3774
3927
  const apiClient = this.requireApi("Instant loan creation");
3775
3928
  await this.validateInstantLoanLtvPolicy(request);
3776
3929
  const response = await apiClient.post(SdkApiPath.instantLoans, {
3777
- collateralPoolId: request.collateralPoolId,
3778
- borrowPoolId: request.borrowPoolId,
3779
- collateralAsset: request.collateralAsset,
3780
- borrowAsset: request.borrowAsset,
3781
- collateralAmount: request.collateralAmount.toString(),
3782
- borrowAmount: request.borrowAmount.toString(),
3930
+ collateralPoolId: request.collateral.poolId,
3931
+ borrowPoolId: request.borrow.poolId,
3932
+ collateralAsset: request.collateral.asset,
3933
+ borrowAsset: request.borrow.asset,
3934
+ collateralAmount: request.collateral.amount.toString(),
3935
+ borrowAmount: request.borrow.amount.toString(),
3783
3936
  ltvMaxBps: request.ltvMaxBps.toString(),
3784
3937
  depositWindowSeconds: request.depositWindowSeconds.toString(),
3785
3938
  borrowDestination,
@@ -3789,15 +3942,21 @@ var InstantLoansModule = class {
3789
3942
  context: INSTANT_LOAN_WIRE_CONTEXT,
3790
3943
  label: "loan ID"
3791
3944
  });
3792
- const collateralAmount = parseUnsignedApiBigint(
3793
- response.loan.collateral.amountHint,
3794
- {
3795
- context: INSTANT_LOAN_WIRE_CONTEXT,
3796
- label: "collateral amount"
3797
- }
3798
- );
3799
- const record = await this.getLoanRecord(loanId);
3800
- return await this.mapLoanRecord(record, collateralAmount);
3945
+ try {
3946
+ const collateralAmount = parseUnsignedApiBigint(
3947
+ response.loan.collateral.amountHint,
3948
+ {
3949
+ context: INSTANT_LOAN_WIRE_CONTEXT,
3950
+ label: "collateral amount"
3951
+ }
3952
+ );
3953
+ const record = await this.getLoanRecord(loanId);
3954
+ return await this.mapLoanRecord(record, collateralAmount, {
3955
+ borrowChain: request.borrow.chain
3956
+ });
3957
+ } catch (cause) {
3958
+ throw new InstantLoanCreatedError(loanId, cause);
3959
+ }
3801
3960
  }
3802
3961
  /**
3803
3962
  * Resolves canonical canister state by loan id or short reference.
@@ -3963,7 +4122,16 @@ var InstantLoansModule = class {
3963
4122
  throw mapCanisterCallErrorToLiquidiumError("get_loan", error);
3964
4123
  }
3965
4124
  }
3966
- async mapLoanRecord(record, collateralAmount) {
4125
+ async mapLoanRecord(record, collateralAmount, chainOptions = {}) {
4126
+ const borrowDestination = accountFromCanister(record.borrow_destination);
4127
+ const collateralAsset = parseInstantLoanAsset(
4128
+ record.lend_asset,
4129
+ "collateral asset"
4130
+ );
4131
+ const borrowAsset = parseInstantLoanAsset(
4132
+ record.borrow_asset,
4133
+ "borrow asset"
4134
+ );
3967
4135
  return await this.hydrateLoan({
3968
4136
  loanId: record.id,
3969
4137
  profileId: record.lending_profile.toText(),
@@ -3972,17 +4140,18 @@ var InstantLoansModule = class {
3972
4140
  collateralPoolId: record.lend_pool_id.toText(),
3973
4141
  collateralAmount,
3974
4142
  borrowPoolId: record.borrow_pool_id.toText(),
3975
- collateralAsset: record.lend_asset,
3976
- borrowAsset: record.borrow_asset,
4143
+ collateralAsset,
4144
+ borrowAsset,
3977
4145
  borrowAmount: record.borrow_amount,
3978
- borrowDestination: accountFromCanister(record.borrow_destination),
4146
+ borrowDestination,
3979
4147
  refundDestination: accountFromCanister(record.refund_destination),
3980
4148
  started: record.started,
3981
4149
  depositDetectedTimestamp: record.deposit_detected_ts[0] ?? null,
3982
4150
  expiryTimestamp: record.expires_at[0] ?? deriveDepositExpiryTimestamp({
3983
4151
  depositDetectedTimestamp: record.deposit_detected_ts[0] ?? null,
3984
4152
  depositWindowSeconds: record.ltv_timer_s
3985
- })
4153
+ }),
4154
+ borrowChain: chainOptions.borrowChain ?? inferInstantLoanDeliveryChain(borrowDestination, borrowAsset)
3986
4155
  });
3987
4156
  }
3988
4157
  async getCollateralAmountHint(loanId) {
@@ -4002,24 +4171,22 @@ var InstantLoansModule = class {
4002
4171
  const collateralAsset = input.collateralAsset;
4003
4172
  const borrowAsset = input.borrowAsset;
4004
4173
  const [
4005
- depositTarget,
4006
- repayTarget,
4174
+ depositTargetSet,
4175
+ repaymentTargetSet,
4007
4176
  collateralPosition,
4008
4177
  borrowPosition,
4009
4178
  borrowPoolRate,
4010
4179
  activeActivities
4011
4180
  ] = await Promise.all([
4012
- resolveSupplyTarget(this.canisterContext, {
4181
+ this.resolveInstantLoanInflowTargets({
4013
4182
  profileId,
4014
4183
  poolId: collateralPoolId,
4015
- action: SupplyAction.deposit,
4016
- mechanism: SupplyPlanType.transfer
4184
+ action: SupplyAction.deposit
4017
4185
  }),
4018
- resolveSupplyTarget(this.canisterContext, {
4186
+ this.resolveInstantLoanInflowTargets({
4019
4187
  profileId,
4020
4188
  poolId: borrowPoolId,
4021
- action: SupplyAction.repayment,
4022
- mechanism: SupplyPlanType.transfer
4189
+ action: SupplyAction.repayment
4023
4190
  }),
4024
4191
  this.positions.getPosition(profileId, collateralPoolId),
4025
4192
  this.positions.getPosition(profileId, borrowPoolId),
@@ -4031,8 +4198,6 @@ var InstantLoansModule = class {
4031
4198
  borrowPosition,
4032
4199
  borrowPoolRate.borrowRate
4033
4200
  );
4034
- const repaymentInflowFee = totalDebtAmount > 0n ? await this.estimateRepaymentInflowFee(borrowAsset, repayTarget.chain) : { totalFee: 0n, estimateAvailable: false };
4035
- const repaymentAmount = totalDebtAmount + interestBufferAmount + repaymentInflowFee.totalFee;
4036
4201
  const currentCollateralAmount = collateralPosition?.deposited ?? 0n;
4037
4202
  const collateralAmount = input.collateralAmount;
4038
4203
  const collateralDecimals = collateralPosition?.depositedDecimals ?? getAssetNativeDecimals(collateralAsset);
@@ -4052,22 +4217,28 @@ var InstantLoansModule = class {
4052
4217
  collateralAmount,
4053
4218
  decimals: collateralDecimals,
4054
4219
  asset: collateralAsset,
4055
- target: depositTarget,
4220
+ targets: depositTargetSet.targets,
4056
4221
  detectedTimestamp: input.depositDetectedTimestamp,
4057
4222
  expiryTimestamp: input.expiryTimestamp
4058
4223
  });
4224
+ const repaymentTargets = await this.createRepaymentTargetQuotes({
4225
+ baseRepaymentAmount: totalDebtAmount + interestBufferAmount,
4226
+ asset: borrowAsset,
4227
+ targets: repaymentTargetSet.targets
4228
+ });
4059
4229
  const repayment = {
4060
- amount: repaymentAmount,
4061
4230
  decimals: borrowedDecimals,
4062
4231
  debtAmount: totalDebtAmount,
4063
4232
  interestBufferAmount,
4064
4233
  interestBufferSeconds: REPAYMENT_BUFFER_SECONDS,
4065
- inflowFeeAmount: repaymentInflowFee.totalFee,
4066
- inflowFeeEstimateAvailable: repaymentInflowFee.estimateAvailable,
4067
4234
  asset: borrowAsset,
4068
- chain: repayTarget.chain,
4069
- target: repayTarget
4235
+ targets: repaymentTargets
4070
4236
  };
4237
+ const borrowIdentifier = getInstantLoanAssetIdentifier(
4238
+ borrowAsset,
4239
+ input.borrowChain,
4240
+ "borrow"
4241
+ );
4071
4242
  return {
4072
4243
  loanId: input.loanId,
4073
4244
  ref: publicIdFromInt(input.loanId),
@@ -4078,16 +4249,14 @@ var InstantLoansModule = class {
4078
4249
  depositWindowSeconds: input.depositWindowSeconds
4079
4250
  },
4080
4251
  collateral: {
4081
- poolId: collateralPoolId,
4082
4252
  asset: collateralAsset,
4083
- chain: depositTarget.chain,
4253
+ poolId: collateralPoolId,
4084
4254
  decimals: collateralDecimals,
4085
4255
  amount: collateralAmount
4086
4256
  },
4087
4257
  borrow: {
4258
+ ...borrowIdentifier,
4088
4259
  poolId: borrowPoolId,
4089
- asset: borrowAsset,
4090
- chain: repayTarget.chain,
4091
4260
  decimals: borrowedDecimals,
4092
4261
  amount: input.borrowAmount,
4093
4262
  destination: input.borrowDestination
@@ -4107,37 +4276,86 @@ var InstantLoansModule = class {
4107
4276
  };
4108
4277
  }
4109
4278
  async createInitialDepositQuote(input) {
4110
- const inflowFee = await this.lending.estimateInflowFee({
4111
- asset: input.asset,
4112
- chain: input.target.chain
4113
- });
4279
+ const targets = {};
4280
+ for (const chain of INSTANT_LOAN_CHAINS) {
4281
+ const target = input.targets[chain];
4282
+ if (!target) continue;
4283
+ assertTargetAsset(target, input.asset);
4284
+ const inflowFee = await this.lending.estimateInflowFee(target);
4285
+ targets[chain] = {
4286
+ amount: input.collateralAmount + inflowFee.totalFee,
4287
+ inflowFeeAmount: inflowFee.totalFee,
4288
+ target
4289
+ };
4290
+ }
4114
4291
  return {
4115
- amount: input.collateralAmount + inflowFee.totalFee,
4116
4292
  decimals: input.decimals,
4117
4293
  collateralAmount: input.collateralAmount,
4118
- inflowFeeAmount: inflowFee.totalFee,
4119
4294
  asset: input.asset,
4120
- chain: input.target.chain,
4121
- target: input.target,
4295
+ targets,
4122
4296
  detectedTimestamp: input.detectedTimestamp,
4123
4297
  expiryTimestamp: input.expiryTimestamp
4124
4298
  };
4125
4299
  }
4126
- async estimateRepaymentInflowFee(asset, chain) {
4127
- try {
4128
- const fee = await this.lending.estimateInflowFee({
4129
- asset,
4130
- chain
4131
- });
4132
- return { totalFee: fee.totalFee, estimateAvailable: true };
4133
- } catch {
4134
- return {
4135
- totalFee: getRepaymentInflowFeeFallback(asset, chain),
4136
- estimateAvailable: false
4137
- };
4138
- }
4139
- }
4140
- requireApi(action) {
4300
+ async resolveInstantLoanInflowTargets(request) {
4301
+ const selectedPool = await getPoolById(
4302
+ this.canisterContext,
4303
+ request.poolId
4304
+ );
4305
+ const baseRequest = {
4306
+ profileId: request.profileId,
4307
+ poolId: request.poolId,
4308
+ action: request.action,
4309
+ mechanism: SupplyPlanType.transfer
4310
+ };
4311
+ const poolChainTarget = await resolveSupplyTargetForPool(
4312
+ this.canisterContext,
4313
+ baseRequest,
4314
+ selectedPool
4315
+ );
4316
+ const poolChain = poolChainTarget.chain;
4317
+ const targets = {
4318
+ [poolChain]: poolChainTarget
4319
+ };
4320
+ if (poolChain === Chain.ICP) {
4321
+ return { targets };
4322
+ }
4323
+ const icpTarget = await resolveSupplyTargetForPool(
4324
+ this.canisterContext,
4325
+ { ...baseRequest, chain: Chain.ICP },
4326
+ selectedPool
4327
+ );
4328
+ targets[Chain.ICP] = icpTarget;
4329
+ return { targets };
4330
+ }
4331
+ async createRepaymentTargetQuotes(input) {
4332
+ const quotes = {};
4333
+ for (const chain of INSTANT_LOAN_CHAINS) {
4334
+ const target = input.targets[chain];
4335
+ if (!target) continue;
4336
+ assertTargetAsset(target, input.asset);
4337
+ const repaymentInflowFee = input.baseRepaymentAmount > 0n ? await this.estimateRepaymentInflowFee(target) : { totalFee: 0n, estimateAvailable: false };
4338
+ quotes[chain] = {
4339
+ amount: input.baseRepaymentAmount + repaymentInflowFee.totalFee,
4340
+ inflowFeeAmount: repaymentInflowFee.totalFee,
4341
+ inflowFeeEstimateAvailable: repaymentInflowFee.estimateAvailable,
4342
+ target
4343
+ };
4344
+ }
4345
+ return quotes;
4346
+ }
4347
+ async estimateRepaymentInflowFee(target) {
4348
+ try {
4349
+ const fee = await this.lending.estimateInflowFee(target);
4350
+ return { totalFee: fee.totalFee, estimateAvailable: true };
4351
+ } catch {
4352
+ return {
4353
+ totalFee: getRepaymentInflowFeeFallback(target.asset, target.chain),
4354
+ estimateAvailable: false
4355
+ };
4356
+ }
4357
+ }
4358
+ requireApi(action) {
4141
4359
  if (!this.apiClient) {
4142
4360
  throw new LiquidiumError(
4143
4361
  LiquidiumErrorCode.VALIDATION_ERROR,
@@ -4168,10 +4386,10 @@ var InstantLoansModule = class {
4168
4386
  ]);
4169
4387
  const ltvCalculation = new QuoteModule().calculateLtv(
4170
4388
  {
4171
- borrowAmount: request.borrowAmount,
4172
- borrowPoolId: request.borrowPoolId,
4173
- collateralAmount: request.collateralAmount,
4174
- collateralPoolId: request.collateralPoolId
4389
+ borrowAmount: request.borrow.amount,
4390
+ borrowPoolId: request.borrow.poolId,
4391
+ collateralAmount: request.collateral.amount,
4392
+ collateralPoolId: request.collateral.poolId
4175
4393
  },
4176
4394
  pools,
4177
4395
  assetPrices
@@ -4193,6 +4411,15 @@ function getRepaymentInflowFeeFallback(asset, chain) {
4193
4411
  }
4194
4412
  return 0n;
4195
4413
  }
4414
+ function assertTargetAsset(target, expectedAsset) {
4415
+ if (target.asset === expectedAsset) {
4416
+ return;
4417
+ }
4418
+ throw new LiquidiumError(
4419
+ LiquidiumErrorCode.INTERNAL,
4420
+ `Instant loan pool target asset ${target.asset} does not match ${expectedAsset}`
4421
+ );
4422
+ }
4196
4423
  function calculateInterestBufferAmount(borrowPosition, annualBorrowRate) {
4197
4424
  const totalDebtAmount = calculateTotalDebtAmount(borrowPosition);
4198
4425
  if (totalDebtAmount <= 0n || annualBorrowRate <= 0n) {
@@ -4285,13 +4512,23 @@ function deriveDepositExpiryTimestamp(input) {
4285
4512
  return input.depositDetectedTimestamp + input.depositWindowSeconds;
4286
4513
  }
4287
4514
  function validateCreateRequest(request) {
4288
- if (request.collateralAmount <= 0n) {
4515
+ getInstantLoanAssetIdentifier(
4516
+ request.borrow.asset,
4517
+ request.borrow.chain,
4518
+ "borrow"
4519
+ );
4520
+ getInstantLoanAssetIdentifier(
4521
+ request.collateral.asset,
4522
+ request.refund.chain,
4523
+ "refund"
4524
+ );
4525
+ if (request.collateral.amount <= 0n) {
4289
4526
  throw new LiquidiumError(
4290
4527
  LiquidiumErrorCode.VALIDATION_ERROR,
4291
4528
  "Instant loan collateral amount must be greater than zero"
4292
4529
  );
4293
4530
  }
4294
- if (request.borrowAmount <= 0n) {
4531
+ if (request.borrow.amount <= 0n) {
4295
4532
  throw new LiquidiumError(
4296
4533
  LiquidiumErrorCode.VALIDATION_ERROR,
4297
4534
  "Instant loan borrow amount must be greater than zero"
@@ -4316,45 +4553,200 @@ function throwLtvCalculationError(error) {
4316
4553
  error?.message ?? "Unable to calculate instant loan LTV"
4317
4554
  );
4318
4555
  }
4319
- function addressFromAccountInput(account) {
4320
- const address = typeof account === "string" ? account.trim() : account.address.trim();
4321
- if (!address) {
4556
+ function inferInstantLoanDeliveryChain(destination, asset) {
4557
+ if (destination.type !== "ChainAddress") {
4558
+ return Chain.ICP;
4559
+ }
4560
+ if (asset === Asset.BTC) {
4561
+ return Chain.BTC;
4562
+ }
4563
+ if (asset === Asset.USDC || asset === Asset.USDT) {
4564
+ return Chain.ETH;
4565
+ }
4566
+ return Chain.ICP;
4567
+ }
4568
+ function resolveInstantLoanDestination(params) {
4569
+ getInstantLoanAssetIdentifier(params.asset, params.chain, params.role);
4570
+ if (typeof params.destination === "string") {
4571
+ return resolveInstantLoanStringDestination({
4572
+ destination: params.destination,
4573
+ asset: params.asset,
4574
+ role: params.role,
4575
+ chain: params.chain,
4576
+ normalizeExternalDestination: params.normalizeExternalDestination
4577
+ });
4578
+ }
4579
+ assertInstantLoanDestinationTypeMatchesChain({
4580
+ destinationType: params.destination.type,
4581
+ asset: params.asset,
4582
+ role: params.role,
4583
+ chain: params.chain
4584
+ });
4585
+ switch (params.destination.type) {
4586
+ case "ChainAddress":
4587
+ assertExternalInstantLoanDestinationSupported(params.asset);
4588
+ return {
4589
+ External: params.normalizeExternalDestination(
4590
+ normalizeInstantLoanDestinationAddress(params.destination.address),
4591
+ params.asset,
4592
+ params.chain
4593
+ )
4594
+ };
4595
+ case "IcPrincipal":
4596
+ return {
4597
+ Native: normalizeInstantLoanPrincipal(
4598
+ normalizeInstantLoanDestinationAddress(params.destination.address)
4599
+ )
4600
+ };
4601
+ case "IcpAccountIdentifier":
4602
+ return parseAccountIdentifierInstantLoanDestination(
4603
+ normalizeInstantLoanDestinationAddress(params.destination.address)
4604
+ );
4605
+ case "IcrcAccount":
4606
+ return parseIcrcInstantLoanDestination(
4607
+ normalizeInstantLoanDestinationAddress(params.destination.address)
4608
+ );
4609
+ }
4610
+ }
4611
+ function resolveInstantLoanStringDestination(params) {
4612
+ const address = normalizeInstantLoanDestinationAddress(params.destination);
4613
+ if (params.chain === Chain.ICP && params.asset !== Asset.ICP) {
4614
+ try {
4615
+ return parsePrincipalInstantLoanDestination(address);
4616
+ } catch {
4617
+ throwInvalidCkInstantLoanDestination(params.role);
4618
+ }
4619
+ }
4620
+ if (params.chain !== Chain.ICP) {
4621
+ assertExternalInstantLoanDestinationSupported(params.asset);
4622
+ return {
4623
+ External: params.normalizeExternalDestination(
4624
+ address,
4625
+ params.asset,
4626
+ params.chain
4627
+ )
4628
+ };
4629
+ }
4630
+ for (const parser of [
4631
+ parseAccountIdentifierInstantLoanDestination,
4632
+ parsePrincipalInstantLoanDestination,
4633
+ parseIcrcInstantLoanDestination
4634
+ ]) {
4635
+ try {
4636
+ return parser(address);
4637
+ } catch {
4638
+ }
4639
+ }
4640
+ throwInvalidIcpInstantLoanDestination();
4641
+ }
4642
+ function getInstantLoanAssetIdentifier(asset, chain, role) {
4643
+ const identifier = { asset, chain };
4644
+ if (isAssetIdentifier(identifier)) {
4645
+ return identifier;
4646
+ }
4647
+ throw new LiquidiumError(
4648
+ LiquidiumErrorCode.VALIDATION_ERROR,
4649
+ `${chain} instant loan ${role} delivery is not supported for ${asset}`
4650
+ );
4651
+ }
4652
+ function assertInstantLoanDestinationTypeMatchesChain(params) {
4653
+ if (params.chain === Chain.ICP && params.asset !== Asset.ICP) {
4654
+ if (params.destinationType === "IcPrincipal") {
4655
+ return;
4656
+ }
4657
+ throwInvalidCkInstantLoanDestination(params.role);
4658
+ }
4659
+ if (params.chain === Chain.ICP && params.asset === Asset.ICP) {
4660
+ if (params.destinationType !== "ChainAddress") {
4661
+ return;
4662
+ }
4663
+ throwInvalidIcpInstantLoanDestination();
4664
+ }
4665
+ if (params.destinationType === "ChainAddress") {
4666
+ return;
4667
+ }
4668
+ if (params.destinationType === "IcPrincipal") {
4322
4669
  throw new LiquidiumError(
4323
4670
  LiquidiumErrorCode.VALIDATION_ERROR,
4324
- "Instant loan account address must be non-empty"
4671
+ `${params.chain} instant loan ${params.role} destination must be an external chain address for ${params.asset}`
4325
4672
  );
4326
4673
  }
4327
- return address;
4674
+ throw new LiquidiumError(
4675
+ LiquidiumErrorCode.VALIDATION_ERROR,
4676
+ `${params.asset} instant loan ${params.role} destination only supports ChainAddress or IcPrincipal destinations`
4677
+ );
4328
4678
  }
4329
- function accountFromCanister(account) {
4330
- if ("Native" in account) {
4331
- return { type: "Native", principal: account.Native.toText() };
4679
+ function throwInvalidCkInstantLoanDestination(role) {
4680
+ throw new LiquidiumError(
4681
+ LiquidiumErrorCode.VALIDATION_ERROR,
4682
+ `ICP instant loan ${role} destination must be an IC principal`
4683
+ );
4684
+ }
4685
+ function assertExternalInstantLoanDestinationSupported(asset) {
4686
+ if (asset !== Asset.ICP) {
4687
+ return;
4332
4688
  }
4333
- if ("AccountIdentifier" in account) {
4334
- return {
4335
- type: "AccountIdentifier",
4336
- address: account.AccountIdentifier
4337
- };
4689
+ throwInvalidIcpInstantLoanDestination();
4690
+ }
4691
+ function throwInvalidIcpInstantLoanDestination() {
4692
+ throw new LiquidiumError(
4693
+ LiquidiumErrorCode.INVALID_ADDRESS,
4694
+ "ICP instant loan destination must be an IC principal, ICP account identifier, or ICRC account"
4695
+ );
4696
+ }
4697
+ function parsePrincipalInstantLoanDestination(address) {
4698
+ return { Native: normalizeInstantLoanPrincipal(address) };
4699
+ }
4700
+ function parseAccountIdentifierInstantLoanDestination(address) {
4701
+ if (address.length !== ICP_ACCOUNT_IDENTIFIER_HEX_LENGTH) {
4702
+ throw new LiquidiumError(
4703
+ LiquidiumErrorCode.INVALID_ADDRESS,
4704
+ "Invalid ICP account identifier"
4705
+ );
4338
4706
  }
4339
- if ("Icrc" in account) {
4340
- const subaccount = normalizeOptionalSubaccount(account.Icrc.subaccount[0]);
4341
- return {
4342
- type: "Icrc",
4343
- owner: account.Icrc.owner.toText(),
4344
- subaccount,
4345
- address: encodeIcrcAccount({
4346
- owner: account.Icrc.owner,
4347
- subaccount
4348
- })
4349
- };
4707
+ try {
4708
+ return { AccountIdentifier: normalizeIcpAccountIdentifier(address) };
4709
+ } catch {
4710
+ throw new LiquidiumError(
4711
+ LiquidiumErrorCode.INVALID_ADDRESS,
4712
+ "Invalid ICP account identifier"
4713
+ );
4350
4714
  }
4351
- return { type: "External", address: account.External };
4352
4715
  }
4353
- function normalizeOptionalSubaccount(subaccount) {
4354
- if (!subaccount) {
4355
- return void 0;
4716
+ function parseIcrcInstantLoanDestination(address) {
4717
+ let decoded;
4718
+ try {
4719
+ decoded = decodeIcrcAccountAddress(address);
4720
+ } catch {
4721
+ throw new LiquidiumError(
4722
+ LiquidiumErrorCode.INVALID_ADDRESS,
4723
+ "Invalid instant loan ICRC destination"
4724
+ );
4725
+ }
4726
+ return { Icrc: decoded.account.address };
4727
+ }
4728
+ function normalizeInstantLoanPrincipal(address) {
4729
+ try {
4730
+ return Principal.fromText(address).toText();
4731
+ } catch {
4732
+ throw new LiquidiumError(
4733
+ LiquidiumErrorCode.INVALID_ADDRESS,
4734
+ "Invalid instant loan IC principal destination"
4735
+ );
4356
4736
  }
4357
- return subaccount instanceof Uint8Array ? subaccount : Uint8Array.from(subaccount);
4737
+ }
4738
+ function normalizeInstantLoanDestinationAddress(address) {
4739
+ const trimmedAddress = address.trim();
4740
+ if (!trimmedAddress) {
4741
+ throw new LiquidiumError(
4742
+ LiquidiumErrorCode.VALIDATION_ERROR,
4743
+ "Instant loan account address must be non-empty"
4744
+ );
4745
+ }
4746
+ return trimmedAddress;
4747
+ }
4748
+ function accountFromCanister(account) {
4749
+ return mapCanisterAccountToLiquidiumAccount(account);
4358
4750
  }
4359
4751
  function mapInstantLoanEvent(event) {
4360
4752
  return {
@@ -4371,7 +4763,10 @@ function mapInstantLoanEventType(eventType) {
4371
4763
  type: "LoanCreated",
4372
4764
  loanId: event.loan_id,
4373
4765
  borrowDestination: accountFromCanister(event.borrow_destination),
4374
- collateralAsset: event.lend_asset,
4766
+ collateralAsset: parseInstantLoanAsset(
4767
+ event.lend_asset,
4768
+ "event collateral asset"
4769
+ ),
4375
4770
  borrowAmount: event.borrow_amount,
4376
4771
  collateralPoolId: event.lend_pool_id.toText(),
4377
4772
  refundDestination: accountFromCanister(event.refund_destination),
@@ -4379,7 +4774,10 @@ function mapInstantLoanEventType(eventType) {
4379
4774
  depositWindowSeconds: event.ltv_timer_s,
4380
4775
  profileId: event.lending_profile.toText(),
4381
4776
  borrowPoolId: event.borrow_pool_id.toText(),
4382
- borrowAsset: event.borrow_asset
4777
+ borrowAsset: parseInstantLoanAsset(
4778
+ event.borrow_asset,
4779
+ "event borrow asset"
4780
+ )
4383
4781
  };
4384
4782
  }
4385
4783
  if ("FullLendWithdrawalRequested" in eventType) {
@@ -4546,6 +4944,12 @@ function mapCandidateWire(wire) {
4546
4944
  })
4547
4945
  };
4548
4946
  }
4947
+ function parseInstantLoanAsset(value, label) {
4948
+ return parseApiStringUnion(value, INSTANT_LOAN_ASSETS, {
4949
+ context: INSTANT_LOAN_WIRE_CONTEXT,
4950
+ label
4951
+ });
4952
+ }
4549
4953
  function mapInstantLoansErrorToLiquidiumError(error) {
4550
4954
  const [key, payload] = Object.entries(error)[0];
4551
4955
  switch (key) {
@@ -4687,11 +5091,25 @@ function encodeBytes32Hex(bytes) {
4687
5091
  var idlFactory4 = ({ IDL }) => IDL.Service({
4688
5092
  icrc1_fee: IDL.Func([], [IDL.Nat], ["query"])
4689
5093
  });
4690
- function createCkBtcLedgerActor(canisterContext) {
4691
- const canisterId = CK_CANISTER_IDS.btcLedger;
5094
+ function createIcrcLedgerActor(params) {
5095
+ if (!params.canisterId) {
5096
+ throw new LiquidiumError(
5097
+ LiquidiumErrorCode.SERVICE_UNAVAILABLE,
5098
+ `${params.ledgerName} ledger canister ID is not configured`
5099
+ );
5100
+ }
4692
5101
  return Actor.createActor(idlFactory4, {
4693
- agent: canisterContext.agent,
4694
- canisterId
5102
+ agent: params.canisterContext.agent,
5103
+ canisterId: params.canisterId
5104
+ });
5105
+ }
5106
+
5107
+ // src/core/canisters/ckbtc/ledger.ts
5108
+ function createCkBtcLedgerActor(canisterContext) {
5109
+ return createIcrcLedgerActor({
5110
+ canisterContext,
5111
+ canisterId: CK_CANISTER_IDS.BTC.ledger,
5112
+ ledgerName: "ckBTC"
4695
5113
  });
4696
5114
  }
4697
5115
 
@@ -4720,11 +5138,51 @@ function accountTypeToString(accountType) {
4720
5138
  switch (accountType.type) {
4721
5139
  case "External":
4722
5140
  return `Address:${accountType.data}`;
5141
+ case "Icrc":
5142
+ return `Icrc:${accountType.data}`;
5143
+ case "AccountIdentifier":
5144
+ return `AccountId:${accountType.data}`;
4723
5145
  case "Native":
4724
5146
  return `Principal:${accountType.data}`;
4725
5147
  }
4726
5148
  }
4727
5149
 
5150
+ // src/core/pool-ledger-assets.ts
5151
+ function getPoolLedgerAssetRoute(params) {
5152
+ if (params.asset === Asset.BTC && (params.chain === Chain.BTC || params.chain === Chain.ICP)) {
5153
+ return {
5154
+ ledgerCanisterId: CK_CANISTER_IDS.BTC.ledger,
5155
+ asset: Asset.BTC,
5156
+ chain: params.chain
5157
+ };
5158
+ }
5159
+ if (params.asset === Asset.USDT && (params.chain === Chain.ETH || params.chain === Chain.ICP)) {
5160
+ return {
5161
+ ledgerCanisterId: CK_CANISTER_IDS.USDT.ledger,
5162
+ asset: Asset.USDT,
5163
+ chain: params.chain
5164
+ };
5165
+ }
5166
+ if (params.asset === Asset.USDC && (params.chain === Chain.ETH || params.chain === Chain.ICP)) {
5167
+ return {
5168
+ ledgerCanisterId: CK_CANISTER_IDS.USDC.ledger,
5169
+ asset: Asset.USDC,
5170
+ chain: params.chain
5171
+ };
5172
+ }
5173
+ if (params.asset === Asset.ICP && params.chain === Chain.ICP) {
5174
+ return {
5175
+ ledgerCanisterId: CK_CANISTER_IDS.ICP.ledger,
5176
+ asset: Asset.ICP,
5177
+ chain: Chain.ICP
5178
+ };
5179
+ }
5180
+ throw new LiquidiumError(
5181
+ LiquidiumErrorCode.VALIDATION_ERROR,
5182
+ `IC ledger transfers are not supported for ${params.asset} on ${params.chain}`
5183
+ );
5184
+ }
5185
+
4728
5186
  // src/core/withdraw-minimums.ts
4729
5187
  var MINIMUM_BTC_WITHDRAW_AMOUNT_SATS = 5000n;
4730
5188
  var MINIMUM_STABLECOIN_WITHDRAW_AMOUNT_BASE_UNITS = 1000000n;
@@ -4842,48 +5300,61 @@ var SupplyFlowExecutor = class {
4842
5300
  }
4843
5301
  params;
4844
5302
  async create(request) {
4845
- const target = await resolveSupplyTarget(
5303
+ const requestedMechanism = request.mechanism ?? null;
5304
+ if (requestedMechanism === null && request.walletAdapter === void 0 && (request.account !== void 0 || request.amount !== void 0)) {
5305
+ throw new LiquidiumError(
5306
+ LiquidiumErrorCode.VALIDATION_ERROR,
5307
+ "Wallet-executed supply requires walletAdapter, account, and amount"
5308
+ );
5309
+ }
5310
+ const selectedPool = await getPoolById(
4846
5311
  this.params.canisterContext,
4847
- request
5312
+ request.poolId
4848
5313
  );
4849
- const instruction = {
4850
- poolId: request.poolId,
4851
- asset: target.asset,
4852
- chain: target.chain,
4853
- action: request.action,
4854
- target
4855
- };
4856
- const mechanism = resolveSupplyMechanism({
4857
- asset: instruction.asset,
4858
- chain: instruction.chain,
4859
- requestedMechanism: request.mechanism
4860
- });
5314
+ const target = await resolveSupplyTargetForPool(
5315
+ this.params.canisterContext,
5316
+ {
5317
+ profileId: request.profileId,
5318
+ poolId: request.poolId,
5319
+ action: request.action,
5320
+ mechanism: requestedMechanism,
5321
+ chain: request.chain
5322
+ },
5323
+ selectedPool
5324
+ );
5325
+ const mechanism = requestedMechanism === SupplyPlanType.contractInteraction ? SupplyPlanType.contractInteraction : SupplyPlanType.transfer;
4861
5326
  const defaultSubmitInflowRequest = getDefaultSubmitInflowRequest({
4862
5327
  action: request.action,
4863
- chain: mechanism === SupplyPlanType.contractInteraction ? Chain.ETH : instruction.chain
5328
+ chain: mechanism === SupplyPlanType.contractInteraction ? Chain.ETH : target.chain
4864
5329
  });
4865
5330
  let txid;
4866
5331
  switch (mechanism) {
4867
5332
  case SupplyPlanType.transfer:
4868
- if (request.walletAdapter) {
4869
- txid = await this.sendAndSubmitNativeSupplyInflow({
5333
+ if (isWalletTransferSupplyRequest(request)) {
5334
+ txid = await this.sendAndSubmitTransferSupplyInflow({
4870
5335
  request,
4871
- instruction,
5336
+ target,
4872
5337
  defaultSubmitInflowRequest
4873
5338
  });
4874
5339
  }
4875
5340
  break;
4876
5341
  case SupplyPlanType.contractInteraction:
5342
+ if (!isContractInteractionSupplyRequest(request)) {
5343
+ throw new LiquidiumError(
5344
+ LiquidiumErrorCode.VALIDATION_ERROR,
5345
+ "Contract-interaction supply requires contract-interaction request fields"
5346
+ );
5347
+ }
4877
5348
  txid = await this.executeContractSupply({
4878
5349
  request,
4879
- instruction,
5350
+ target,
4880
5351
  defaultSubmitInflowRequest
4881
5352
  });
4882
5353
  break;
4883
5354
  }
4884
5355
  return {
4885
5356
  type: mechanism,
4886
- target: instruction.target,
5357
+ target,
4887
5358
  txid,
4888
5359
  status: createLiquidiumStatus({
4889
5360
  operation: mapSupplyActionToStatusOperation(request.action),
@@ -4891,7 +5362,7 @@ var SupplyFlowExecutor = class {
4891
5362
  }),
4892
5363
  submit: async (submitRequest) => {
4893
5364
  return await this.submitSupplyFlowInflow({
4894
- instruction,
5365
+ target,
4895
5366
  mechanism,
4896
5367
  defaultSubmitInflowRequest,
4897
5368
  submitRequest
@@ -4900,7 +5371,10 @@ var SupplyFlowExecutor = class {
4900
5371
  };
4901
5372
  }
4902
5373
  async getEvmSupplyContext(request) {
4903
- const selectedPool = await this.params.getPoolById(request.poolId);
5374
+ const selectedPool = await getPoolById(
5375
+ this.params.canisterContext,
5376
+ request.poolId
5377
+ );
4904
5378
  return await this.getEvmSupplyContextForPool({
4905
5379
  request,
4906
5380
  asset: selectedPool.asset,
@@ -4964,14 +5438,8 @@ var SupplyFlowExecutor = class {
4964
5438
  approvalStrategy
4965
5439
  };
4966
5440
  }
4967
- async sendAndSubmitNativeSupplyInflow(params) {
4968
- const { request, instruction, defaultSubmitInflowRequest } = params;
4969
- if (instruction.target.type !== "nativeAddress") {
4970
- throw new LiquidiumError(
4971
- LiquidiumErrorCode.VALIDATION_ERROR,
4972
- "Wallet-executed supply requires a native-address target"
4973
- );
4974
- }
5441
+ async sendAndSubmitTransferSupplyInflow(params) {
5442
+ const { request, target, defaultSubmitInflowRequest } = params;
4975
5443
  if (!request.walletAdapter) {
4976
5444
  throw new LiquidiumError(
4977
5445
  LiquidiumErrorCode.VALIDATION_ERROR,
@@ -4991,16 +5459,27 @@ var SupplyFlowExecutor = class {
4991
5459
  "Wallet-executed supply requires a positive amount"
4992
5460
  );
4993
5461
  }
4994
- const txid = await this.sendNativeSupplyTransaction({
5462
+ const txid = target.chain === Chain.ICP ? await this.sendIcrcSupplyTransaction({
4995
5463
  walletAdapter: request.walletAdapter,
4996
- chain: instruction.chain,
4997
- toAddress: instruction.target.address,
5464
+ asset: target.asset,
5465
+ chain: target.chain,
5466
+ transfer: {
5467
+ ledgerCanisterId: getPoolLedgerAssetRoute(target).ledgerCanisterId,
5468
+ to: decodeIcrcAccountAddress(target.address).account,
5469
+ amount: request.amount
5470
+ },
5471
+ senderAccount: account,
5472
+ action: request.action
5473
+ }) : await this.sendChainAddressSupplyTransaction({
5474
+ walletAdapter: request.walletAdapter,
5475
+ chain: target.chain,
5476
+ toAddress: target.address,
4998
5477
  amount: request.amount,
4999
5478
  senderAccount: account,
5000
- asset: instruction.asset,
5479
+ asset: target.asset,
5001
5480
  action: request.action
5002
5481
  });
5003
- if (shouldSubmitInflow({ instruction, mechanism: SupplyPlanType.transfer })) {
5482
+ if (shouldSubmitInflow({ target, mechanism: SupplyPlanType.transfer })) {
5004
5483
  try {
5005
5484
  await this.submitInflowWithRetry(txid, defaultSubmitInflowRequest);
5006
5485
  } catch {
@@ -5027,11 +5506,11 @@ var SupplyFlowExecutor = class {
5027
5506
  });
5028
5507
  }
5029
5508
  async executeContractSupply(params) {
5030
- const { request, instruction, defaultSubmitInflowRequest } = params;
5031
- if (instruction.target.type !== "icrcAccount" || instruction.chain !== Chain.ETH) {
5509
+ const { request, target, defaultSubmitInflowRequest } = params;
5510
+ if (!isEthStablecoin(target.asset, target.chain)) {
5032
5511
  throw new LiquidiumError(
5033
5512
  LiquidiumErrorCode.VALIDATION_ERROR,
5034
- "Contract-interaction supply is only supported for ETH ICRC-account targets"
5513
+ "Contract-interaction supply is only supported for ETH stablecoin targets"
5035
5514
  );
5036
5515
  }
5037
5516
  const walletAddressInput = request.account?.trim();
@@ -5070,8 +5549,8 @@ var SupplyFlowExecutor = class {
5070
5549
  amount: request.amount,
5071
5550
  action: request.action
5072
5551
  },
5073
- asset: instruction.asset,
5074
- chain: instruction.chain
5552
+ asset: target.asset,
5553
+ chain: target.chain
5075
5554
  });
5076
5555
  const supplyAmount = BigInt(evmSupplyContext.amount);
5077
5556
  if (BigInt(evmSupplyContext.balance) < supplyAmount) {
@@ -5127,7 +5606,7 @@ var SupplyFlowExecutor = class {
5127
5606
  amount: supplyAmount,
5128
5607
  poolId: request.poolId,
5129
5608
  profileId: request.profileId,
5130
- destinationAccount: instruction.target.account,
5609
+ destinationAccount: target.address,
5131
5610
  action: request.action
5132
5611
  }),
5133
5612
  `supply-${request.action}-deposit-erc20`
@@ -5138,7 +5617,7 @@ var SupplyFlowExecutor = class {
5138
5617
  }
5139
5618
  return depositTxid;
5140
5619
  }
5141
- async sendNativeSupplyTransaction(params) {
5620
+ async sendChainAddressSupplyTransaction(params) {
5142
5621
  switch (params.chain) {
5143
5622
  case Chain.BTC: {
5144
5623
  if (!params.walletAdapter.sendBtcTransaction) {
@@ -5152,8 +5631,7 @@ var SupplyFlowExecutor = class {
5152
5631
  toAddress: params.toAddress,
5153
5632
  amountSats: params.amount,
5154
5633
  account: params.senderAccount,
5155
- actionType: `supply-${params.action}`,
5156
- transferMode: TransferMode.native
5634
+ actionType: `supply-${params.action}`
5157
5635
  });
5158
5636
  }
5159
5637
  case Chain.ETH: {
@@ -5168,7 +5646,6 @@ var SupplyFlowExecutor = class {
5168
5646
  chain: Chain.ETH,
5169
5647
  account: params.senderAccount,
5170
5648
  actionType: `supply-${params.action}`,
5171
- transferMode: TransferMode.native,
5172
5649
  transaction: createTransferErc20Transaction({
5173
5650
  tokenAddress: getEthStablecoinContractAddress(params.asset),
5174
5651
  recipientAddress: params.toAddress,
@@ -5180,7 +5657,6 @@ var SupplyFlowExecutor = class {
5180
5657
  chain: Chain.ETH,
5181
5658
  account: params.senderAccount,
5182
5659
  actionType: `supply-${params.action}`,
5183
- transferMode: TransferMode.native,
5184
5660
  transaction: {
5185
5661
  to: params.toAddress,
5186
5662
  value: params.amount.toString()
@@ -5190,10 +5666,32 @@ var SupplyFlowExecutor = class {
5190
5666
  default:
5191
5667
  throw new LiquidiumError(
5192
5668
  LiquidiumErrorCode.VALIDATION_ERROR,
5193
- `Native-address wallet execution is not supported for ${params.chain}`
5669
+ `Chain-address wallet execution is not supported for ${params.chain}`
5194
5670
  );
5195
5671
  }
5196
5672
  }
5673
+ async sendIcrcSupplyTransaction(params) {
5674
+ if (!params.walletAdapter.sendIcrcTransfer) {
5675
+ throw new LiquidiumError(
5676
+ LiquidiumErrorCode.VALIDATION_ERROR,
5677
+ "ICRC wallet adapter does not support ledger transfers"
5678
+ );
5679
+ }
5680
+ const route = getPoolLedgerAssetRoute({
5681
+ asset: params.asset,
5682
+ chain: params.chain
5683
+ });
5684
+ return await params.walletAdapter.sendIcrcTransfer({
5685
+ chain: Chain.ICP,
5686
+ asset: route.asset,
5687
+ transfer: {
5688
+ ...params.transfer,
5689
+ ledgerCanisterId: route.ledgerCanisterId
5690
+ },
5691
+ account: params.senderAccount,
5692
+ actionType: `supply-${params.action}`
5693
+ });
5694
+ }
5197
5695
  async submitInflowWithRetry(txid, extraRequest) {
5198
5696
  if (!extraRequest) {
5199
5697
  throw new LiquidiumError(
@@ -5222,7 +5720,6 @@ var SupplyFlowExecutor = class {
5222
5720
  chain: Chain.ETH,
5223
5721
  account: walletAddress,
5224
5722
  actionType,
5225
- transferMode: TransferMode.native,
5226
5723
  transaction: request
5227
5724
  });
5228
5725
  }
@@ -5311,13 +5808,19 @@ function getUnknownErrorMessage(error) {
5311
5808
  }
5312
5809
  function getDefaultSubmitInflowRequest(params) {
5313
5810
  if (params.chain !== Chain.BTC && params.chain !== Chain.ETH) {
5314
- return void 0;
5811
+ return null;
5315
5812
  }
5316
5813
  return {
5317
5814
  chain: params.chain,
5318
5815
  operation: mapSupplyActionToStatusOperation(params.action)
5319
5816
  };
5320
5817
  }
5818
+ function isWalletTransferSupplyRequest(request) {
5819
+ return request.mechanism !== SupplyPlanType.contractInteraction && request.walletAdapter !== void 0;
5820
+ }
5821
+ function isContractInteractionSupplyRequest(request) {
5822
+ return request.mechanism === SupplyPlanType.contractInteraction;
5823
+ }
5321
5824
  function mapSupplyActionToStatusOperation(action) {
5322
5825
  return action === SupplyAction.repayment ? "repayment" : "deposit";
5323
5826
  }
@@ -5325,7 +5828,10 @@ function shouldSubmitInflow(params) {
5325
5828
  if (params.mechanism !== SupplyPlanType.transfer) {
5326
5829
  return true;
5327
5830
  }
5328
- return !isEthStablecoin(params.instruction.asset, params.instruction.chain);
5831
+ if (params.target.chain === Chain.ICP) {
5832
+ return false;
5833
+ }
5834
+ return !isEthStablecoin(params.target.asset, params.target.chain);
5329
5835
  }
5330
5836
  function mapCanisterOutflowDetails(outflow) {
5331
5837
  const rawOutflowType = getVariantKey(outflow.outflow_type);
@@ -5339,41 +5845,31 @@ function mapCanisterOutflowDetails(outflow) {
5339
5845
  };
5340
5846
  }
5341
5847
  function mapCanisterAccountType(receiver) {
5342
- if ("Native" in receiver) {
5343
- return {
5344
- type: "Native",
5345
- account: receiver.Native.toText()
5346
- };
5347
- }
5348
- if ("AccountIdentifier" in receiver) {
5349
- return {
5350
- type: "AccountIdentifier",
5351
- account: receiver.AccountIdentifier
5352
- };
5353
- }
5354
- if ("Icrc" in receiver) {
5355
- const subaccount = normalizeOptionalSubaccount2(receiver.Icrc.subaccount[0]);
5356
- return {
5357
- type: "Icrc",
5358
- owner: receiver.Icrc.owner.toText(),
5359
- subaccount,
5360
- account: encodeIcrcAccount({
5361
- owner: receiver.Icrc.owner,
5362
- subaccount
5363
- })
5364
- };
5848
+ return mapCanisterAccountToLiquidiumAccount(receiver);
5849
+ }
5850
+ function parseOutflowDestination(params) {
5851
+ const destination = normalizeOutflowDestinationInput(params.destination);
5852
+ const parsedAccount = destination.type ? parseOutflowDestinationWithHint(destination) : parseOutflowDestinationAutomatically(destination.address);
5853
+ assertDestinationTypeSupportedByChain({
5854
+ accountType: parsedAccount.accountType,
5855
+ poolChain: params.poolChain,
5856
+ destinationChain: params.destinationChain
5857
+ });
5858
+ if (parsedAccount.accountType !== "ChainAddress") {
5859
+ return parsedAccount;
5365
5860
  }
5861
+ const externalAddress = normalizeExternalAddress({
5862
+ address: parsedAccount.address,
5863
+ asset: params.asset,
5864
+ chain: params.destinationChain
5865
+ });
5366
5866
  return {
5367
- type: "External",
5368
- account: receiver.External
5867
+ address: externalAddress,
5868
+ accountType: "ChainAddress",
5869
+ canisterAccount: { External: externalAddress },
5870
+ messageAccount: { type: "External", data: externalAddress }
5369
5871
  };
5370
5872
  }
5371
- function normalizeOptionalSubaccount2(subaccount) {
5372
- if (!subaccount) {
5373
- return void 0;
5374
- }
5375
- return subaccount instanceof Uint8Array ? subaccount : Uint8Array.from(subaccount);
5376
- }
5377
5873
  function normalizeOutflowType(rawOutflowType) {
5378
5874
  switch (rawOutflowType) {
5379
5875
  case "Withdraw":
@@ -5396,6 +5892,117 @@ function mapWalletChainToLendingChain(chain) {
5396
5892
  case Chain.ETH:
5397
5893
  return { ETH: null };
5398
5894
  }
5895
+ throw new LiquidiumError(
5896
+ LiquidiumErrorCode.VALIDATION_ERROR,
5897
+ `Message signing is not supported for ${String(chain)}`
5898
+ );
5899
+ }
5900
+ function normalizeOutflowDestinationInput(destination) {
5901
+ if (typeof destination === "string") {
5902
+ return { address: destination, type: null };
5903
+ }
5904
+ return destination;
5905
+ }
5906
+ function parseOutflowDestinationWithHint(destination) {
5907
+ try {
5908
+ switch (destination.type) {
5909
+ case "ChainAddress":
5910
+ return parseExternalDestination(destination.address);
5911
+ case "IcPrincipal":
5912
+ return parseIcPrincipalDestination(destination.address);
5913
+ case "IcrcAccount":
5914
+ return parseIcrcDestination(destination.address);
5915
+ case "IcpAccountIdentifier":
5916
+ return parseAccountIdentifierDestination(destination.address);
5917
+ default:
5918
+ throw new LiquidiumError(
5919
+ LiquidiumErrorCode.VALIDATION_ERROR,
5920
+ `Unsupported outflow account type: ${String(destination.type)}`
5921
+ );
5922
+ }
5923
+ } catch (error) {
5924
+ if (error instanceof LiquidiumError) {
5925
+ throw error;
5926
+ }
5927
+ throw new LiquidiumError(
5928
+ LiquidiumErrorCode.INVALID_ADDRESS,
5929
+ `Invalid ${destination.type} outflow destination`
5930
+ );
5931
+ }
5932
+ }
5933
+ function parseOutflowDestinationAutomatically(address) {
5934
+ const parsers = [
5935
+ parseAccountIdentifierDestination,
5936
+ parseIcPrincipalDestination,
5937
+ parseIcrcDestination
5938
+ ];
5939
+ for (const parser of parsers) {
5940
+ try {
5941
+ return parser(address);
5942
+ } catch {
5943
+ }
5944
+ }
5945
+ return parseExternalDestination(address);
5946
+ }
5947
+ function parseExternalDestination(address) {
5948
+ return {
5949
+ address,
5950
+ accountType: "ChainAddress",
5951
+ canisterAccount: { External: address },
5952
+ messageAccount: { type: "External", data: address }
5953
+ };
5954
+ }
5955
+ function parseIcPrincipalDestination(address) {
5956
+ const principal = Principal.fromText(address);
5957
+ const principalText = principal.toText();
5958
+ return {
5959
+ address: principalText,
5960
+ accountType: "IcPrincipal",
5961
+ canisterAccount: { Native: principal },
5962
+ messageAccount: { type: "Native", data: principalText }
5963
+ };
5964
+ }
5965
+ function parseIcrcDestination(address) {
5966
+ const decoded = decodeIcrcAccountAddress(address);
5967
+ return {
5968
+ address: decoded.account.address,
5969
+ accountType: "IcrcAccount",
5970
+ canisterAccount: {
5971
+ Icrc: {
5972
+ owner: decoded.owner,
5973
+ subaccount: decoded.subaccount ? [decoded.subaccount] : []
5974
+ }
5975
+ },
5976
+ messageAccount: { type: "Icrc", data: decoded.account.address }
5977
+ };
5978
+ }
5979
+ function parseAccountIdentifierDestination(address) {
5980
+ const accountIdentifier = normalizeIcpAccountIdentifier(address);
5981
+ return {
5982
+ address: accountIdentifier,
5983
+ accountType: "IcpAccountIdentifier",
5984
+ canisterAccount: { AccountIdentifier: accountIdentifier },
5985
+ messageAccount: { type: "AccountIdentifier", data: accountIdentifier }
5986
+ };
5987
+ }
5988
+ function assertDestinationTypeSupportedByChain(params) {
5989
+ if (params.poolChain === Chain.BTC || params.poolChain === Chain.ETH) {
5990
+ if (params.destinationChain === params.poolChain && params.accountType === "ChainAddress") {
5991
+ return;
5992
+ }
5993
+ if (params.destinationChain === Chain.ICP && params.accountType === "IcPrincipal") {
5994
+ return;
5995
+ }
5996
+ }
5997
+ if (params.poolChain === Chain.ICP && params.destinationChain === Chain.ICP) {
5998
+ if (params.accountType === "IcpAccountIdentifier" || params.accountType === "IcrcAccount" || params.accountType === "IcPrincipal") {
5999
+ return;
6000
+ }
6001
+ }
6002
+ throw new LiquidiumError(
6003
+ LiquidiumErrorCode.VALIDATION_ERROR,
6004
+ "Target pool does not support this address type"
6005
+ );
5399
6006
  }
5400
6007
 
5401
6008
  // src/modules/lending/lending.ts
@@ -5417,16 +6024,13 @@ var LendingModule = class {
5417
6024
  * @returns A signable {@link WithdrawAction} with `submit` wired to the canister.
5418
6025
  */
5419
6026
  async prepareWithdraw(request) {
5420
- const destinationAccount = request.receiverAddress.trim();
6027
+ const destination = resolveOutflowDestinationInput({
6028
+ receiver: request.receiver,
6029
+ errorMessage: "Withdraw requires a custom outflow account"
6030
+ });
5421
6031
  const signerAccount = normalizeEvmAddress(
5422
6032
  request.signerWalletAddress.trim()
5423
6033
  );
5424
- if (!destinationAccount) {
5425
- throw new LiquidiumError(
5426
- LiquidiumErrorCode.VALIDATION_ERROR,
5427
- "Withdraw requires a custom outflow account"
5428
- );
5429
- }
5430
6034
  if (!signerAccount) {
5431
6035
  throw new LiquidiumError(
5432
6036
  LiquidiumErrorCode.VALIDATION_ERROR,
@@ -5439,7 +6043,10 @@ var LendingModule = class {
5439
6043
  "Withdraw amount must be greater than 0"
5440
6044
  );
5441
6045
  }
5442
- const selectedPool = await this.getPoolById(request.poolId);
6046
+ const selectedPool = await getPoolById(
6047
+ this.canisterContext,
6048
+ request.poolId
6049
+ );
5443
6050
  const selectedAsset = selectedPool.asset;
5444
6051
  const minimumWithdrawAmountError = getWithdrawAmountMinimumValidationError({
5445
6052
  amount: request.amount,
@@ -5451,41 +6058,52 @@ var LendingModule = class {
5451
6058
  minimumWithdrawAmountError.message
5452
6059
  );
5453
6060
  }
5454
- const receiverAddress = normalizeExternalAddress({
5455
- address: destinationAccount,
6061
+ const receiver = parseOutflowDestination({
6062
+ destination,
5456
6063
  asset: selectedAsset,
5457
- chain: selectedPool.chain
6064
+ poolChain: selectedPool.chain,
6065
+ destinationChain: request.chain
5458
6066
  });
5459
6067
  const lendingActor = createLendingActor(this.canisterContext);
5460
6068
  try {
5461
6069
  const expiryTimestamp = computeExpiryTimestampFromNow();
5462
6070
  const nonce = await lendingActor.get_nonce(signerAccount);
5463
- const withdrawRequestData = {
6071
+ const withdrawActionData = {
5464
6072
  profileId: request.profileId,
5465
6073
  poolId: request.poolId,
5466
6074
  amount: request.amount,
5467
- receiverAddress,
6075
+ chain: request.chain,
6076
+ receiver: {
6077
+ address: receiver.address,
6078
+ type: receiver.accountType
6079
+ },
5468
6080
  signerWalletAddress: signerAccount,
5469
6081
  expiryTimestamp
5470
6082
  };
6083
+ const withdrawSubmissionData = {
6084
+ ...withdrawActionData,
6085
+ receiverAccount: receiver.canisterAccount
6086
+ };
5471
6087
  return {
5472
6088
  kind: WalletActionKind.createWithdraw,
5473
6089
  executionKind: WalletExecutionKind.signMessage,
5474
6090
  actionType: WalletActionKind.createWithdraw,
5475
- transferMode: TransferMode.native,
5476
6091
  account: signerAccount,
5477
6092
  message: createWithdrawAssetMessage(
5478
6093
  {
5479
6094
  pool_id: request.poolId,
5480
6095
  amount: request.amount.toString(),
5481
- account: { type: "External", data: receiverAddress },
6096
+ account: receiver.messageAccount,
5482
6097
  expiry_timestamp: expiryTimestamp
5483
6098
  },
5484
6099
  nonce
5485
6100
  ),
5486
- data: withdrawRequestData,
6101
+ data: withdrawActionData,
5487
6102
  submit: async (signatureInfo) => {
5488
- return await this.submitWithdraw(withdrawRequestData, signatureInfo);
6103
+ return await this.submitWithdraw(
6104
+ withdrawSubmissionData,
6105
+ signatureInfo
6106
+ );
5489
6107
  }
5490
6108
  };
5491
6109
  } catch (error) {
@@ -5502,7 +6120,7 @@ var LendingModule = class {
5502
6120
  {
5503
6121
  data: {
5504
6122
  expiry_timestamp: request.expiryTimestamp,
5505
- account: { External: request.receiverAddress },
6123
+ account: request.receiverAccount,
5506
6124
  pool_id: Principal.fromText(request.poolId),
5507
6125
  amount: request.amount
5508
6126
  },
@@ -5558,16 +6176,13 @@ var LendingModule = class {
5558
6176
  * @returns A signable {@link BorrowAction} with `submit` wired to the canister.
5559
6177
  */
5560
6178
  async prepareBorrow(request) {
5561
- const destinationAccount = request.receiverAddress.trim();
6179
+ const destination = resolveOutflowDestinationInput({
6180
+ receiver: request.receiver,
6181
+ errorMessage: "Borrow requires a custom outflow account"
6182
+ });
5562
6183
  const signerAccount = normalizeEvmAddress(
5563
6184
  request.signerWalletAddress.trim()
5564
6185
  );
5565
- if (!destinationAccount) {
5566
- throw new LiquidiumError(
5567
- LiquidiumErrorCode.VALIDATION_ERROR,
5568
- "Borrow requires a custom outflow account"
5569
- );
5570
- }
5571
6186
  if (!signerAccount) {
5572
6187
  throw new LiquidiumError(
5573
6188
  LiquidiumErrorCode.VALIDATION_ERROR,
@@ -5580,7 +6195,10 @@ var LendingModule = class {
5580
6195
  "Borrow amount must be greater than 0"
5581
6196
  );
5582
6197
  }
5583
- const selectedPool = await this.getPoolById(request.poolId);
6198
+ const selectedPool = await getPoolById(
6199
+ this.canisterContext,
6200
+ request.poolId
6201
+ );
5584
6202
  const selectedAsset = selectedPool.asset;
5585
6203
  const minimumBorrowAmountError = getBorrowAmountMinimumValidationError({
5586
6204
  amount: request.amount,
@@ -5592,41 +6210,49 @@ var LendingModule = class {
5592
6210
  minimumBorrowAmountError.message
5593
6211
  );
5594
6212
  }
5595
- const receiverAddress = normalizeExternalAddress({
5596
- address: destinationAccount,
6213
+ const receiver = parseOutflowDestination({
6214
+ destination,
5597
6215
  asset: selectedAsset,
5598
- chain: selectedPool.chain
6216
+ poolChain: selectedPool.chain,
6217
+ destinationChain: request.chain
5599
6218
  });
5600
6219
  const lendingActor = createLendingActor(this.canisterContext);
5601
6220
  try {
5602
6221
  const expiryTimestamp = computeExpiryTimestampFromNow();
5603
6222
  const nonce = await lendingActor.get_nonce(signerAccount);
5604
- const borrowRequestData = {
6223
+ const borrowActionData = {
5605
6224
  profileId: request.profileId,
5606
6225
  poolId: request.poolId,
5607
6226
  amount: request.amount,
5608
- receiverAddress,
6227
+ chain: request.chain,
6228
+ receiver: {
6229
+ address: receiver.address,
6230
+ type: receiver.accountType
6231
+ },
5609
6232
  signerWalletAddress: signerAccount,
5610
6233
  expiryTimestamp
5611
6234
  };
6235
+ const borrowSubmissionData = {
6236
+ ...borrowActionData,
6237
+ receiverAccount: receiver.canisterAccount
6238
+ };
5612
6239
  return {
5613
6240
  kind: WalletActionKind.createBorrow,
5614
6241
  executionKind: WalletExecutionKind.signMessage,
5615
6242
  actionType: WalletActionKind.createBorrow,
5616
- transferMode: TransferMode.native,
5617
6243
  account: signerAccount,
5618
6244
  message: createBorrowAssetMessage(
5619
6245
  {
5620
6246
  pool_id: request.poolId,
5621
6247
  amount: request.amount.toString(),
5622
- account: { type: "External", data: receiverAddress },
6248
+ account: receiver.messageAccount,
5623
6249
  expiry_timestamp: expiryTimestamp
5624
6250
  },
5625
6251
  nonce
5626
6252
  ),
5627
- data: borrowRequestData,
6253
+ data: borrowActionData,
5628
6254
  submit: async (signatureInfo) => {
5629
- return await this.submitBorrow(borrowRequestData, signatureInfo);
6255
+ return await this.submitBorrow(borrowSubmissionData, signatureInfo);
5630
6256
  }
5631
6257
  };
5632
6258
  } catch (error) {
@@ -5643,7 +6269,7 @@ var LendingModule = class {
5643
6269
  ).borrow_assets(Principal.fromText(request.profileId), {
5644
6270
  data: {
5645
6271
  expiry_timestamp: request.expiryTimestamp,
5646
- account: { External: request.receiverAddress },
6272
+ account: request.receiverAccount,
5647
6273
  pool_id: Principal.fromText(request.poolId),
5648
6274
  amount: request.amount
5649
6275
  },
@@ -5761,11 +6387,18 @@ var LendingModule = class {
5761
6387
  *
5762
6388
  * ETH stablecoin deposit-address estimates are served by the deposit-address
5763
6389
  * canister. BTC estimates include the ckBTC minter deposit fee and ledger fee.
6390
+ * ICP-chain estimates return the corresponding ICRC ledger fee.
5764
6391
  *
5765
6392
  * @param request - Asset and chain pair to estimate for.
5766
6393
  * @returns Total fee estimate rounded up in the asset's base units.
5767
6394
  */
5768
6395
  async estimateInflowFee(request) {
6396
+ if (request.chain === Chain.ICP) {
6397
+ const ledgerFee = await this.estimateIcrcLedgerFee(request);
6398
+ return {
6399
+ totalFee: roundInflowFeeEstimate(request, ledgerFee)
6400
+ };
6401
+ }
5769
6402
  if (isEthStablecoin(request.asset, request.chain)) {
5770
6403
  const result = await createDepositAccountsActor(
5771
6404
  this.canisterContext
@@ -5795,6 +6428,14 @@ var LendingModule = class {
5795
6428
  ]);
5796
6429
  return { totalFee: minterFee + ledgerFee };
5797
6430
  }
6431
+ async estimateIcrcLedgerFee(request) {
6432
+ const route = getInflowFeeLedgerAssetRoute(request);
6433
+ return await createIcrcLedgerActor({
6434
+ canisterContext: this.canisterContext,
6435
+ canisterId: route.ledgerCanisterId,
6436
+ ledgerName: `${request.asset} ${request.chain}`
6437
+ }).icrc1_fee();
6438
+ }
5798
6439
  /**
5799
6440
  * Submits an inflow transaction id for faster indexing.
5800
6441
  *
@@ -5804,6 +6445,12 @@ var LendingModule = class {
5804
6445
  * @returns Acknowledgement including the submitted `txid`.
5805
6446
  */
5806
6447
  async submitInflow(request) {
6448
+ if (request.chain !== void 0 && request.chain !== Chain.BTC && request.chain !== Chain.ETH) {
6449
+ throw new LiquidiumError(
6450
+ LiquidiumErrorCode.VALIDATION_ERROR,
6451
+ `Inflow submission is not supported for ${String(request.chain)}`
6452
+ );
6453
+ }
5807
6454
  const apiClient = this.requireApi();
5808
6455
  const response = await apiClient.post(SdkApiPath.inflow, request);
5809
6456
  return {
@@ -5843,30 +6490,39 @@ var LendingModule = class {
5843
6490
  return new SupplyFlowExecutor({
5844
6491
  canisterContext: this.canisterContext,
5845
6492
  evmReadClient: this.evmReadClient,
5846
- getPoolById: async (poolId) => await this.getPoolById(poolId),
5847
6493
  requireApi: () => {
5848
6494
  this.requireApi();
5849
6495
  },
5850
6496
  submitInflow: async (request) => await this.submitInflow(request)
5851
6497
  });
5852
6498
  }
5853
- async getPoolById(poolId) {
5854
- const pools = await createFlexibleLendingActor(
5855
- this.canisterContext
5856
- ).list_pools();
5857
- const selectedPool = pools.find(
5858
- (pool) => pool.principal.toText() === poolId
6499
+ };
6500
+ function getInflowFeeLedgerAssetRoute(request) {
6501
+ if (request.chain !== Chain.ICP) {
6502
+ throw new LiquidiumError(
6503
+ LiquidiumErrorCode.VALIDATION_ERROR,
6504
+ `IC ledger inflow fees are not supported for ${request.asset} on ${request.chain}`
5859
6505
  );
5860
- const decodedPool = selectedPool ? decodeFlexiblePool(selectedPool) : null;
5861
- if (!decodedPool) {
5862
- throw new LiquidiumError(
5863
- LiquidiumErrorCode.POOL_NOT_FOUND,
5864
- `Pool not found: ${poolId}`
5865
- );
5866
- }
5867
- return decodedPool;
5868
6506
  }
5869
- };
6507
+ return getPoolLedgerAssetRoute(request);
6508
+ }
6509
+ function resolveOutflowDestinationInput(params) {
6510
+ const receiver = params.receiver;
6511
+ const address = typeof receiver === "string" ? receiver.trim() : receiver.address.trim();
6512
+ if (!address) {
6513
+ throw new LiquidiumError(
6514
+ LiquidiumErrorCode.VALIDATION_ERROR,
6515
+ params.errorMessage
6516
+ );
6517
+ }
6518
+ if (typeof receiver === "string") {
6519
+ return address;
6520
+ }
6521
+ return {
6522
+ address,
6523
+ type: receiver.type
6524
+ };
6525
+ }
5870
6526
  function mapExpectedOutflowDetails(details, expectedOutflowType, operation) {
5871
6527
  if (details.outflowType !== expectedOutflowType) {
5872
6528
  throw new LiquidiumError(
@@ -5960,6 +6616,12 @@ function formatPrice(price, decimals) {
5960
6616
 
5961
6617
  // src/modules/market/market.ts
5962
6618
  var ZERO_POOL_RATE = [0n, 0n, 0n];
6619
+ var BACKING_POOL_CHAIN_BY_ASSET = {
6620
+ [Asset.BTC]: Chain.BTC,
6621
+ [Asset.ICP]: Chain.ICP,
6622
+ [Asset.USDC]: Chain.ETH,
6623
+ [Asset.USDT]: Chain.ETH
6624
+ };
5963
6625
  var MarketModule = class {
5964
6626
  constructor(canisterContext) {
5965
6627
  this.canisterContext = canisterContext;
@@ -6017,15 +6679,26 @@ var MarketModule = class {
6017
6679
  }
6018
6680
  }
6019
6681
  /**
6020
- * Resolves a single pool for the given asset and chain pair.
6682
+ * Resolves a single backing pool for the given Chain + Asset identifier.
6683
+ *
6684
+ * Native and chain-key identifiers share a pool. For example, both
6685
+ * `ETH/USDT` and `ICP/USDT` resolve to the USDT lending pool.
6021
6686
  *
6022
6687
  * @param query - The market asset and chain pair to match.
6023
6688
  * @returns The single pool that matches the requested asset and chain.
6024
6689
  */
6025
6690
  async findPool(query) {
6691
+ const identifier = { chain: query.chain, asset: query.asset };
6692
+ if (!isAssetIdentifier(identifier)) {
6693
+ throw new LiquidiumError(
6694
+ LiquidiumErrorCode.VALIDATION_ERROR,
6695
+ `Unsupported asset identifier: ${identifier.chain}/${identifier.asset}`
6696
+ );
6697
+ }
6026
6698
  const pools = await this.listPools();
6699
+ const backingPoolChain = BACKING_POOL_CHAIN_BY_ASSET[identifier.asset];
6027
6700
  const matchedPools = pools.filter(
6028
- (pool) => pool.asset === query.asset && pool.chain === query.chain
6701
+ (pool) => pool.asset === identifier.asset && pool.chain === backingPoolChain
6029
6702
  );
6030
6703
  if (matchedPools.length === 0) {
6031
6704
  throw new LiquidiumError(
@@ -6446,6 +7119,6 @@ function resolveEvmReadClient(config) {
6446
7119
  });
6447
7120
  }
6448
7121
 
6449
- export { AccountsModule, ActivitiesModule, ActivityFilter, Asset, CK_ETH_DEPOSIT_CONTRACT_ADDRESS, Chain, Environment, EvmSupplyApprovalStrategy, HistoryModule, InstantLoansModule, LendingModule, LiquidiumClient, LiquidiumError, LiquidiumErrorCode, MIN_BORROW_AMOUNTS_BY_ASSET, MIN_WITHDRAW_AMOUNTS_BY_ASSET, MarketModule, OutflowType, PositionsModule, QuoteModule, QuoteValidationErrorCode, QuoteWarningCode, RATE_DECIMALS, RATE_SCALE2 as RATE_SCALE, SupplyAction, SupplyPlanType, TransferMode, USDC_CONTRACT_ADDRESS, USDT_CONTRACT_ADDRESS, WalletActionKind, WalletExecutionKind, createTransferErc20Transaction, executeWith, getMinimumBorrowAmount, getMinimumWithdrawAmount, intFromPublicId, publicIdFromInt };
7122
+ export { AccountsModule, ActivitiesModule, ActivityFilter, Asset, CK_ETH_DEPOSIT_CONTRACT_ADDRESS, Chain, Environment, EvmSupplyApprovalStrategy, HistoryModule, InstantLoanCreatedError, InstantLoansModule, LendingModule, LiquidiumAccountType, LiquidiumClient, LiquidiumError, LiquidiumErrorCode, MIN_BORROW_AMOUNTS_BY_ASSET, MIN_WITHDRAW_AMOUNTS_BY_ASSET, MarketModule, OutflowType, PositionsModule, QuoteModule, QuoteValidationErrorCode, QuoteWarningCode, RATE_DECIMALS, RATE_SCALE2 as RATE_SCALE, SupplyAction, SupplyPlanType, USDC_CONTRACT_ADDRESS, USDT_CONTRACT_ADDRESS, WalletActionKind, WalletExecutionKind, createTransferErc20Transaction, executeWith, getMinimumBorrowAmount, getMinimumWithdrawAmount, intFromPublicId, isAssetIdentifier, publicIdFromInt };
6450
7123
  //# sourceMappingURL=index.js.map
6451
7124
  //# sourceMappingURL=index.js.map